345 lines
7.6 KiB
C
345 lines
7.6 KiB
C
#include <linux/module.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/list.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/hook_frame.h>
|
|
#include <linux/slab.h>
|
|
#include "list.h"
|
|
#include "hook_info.h"
|
|
#include "module.h"
|
|
|
|
#define HOOKINFO_WAKEUP_LENGTH 1
|
|
static struct proc_dir_entry *hook_dir_entry;
|
|
|
|
|
|
unsigned long percpu_total_num(long __percpu *num)
|
|
{
|
|
int cpu;
|
|
unsigned long total = 0;
|
|
|
|
get_online_cpus();
|
|
for_each_cpu((cpu), hook_cpu_mask)
|
|
total += *per_cpu_ptr(num, cpu);
|
|
put_online_cpus();
|
|
|
|
return total;
|
|
}
|
|
|
|
unsigned long percpu_total_num_atomic64(atomic64_t __percpu *num)
|
|
{
|
|
int cpu;
|
|
unsigned long total = 0;
|
|
|
|
get_online_cpus();
|
|
for_each_cpu((cpu), hook_cpu_mask)
|
|
total += atomic64_read(per_cpu_ptr(num, cpu));
|
|
put_online_cpus();
|
|
|
|
return total;
|
|
}
|
|
|
|
int clear_hookinfo_list(struct hook_info *info)
|
|
{
|
|
struct list_head *info_head, *tmp;
|
|
int cpu;
|
|
bool try = true;
|
|
|
|
get_online_cpus();
|
|
for_each_possible_cpu(cpu) {
|
|
struct list_head *list = per_cpu_ptr(info->lists, cpu);
|
|
retry:
|
|
list_for_each_safe(info_head, tmp, list) {
|
|
if (info->extra_free_func)
|
|
info->extra_free_func(info_head);
|
|
list_del(info_head);
|
|
kfree(info_head);
|
|
}
|
|
|
|
if (try) {
|
|
list = &info->list;
|
|
try = false;
|
|
goto retry;
|
|
}
|
|
}
|
|
put_online_cpus();
|
|
return 0;
|
|
}
|
|
|
|
void clear_cpu_list(void)
|
|
{
|
|
int i = 0;
|
|
struct hook_info *info;
|
|
|
|
for (i = 0; hook_info_array[i].dir; i++) {
|
|
info = &hook_info_array[i];
|
|
clear_hookinfo_list(info);
|
|
}
|
|
}
|
|
|
|
void hookinfo_list_in(struct list_head *new, int type)
|
|
{
|
|
struct list_head *list = this_cpu_ptr(hook_info_array[type].lists);
|
|
raw_spinlock_t *plock = this_cpu_ptr(hook_info_array[type].lock);
|
|
|
|
raw_spin_lock_bh(plock);
|
|
list_add_tail(new, list);
|
|
raw_spin_unlock_bh(plock);
|
|
atomic64_inc(this_cpu_ptr(hook_info_array[type].info_num));
|
|
|
|
if (wq_has_sleeper(&hook_info_array[type].wait_queue))
|
|
wake_up_interruptible_poll(&hook_info_array[type].wait_queue, POLLIN);
|
|
}
|
|
|
|
static ssize_t fops_read(struct file *file, char __user *buf,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
ssize_t len = 0;
|
|
struct hook_info *info = pde_data(file_inode(file));
|
|
|
|
if (file->hook_flags != HOOK_INFO_READ_FLAG)
|
|
return 0;
|
|
|
|
mutex_lock(&info->readlock);
|
|
len = hook_info_read(info, file, buf, count, ppos);
|
|
mutex_unlock(&info->readlock);
|
|
return len;
|
|
}
|
|
|
|
static int fops_open(struct inode *inode, struct file *file)
|
|
{
|
|
return try_module_get(THIS_MODULE) ? 0 : -ENOENT;
|
|
}
|
|
|
|
static unsigned int fops_poll(struct file *file, poll_table *wait)
|
|
{
|
|
struct hook_info *info = pde_data(file_inode(file));
|
|
unsigned int mask = 0;
|
|
|
|
if (file->hook_flags != HOOK_INFO_READ_FLAG)
|
|
return POLLERR;
|
|
|
|
poll_wait(file, &info->wait_queue, wait);
|
|
if (percpu_total_num_atomic64(info->info_num) > (sysctl_poll_wakeup_length & SYSCTL_VALID_MASK))
|
|
mask = POLLIN;
|
|
return mask;
|
|
}
|
|
|
|
static int fops_release(struct inode *inode, struct file *file)
|
|
{
|
|
module_put(THIS_MODULE);
|
|
return 0;
|
|
}
|
|
|
|
static const struct proc_ops hook_info_fops = {
|
|
.proc_open = fops_open,
|
|
.proc_release = fops_release,
|
|
.proc_read = fops_read,
|
|
.proc_poll = fops_poll,
|
|
.proc_lseek = noop_llseek,
|
|
};
|
|
|
|
static ssize_t fops_statistics_read(struct file *file, char __user *buf,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
int ret, i;
|
|
struct statistics_info statistics_information = {};
|
|
unsigned int copied = sizeof(struct statistics_info);
|
|
|
|
if (count < copied)
|
|
return 0;
|
|
|
|
for (i = 0; hook_info_array[i].dir; i++) {
|
|
statistics_information.info_entry[i].type = hook_info_array[i].type;
|
|
statistics_information.info_entry[i].discard =
|
|
percpu_total_num(hook_info_array[i].drop_stats);
|
|
statistics_information.info_entry[i].total =
|
|
percpu_total_num(hook_info_array[i].total_numb);
|
|
}
|
|
|
|
statistics_information.version = STATISTIC_VERSION;
|
|
ret = copy_to_user(buf, &statistics_information, copied);
|
|
return copied - ret;
|
|
}
|
|
|
|
static const struct proc_ops stats_info_fops = {
|
|
.proc_open = fops_open,
|
|
.proc_release = fops_release,
|
|
.proc_read = fops_statistics_read,
|
|
.proc_lseek = noop_llseek,
|
|
};
|
|
|
|
int hook_info_proc_create(void)
|
|
{
|
|
int i, ret;
|
|
|
|
hook_dir_entry = proc_mkdir("aegis", NULL);
|
|
if (!hook_dir_entry) {
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; hook_info_array[i].dir; i++) {
|
|
if (!proc_create_data(hook_info_array[i].dir, 0400, hook_dir_entry, &hook_info_fops, (void *)&hook_info_array[i])) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
mutex_init(&hook_info_array[i].readlock);
|
|
}
|
|
|
|
if (!proc_create("statistics_info", 0400, hook_dir_entry, &stats_info_fops)) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
|
|
return 0;
|
|
err:
|
|
proc_remove(hook_dir_entry);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
void hook_info_proc_delete(void)
|
|
{
|
|
proc_remove(hook_dir_entry);
|
|
}
|
|
|
|
int hook_info_percpu_create(void)
|
|
{
|
|
int i, cpu, ret;
|
|
|
|
for (i = 0; hook_info_array[i].dir; i++) {
|
|
hook_info_array[i].lists = alloc_percpu(struct list_head);
|
|
hook_info_array[i].lock = alloc_percpu(raw_spinlock_t);
|
|
hook_info_array[i].info_num = alloc_percpu(atomic64_t);
|
|
hook_info_array[i].drop_stats = alloc_percpu(unsigned long);
|
|
hook_info_array[i].total_numb = alloc_percpu(unsigned long);
|
|
if (!hook_info_array[i].lists || !hook_info_array[i].info_num || !hook_info_array[i].total_numb
|
|
|| !hook_info_array[i].drop_stats || !hook_info_array[i].lock) {
|
|
printk(KERN_ERR "security: failed to allocate percpu data\n");
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
get_online_cpus();
|
|
for_each_possible_cpu(cpu) {
|
|
struct list_head *list = per_cpu_ptr(hook_info_array[i].lists, cpu);
|
|
raw_spinlock_t *plock = per_cpu_ptr(hook_info_array[i].lock, cpu);
|
|
|
|
INIT_LIST_HEAD(list);
|
|
raw_spin_lock_init(plock);
|
|
atomic64_set(per_cpu_ptr(hook_info_array[i].info_num, cpu), 0);
|
|
*per_cpu_ptr(hook_info_array[i].drop_stats, cpu) = 0;
|
|
*per_cpu_ptr(hook_info_array[i].total_numb, cpu) = 0;
|
|
}
|
|
put_online_cpus();
|
|
|
|
INIT_LIST_HEAD(&hook_info_array[i].list);
|
|
}
|
|
return 0;
|
|
err:
|
|
for (; i >= 0; i--) {
|
|
free_percpu(hook_info_array[i].lists);
|
|
free_percpu(hook_info_array[i].lock);
|
|
free_percpu(hook_info_array[i].info_num);
|
|
free_percpu(hook_info_array[i].drop_stats);
|
|
free_percpu(hook_info_array[i].total_numb);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int hook_info_percpu_delete(void)
|
|
{
|
|
int i;
|
|
|
|
clear_cpu_list();
|
|
for (i = 0; hook_info_array[i].dir; i++) {
|
|
free_percpu(hook_info_array[i].lists);
|
|
free_percpu(hook_info_array[i].lock);
|
|
free_percpu(hook_info_array[i].info_num);
|
|
free_percpu(hook_info_array[i].drop_stats);
|
|
free_percpu(hook_info_array[i].total_numb);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int hook_info_func_register(void)
|
|
{
|
|
int i, type, ret;
|
|
|
|
for (i = 0; hook_info_array[i].dir; i++) {
|
|
type = hook_info_array[i].type;
|
|
if (!hook_func_array[type])
|
|
hook_func_array[type] = hook_info_array[i].hook_func_addr;
|
|
else {
|
|
ret = -EBUSY;
|
|
goto err;
|
|
}
|
|
}
|
|
return 0;
|
|
err:
|
|
for (i = i - 1; i >= 0; i++) {
|
|
type = hook_info_array[i].type;
|
|
hook_func_array[type] = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void hook_info_func_unregister(void)
|
|
{
|
|
int i, type;
|
|
|
|
for (i = 0; hook_info_array[i].dir; i++) {
|
|
type = hook_info_array[i].type;
|
|
hook_func_array[type] = 0;
|
|
}
|
|
}
|
|
|
|
void init_wait_queue(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; hook_info_array[i].dir; i++)
|
|
init_waitqueue_head(&hook_info_array[i].wait_queue);
|
|
}
|
|
|
|
int list_module_init(void)
|
|
{
|
|
|
|
int ret;
|
|
|
|
ret = hook_info_percpu_create();
|
|
if (ret)
|
|
goto list_err;
|
|
|
|
ret = hook_info_proc_create();
|
|
if (ret)
|
|
goto proc_err;
|
|
|
|
ret = hook_info_func_register();
|
|
if (ret)
|
|
goto func_err;
|
|
|
|
init_wait_queue();
|
|
|
|
return 0;
|
|
|
|
func_err:
|
|
hook_info_proc_delete();
|
|
proc_err:
|
|
hook_info_percpu_delete();
|
|
list_err:
|
|
return ret;
|
|
|
|
}
|
|
|
|
void list_module_exit(void)
|
|
{
|
|
hook_info_func_unregister();
|
|
hook_info_proc_delete();
|
|
hook_info_percpu_delete();
|
|
}
|