Mod aaa-12 提交内核钩子模块代码

RCA:
SOL:
修改人: 孟庆超
检视人:
This commit is contained in:
mengqingchao 2019-10-25 17:34:31 +08:00
parent ff08ddb462
commit a75a413208
5 changed files with 965 additions and 0 deletions

View File

@ -0,0 +1,12 @@
#CONFIG_TOUCHSCREEN_FOCALTECH=m
obj-m+=eg_hk_hook.o
eg_hk_hook-objs:=lkh_hook.o
eg_hk_hook-objs+=lkh_hook_fwd.o
eg_hk_hook-objs+=lkh_hook_init.o
#mymodule-objs:=module
KDIR:=/lib/modules/4.18.12-1.el7.elrepo.x86_64/build
MAKE:=make
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean

View File

@ -0,0 +1,530 @@
#include <linux/kernel.h>
#include <linux/if.h>
#include <linux/netdevice.h>
#include <linux/netfilter_ipv6.h>
#include <linux/inetdevice.h>
#include <linux/mutex.h>
#include <linux/mm.h>
#include <linux/rcupdate.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#include <linux/list.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include "lkh_hook.h"
struct lkh_hook_handle g_lkh_hook_handle;
static unsigned int lkh_accept_all(void *priv, struct sk_buff *skb, const struct lkh_hook_state *state)
{
return LKH_ACCEPT; /* ACCEPT makes nf_hook_slow call next hook */
}
static const struct lkh_hook_ops dummy_ops = {
.hook = lkh_accept_all,
.priority = INT_MIN,
};
static inline struct lkh_hook_ops **lkh_hook_entries_get_hook_ops(const struct lkh_hook_entries *e)
{
unsigned int n = e->num_hook_entries;
const void *hook_end;
hook_end = &e->hooks[n]; /* this is *past* ->hooks[]! */
return (struct lkh_hook_ops **)hook_end;
}
/* 注册流程 */
static void __lkh_hook_entries_free(struct rcu_head *h)
{
struct lkh_hook_entries_rcu_head *head;
head = container_of(h, struct lkh_hook_entries_rcu_head, head);
kvfree(head->allocation);
return;
}
static void lkh_hook_entries_free(struct lkh_hook_entries *e)
{
struct lkh_hook_entries_rcu_head *head;
struct lkh_hook_ops **ops;
unsigned int num;
if (NULL == e)
{
return;
}
num = e->num_hook_entries;
ops = lkh_hook_entries_get_hook_ops(e);
head = (void *)&ops[num];
head->allocation = e;
call_rcu(&head->head, __lkh_hook_entries_free);
return;
}
static void lkh_hooks_validate(const struct lkh_hook_entries *hooks)
{
#ifdef CONFIG_DEBUG_KERNEL
struct lkh_hook_ops **orig_ops;
int prio = INT_MIN;
size_t i = 0;
orig_ops = lkh_hook_entries_get_hook_ops(hooks);
for (i = 0; i < hooks->num_hook_entries; i++)
{
if (orig_ops[i] == &dummy_ops)
{
continue;
}
if (orig_ops[i]->priority > prio)
{
prio = orig_ops[i]->priority;
}
}
#endif
}
/* 申请新的存储结构 */
static struct lkh_hook_entries * lkh_allocate_hook_entries_size(u16 num)
{
struct lkh_hook_entries *e;
size_t alloc = sizeof(*e) +
sizeof(struct lkh_hook_entry) * num +
sizeof(struct lkh_hook_ops *) * num +
sizeof(struct lkh_hook_entries_rcu_head);
if (num == 0)
{
return NULL;
}
e = kvzalloc(alloc, GFP_KERNEL);
if (e)
{
e->num_hook_entries = num;
}
return e;
}
static struct lkh_hook_entries * lkh_hook_entries_grow(const struct lkh_hook_entries *old, const struct lkh_hook_ops *reg)
{
unsigned int i, alloc_entries, nhooks, old_entries;
struct lkh_hook_ops **orig_ops = NULL;
struct lkh_hook_ops **new_ops;
struct lkh_hook_entries *new;
bool inserted = false;
alloc_entries = 1;
old_entries = old ? old->num_hook_entries : 0;
if (old != NULL)
{
orig_ops = lkh_hook_entries_get_hook_ops(old);
for (i = 0; i < old_entries; i++)
{
if (orig_ops[i] != &dummy_ops)
{
alloc_entries++;
}
}
}
if (alloc_entries > MAX_HOOK_COUNT)
{
return ERR_PTR(-E2BIG);
}
new = lkh_allocate_hook_entries_size(alloc_entries);
if (NULL == new)
{
return ERR_PTR(-ENOMEM);
}
new_ops = lkh_hook_entries_get_hook_ops(new);
i = 0;
nhooks = 0;
while (i < old_entries)
{
if (orig_ops[i] == &dummy_ops)
{
++i;
continue;
}
if (inserted || (reg->priority > orig_ops[i]->priority))
{
new_ops[nhooks] = (void *)orig_ops[i];
new->hooks[nhooks] = old->hooks[i];
i++;
}
else
{
new_ops[nhooks] = (void *)reg;
new->hooks[nhooks].hook = reg->hook;
new->hooks[nhooks].priv = reg->priv;
inserted = true;
}
nhooks++;
}
if (!inserted)
{
new_ops[nhooks] = (void *)reg;
new->hooks[nhooks].hook = reg->hook;
new->hooks[nhooks].priv = reg->priv;
}
return new;
}
static struct lkh_hook_entries __rcu ** lkh_hook_entry_head(struct net *net, int pf, unsigned int hook_stage)
{
struct lkh_hook * hook;
list_for_each_entry(hook, &g_lkh_hook_handle.list, list)
{
if (hook->net_ptr == net)
{
switch (pf)
{
case LKH_PROTO_IPV4:
if ((ARRAY_SIZE(hook->hooks_ipv4) <= hook_stage))
{
return NULL;
}
return hook->hooks_ipv4 + hook_stage;
case LKH_PROTO_IPV6:
if ((ARRAY_SIZE(hook->hooks_ipv6) <= hook_stage))
{
return NULL;
}
return hook->hooks_ipv6 + hook_stage;
default:
return NULL;
}
break;
}
}
return NULL;
}
static int __lkh_register_net_hook(struct net *net, int pf, const struct lkh_hook_ops *reg)
{
struct lkh_hook_entries *p, *new_hooks;
struct lkh_hook_entries __rcu **pp;
pp = lkh_hook_entry_head(net, pf, reg->hook_stage);
if (NULL == pp)
{
return -EINVAL;
}
mutex_lock(&lkh_hook_mutex);
/* 多核信号量保护处理 RCU */
p = lkh_entry_dereference(*pp);
new_hooks = lkh_hook_entries_grow(p, reg);
if (!IS_ERR(new_hooks))
{
/*
* writer用来进行removal的操作witer完成新版本数据分配和更新之后
* RCU protected pointer指向RCU protected data
*/
rcu_assign_pointer(*pp, new_hooks);
}
mutex_unlock(&lkh_hook_mutex);
if (IS_ERR(new_hooks))
{
return PTR_ERR(new_hooks);
}
lkh_hooks_validate(new_hooks);
/* 释放旧的指针内存 */
lkh_hook_entries_free(p);
return 0;
}
int lkh_register_net_hook(struct net *net, const struct lkh_hook_ops *reg)
{
int err;
err = __lkh_register_net_hook(net, reg->pf, reg);
if (err < 0)
{
return err;
}
return 0;
}
static void *__lkh_hook_entries_try_shrink(struct lkh_hook_entries *old, struct lkh_hook_entries __rcu **pp)
{
unsigned int i, j, skip = 0, hook_entries;
struct lkh_hook_entries *new = NULL;
struct lkh_hook_ops **orig_ops;
struct lkh_hook_ops **new_ops;
if (NULL == old)
{
return NULL;
}
orig_ops = lkh_hook_entries_get_hook_ops(old);
for (i = 0; i < old->num_hook_entries; i++)
{
if (orig_ops[i] == &dummy_ops)
{
skip++;
}
}
hook_entries = old->num_hook_entries;
if (skip == hook_entries)
{
goto out_assign;
}
if (skip == 0)
{
return NULL;
}
hook_entries -= skip;
new = lkh_allocate_hook_entries_size(hook_entries);
if (NULL == new)
{
return NULL;
}
new_ops = lkh_hook_entries_get_hook_ops(new);
for (i = 0, j = 0; i < old->num_hook_entries; i++)
{
if (orig_ops[i] == &dummy_ops)
{
continue;
}
new->hooks[j] = old->hooks[i];
new_ops[j] = (void *)orig_ops[i];
j++;
}
lkh_hooks_validate(new);
out_assign:
rcu_assign_pointer(*pp, new);
return old;
}
static bool lkh_remove_net_hook(struct lkh_hook_entries *old, const struct lkh_hook_ops *unreg)
{
struct lkh_hook_ops **orig_ops;
unsigned int i;
orig_ops = lkh_hook_entries_get_hook_ops(old);
for (i = 0; i < old->num_hook_entries; i++)
{
if (orig_ops[i] != unreg)
{
continue;
}
WRITE_ONCE(old->hooks[i].hook, lkh_accept_all);
WRITE_ONCE(orig_ops[i], &dummy_ops);
return true;
}
return false;
}
static void __lkh_unregister_net_hook(struct net *net, int pf, const struct lkh_hook_ops *reg)
{
struct lkh_hook_entries __rcu **pp;
struct lkh_hook_entries *p;
pp = lkh_hook_entry_head(net, pf, reg->hook_stage);
if (NULL == pp)
{
return;
}
mutex_lock(&lkh_hook_mutex);
p = lkh_entry_dereference(*pp);
if (NULL == p)
{
mutex_unlock(&lkh_hook_mutex);
return;
}
lkh_remove_net_hook(p, reg);
p = __lkh_hook_entries_try_shrink(p, pp);
mutex_unlock(&lkh_hook_mutex);
if (NULL == p)
{
return;
}
lkh_hook_entries_free(p);
return;
}
void lkh_unregister_net_hook(struct net *net, const struct lkh_hook_ops *reg)
{
__lkh_unregister_net_hook(net, reg->pf, reg);
return;
}
/*********************************************************************************
* Description  
*
* Input:  
* net -
* reg -
* n -
* Output:
* 
* Return:
*
* Others:
*
**********************************************************************************/
void lkh_unregister_net_hooks(struct net *net, const struct lkh_hook_ops *reg, unsigned int hookcount)
{
unsigned int i;
for (i = 0; i < hookcount; i++)
{
lkh_unregister_net_hook(net, &reg[i]);
}
return;
}
/*********************************************************************************
* Description  
*
* Input:  
* net -
* reg -
* hookcount -
* Output:
* 
* Return:
* 0 -
* 0 -
* Others:
* reg结构需要调用者确认是否需要保存HOOK管理结构不会记录reg的内容reg的指针 netfilter就是这样实现
* netfilter的方式实现
**********************************************************************************/
int lkh_register_net_hooks(struct net *net, const struct lkh_hook_ops *reg, unsigned int hookcount)
{
unsigned int i;
int err = 0;
for (i = 0; i < hookcount; i++)
{
err = lkh_register_net_hook(net, &reg[i]);
if (err != 0)
{
goto err;
}
}
return err;
err:
if (i > 0)
{
lkh_unregister_net_hooks(net, reg, i);
}
return err;
}
void lkh_hash_struct_show(void)
{
struct lkh_hook * hook;
struct lkh_hook_entries * hook_entries= NULL;
struct lkh_hook_ops **new_ops;
struct lkh_hook_ops * ops = NULL;
struct lkh_hook_entry * entry = NULL;
list_for_each_entry(hook, &g_lkh_hook_handle.list, list)
{
if (hook->net_ptr != NULL)
{
printk(KERN_EMERG "------------------------------ net hook begin-----------------------------");
printk(KERN_EMERG "hook->net_ptr %p", hook->net_ptr);
printk(KERN_EMERG "------------------------------ net hook ipv4-----------------------------");
printk(KERN_EMERG "hook->hooks_ipv4 %p", hook->hooks_ipv4);
printk(KERN_EMERG "------------------------- LKH_INET_PRE_FORWARD------------------------");
printk(KERN_EMERG "hook_entries %p", hook->hooks_ipv4[LKH_INET_PRE_FORWARD]);
hook_entries = hook->hooks_ipv4[LKH_INET_PRE_FORWARD];
if (hook_entries != NULL)
{
int i = 0;
new_ops = lkh_hook_entries_get_hook_ops(hook_entries);
printk(KERN_EMERG "hook_entries num_hook_entries %d new_ops: %p", hook_entries->num_hook_entries, new_ops);
for (; i < hook_entries->num_hook_entries; i++)
{
entry = &(hook_entries->hooks[i]);
ops = new_ops[i];
printk(KERN_EMERG "----------- hook [%d]------------", i);
printk(KERN_EMERG "hook entry [%d] entry ptr: %p new_ops: %p", i, entry, ops);
printk(KERN_EMERG "entry hook: %p", entry->hook);
printk(KERN_EMERG "ops pf: %d hook_stage: %d priority: %d hook: %p", ops->pf, ops->hook_stage, ops->priority, ops->hook);
}
}
printk(KERN_EMERG "------------------------------ net hook end-----------------------------");
}
else
{
printk(KERN_EMERG "------------------------------ net hook begin-----------------------------");
printk(KERN_EMERG "hook->net_ptr is NULL");
printk(KERN_EMERG "------------------------------ net hook end-----------------------------");
}
}
return;
}
EXPORT_SYMBOL(lkh_register_net_hooks);
EXPORT_SYMBOL(lkh_unregister_net_hooks);

View File

@ -0,0 +1,107 @@
#ifndef LKH_HOOK_H
#define LKH_HOOK_H
static DEFINE_MUTEX(lkh_hook_mutex);
#define MAX_HOOK_COUNT 1024
/* 多核信息同步处理 */
#define lkh_entry_dereference(e) rcu_dereference_protected(e, lockdep_is_held(&lkh_hook_mutex))
/* 执行HOOK处理后的返回值 */
#define LKH_DROP 0
#define LKH_ACCEPT 1
#define LKH_STOLEN 2
#define LKH_QUEUE 3
#define LKH_REPEAT 4
#define LKH_STOP 5 /* Deprecated, for userspace nf_queue compatibility. */
/* 钩子函数调用位置 */
enum lkh_inet_hooks
{
LKH_INET_PRE_FORWARD,
LKH_INET_NUMHOOKS
};
/* 支持的协议类型 */
enum
{
LKH_PROTO_UNSPEC = 0,
LKH_PROTO_IPV4 = 1,
LKH_PROTO_IPV6 = 2,
LKH_PROTO_NUMPROTO,
};
struct lkh_hook_entries_rcu_head
{
struct rcu_head head;
void *allocation;
};
struct lkh_hook_state
{
unsigned int hook_stage;
u_int8_t pf;
struct net_device *in;
struct net_device *out;
struct sock *sk;
struct net *net;
};
typedef unsigned int lkh_hookfn(void *priv, struct sk_buff *skb, const struct lkh_hook_state *state);
struct lkh_hook_ops
{
/* User fills in from here down. */
lkh_hookfn *hook; /* 钩子函数 */
void *priv;
u_int8_t pf; /* 协议类型 IPv4、IPv6、brigde等 */
unsigned int hook_stage; /* 阶段ID比如PRE_FORWARD等*/
int priority; /* 优先级 */
};
struct lkh_hook_entry
{
lkh_hookfn *hook;
void *priv;
};
/* 一种协议类型的hook集合包括多个处理阶段 */
struct lkh_hook_entries
{
u_int8_t num_hook_entries;
struct lkh_hook_entry hooks[];
};
struct lkh_hook
{
struct list_head list;
void * net_ptr; /* 记录struct net 结构指针用来查找对应的hook */
struct lkh_hook_entries __rcu *hooks_ipv4[LKH_INET_NUMHOOKS];
struct lkh_hook_entries __rcu *hooks_ipv6[LKH_INET_NUMHOOKS];
};
/* 句柄 */
struct lkh_hook_handle
{
struct list_head list;
unsigned int net_num; /* 网络命名空间数量 */
};
extern struct lkh_hook_handle g_lkh_hook_handle;
extern void lkh_hash_struct_show(void);
extern int lkh_register_net_hooks(struct net *net, const struct lkh_hook_ops *reg, unsigned int hookcount);
extern void lkh_unregister_net_hooks(struct net *net, const struct lkh_hook_ops *reg, unsigned int hookcount);
extern int lkh_hook(u_int8_t pf,
unsigned int hook_stage,
struct net *net,
struct sock *sk,
struct sk_buff *skb,
struct net_device *indev,
struct net_device *outdev);
#endif

View File

@ -0,0 +1,148 @@
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/module.h>
#include <linux/if.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/rcupdate.h>
#include <net/net_namespace.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include "lkh_hook.h"
#define LKH_VERDICT_MASK 0x000000ff
#define LKH_VERDICT_QBITS 16
static inline int LKH_DROP_GETERR(int verdict)
{
return -(verdict >> LKH_VERDICT_QBITS);
}
static inline int lkh_hook_entry_hookfn(const struct lkh_hook_entry *entry, struct sk_buff *skb, struct lkh_hook_state *state)
{
return entry->hook(entry->priv, skb, state);
}
static inline void lkh_hook_state_init(struct lkh_hook_state *p,
unsigned int hook,
u_int8_t pf,
struct net_device *indev,
struct net_device *outdev,
struct sock *sk,
struct net *net)
{
p->hook_stage = hook;
p->pf = pf;
p->in = indev;
p->out = outdev;
p->sk = sk;
p->net = net;
}
int lkh_hook_slow(struct sk_buff *skb, struct lkh_hook_state *state,
const struct lkh_hook_entries *e, unsigned int s)
{
unsigned int verdict;
int ret;
for (; s < e->num_hook_entries; s++)
{
verdict = lkh_hook_entry_hookfn(&e->hooks[s], skb, state);
switch (verdict & LKH_VERDICT_MASK)
{
case LKH_ACCEPT:
break;
case LKH_DROP:
kfree_skb(skb);
ret = LKH_DROP_GETERR(verdict);
if (ret == 0)
{
ret = -EPERM;
}
return ret;
default:
return 0;
}
}
return 1;
}
/*********************************************************************************
* Description  
*
* Input:  
* pf -
* hook_stage -
* sk - socket
* skb -
* indev -
* outdev -
* Output:
* 
* Return:
*
* Others:
* 使netfilter一样
**********************************************************************************/
int lkh_hook(u_int8_t pf,
unsigned int hook_stage,
struct net *net,
struct sock *sk,
struct sk_buff *skb,
struct net_device *indev,
struct net_device *outdev)
{
struct lkh_hook_entries * hook_entrise;
int ret = 1;
bool find_flag = false;
struct lkh_hook * hook_head;
/* 查找对应网络空间的hook结构 */
list_for_each_entry(hook_head, &g_lkh_hook_handle.list, list)
{
if (hook_head->net_ptr == net)
{
find_flag = true;
break;
}
}
if (!find_flag)
{
return ret;
}
rcu_read_lock();
switch (pf)
{
case LKH_PROTO_IPV4:
hook_entrise = rcu_dereference(hook_head->hooks_ipv4[hook_stage]);
break;
case LKH_PROTO_IPV6:
hook_entrise = rcu_dereference(hook_head->hooks_ipv6[hook_stage]);
break;
default:
break;
}
if (hook_entrise != NULL)
{
struct lkh_hook_state state;
lkh_hook_state_init(&state, hook_stage, pf, indev, outdev, sk, net);
ret = lkh_hook_slow(skb, &state, hook_entrise, 0);
}
rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL(lkh_hook);

View File

@ -0,0 +1,168 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/module.h>
#include <linux/if.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/rcupdate.h>
#include <net/net_namespace.h>
#include "lkh_hook.h"
static void __net_init __lkh_net_init(struct lkh_hook_entries __rcu **e, int max)
{
int h;
for (h = 0; h < max; h++)
{
RCU_INIT_POINTER(e[h], NULL);
}
return;
}
/*********************************************************************************
* Description  
* HOOK数据结构
* Input:  
* net -
* Output:
* 
* Return:
*
* Others:
*
**********************************************************************************/
static void __net_exit lkh_net_exit(struct net *net)
{
struct list_head * temp_del;
struct list_head * temp_node;
struct lkh_hook * hook;
if (NULL == net)
{
return;
}
list_for_each_safe(temp_del, temp_node, &g_lkh_hook_handle.list)
{
hook = list_entry(temp_del, struct lkh_hook, list);
if (hook != NULL)
{
if (hook->net_ptr == net)
{
g_lkh_hook_handle.net_num--;
list_del(&hook->list);
/* 释放内存还需要释放hook_ptr内申请的内存之后补充 */
kvfree(hook);
}
}
}
return;
}
/*********************************************************************************
* Description  
* HOOK数据结构
* Input:  
* net -
* Output:
*  1 -
* 0 -
* Return:
*
* Others:
*
**********************************************************************************/
static int __net_init lkh_net_init(struct net *net)
{
struct lkh_hook * hook_ptr;
size_t alloc;
if (NULL == net)
{
return 1;
}
hook_ptr = kvzalloc(sizeof(struct lkh_hook), GFP_KERNEL);
if (NULL == hook_ptr)
{
return 1;
}
/* 记录net指针用来作为标识命名空间的关键字 */
hook_ptr->net_ptr = net;
list_add(&hook_ptr->list, &g_lkh_hook_handle.list);
g_lkh_hook_handle.net_num++;
alloc = sizeof(struct lkh_hook_entries __rcu *) * LKH_INET_NUMHOOKS;
hook_ptr->hooks_ipv4[0] = (struct lkh_hook_entries __rcu *)kvzalloc(alloc, GFP_KERNEL);
__lkh_net_init(hook_ptr->hooks_ipv4, ARRAY_SIZE(hook_ptr->hooks_ipv4));
hook_ptr->hooks_ipv6[0] = (struct lkh_hook_entries __rcu *)kvzalloc(alloc, GFP_KERNEL);
__lkh_net_init(hook_ptr->hooks_ipv6, ARRAY_SIZE(hook_ptr->hooks_ipv6));
return 0;
}
static struct pernet_operations lkh_net_ops = {
.init = lkh_net_init,
.exit = lkh_net_exit,
};
/*********************************************************************************
* Description  
* .KO insmod处理函数
* Input:  
*
* Output:
* 
* Return:
*
* Others:
*
**********************************************************************************/
static int lkh_init(void)
{
int ret;
INIT_LIST_HEAD(&g_lkh_hook_handle.list);
/* 网络空间模块注册函数 */
ret = register_pernet_subsys(&lkh_net_ops);
return ret;
}
/*********************************************************************************
* Description  
* .KO rmmod处理函数
* Input:  
*
* Output:
* 
* Return:
*
* Others:
*
**********************************************************************************/
static void lkh_exit(void)
{
/* 解除网络空间模块注册函数 */
unregister_pernet_subsys(&lkh_net_ops);
return;
}
module_init(lkh_init);
module_exit(lkh_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("meng");
MODULE_DESCRIPTION("LINUX HOOK");