内核版本:2.6.34
NetFilter在2.4.x内核中引入,成为linux平台下进行网络应用的主要扩展,不仅包括防火墙的实现,还包括报文的处理(如报文加密、报文分类统计等)等。
NetFilter数据结构 勾子struct nf_hook_ops[net\filter\core.c]
- struct nf_hook_ops {
- struct list_head list;
-
- nf_hookfn *hook;
- struct module *owner;
- u_int8_t pf;
- unsigned int hooknum;
-
- int priority;
- };
成员list用于链入全局勾子数组nf_hooks中,它一定在第一位,保证&nf_hook_ops->list的值与&nf_hook_ops相同,稍后在使用时会用到这一技巧;
成员hook即用户定义的勾子函数;owner表示注册这个勾子函数的模块,因为netfilter是内核空间的,所以一般为模块来完成勾子函数注册;pf与hooknum一起索引到特定协议特定编号的勾子函数队列,用于索引nf_hooks;priority决定在同一队列(pf与hooknum相同)的顺序,priority越小则排列越靠前。
struct nf_hook_ops只是存储勾子的数据结构,而真正存储这些勾子供协议栈调用的是nf_hooks,从定义可以看出,它其实就是二维数组的链表。
struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; [net\filter\core.c]
其中NFPROTO_NUMPROTO表示勾子关联的协议,可取值:
- enum {
- NFPROTO_UNSPEC = 0,
- NFPROTO_IPV4 = 2,
- NFPROTO_ARP = 3,
- NFPROTO_BRIDGE = 7,
- NFPROTO_IPV6 = 10,
- NFPROTO_DECNET = 12,
- NFPROTO_NUMPROTO,
- };
NF_MAX_HOOKS表示勾子应用的位置,可选值在每个协议模块内部定义,这些值代表了勾子函数在协议流程中应用的位置(稍后会以bridge为例详细说明),大致上都有以下值:
- NF_XXX_PRE_ROUTING,
- NF_XXX_LOCAL_IN,
- NF_XXX_FORWARD,
- NF_XXX_LOCAL_OUT,
- NF_XXX_POST_ROUTING,
- NF_XXX_NUMHOOKS
NetFilter注册
在了解了nf_hook_ops和nf_hooks后,来看下如何操作nf_hooks中的元素。
nf_register_hook()将nf_hook_ops注册到nf_hooks中:
- int nf_register_hook(struct nf_hook_ops *reg)
- {
- struct nf_hook_ops *elem;
- int err;
-
-
- err = mutex_lock_interruptible(&nf_hook_mutex);
- if (err < 0)
- return err;
- list_for_each_entry(elem, &nf_hooks[reg->pf][reg->hooknum], list) {
- if (reg->priority < elem->priority)
- break;
- }
- list_add_rcu(?->list, elem->list.prev);
- mutex_unlock(&nf_hook_mutex);
- return 0;
- }
这个函数很简单,从指定pf&hooknum的nf_hooks队列遍历,按priority从小到大顺序,将reg插入相应位置,完成勾子函数的注册。
nf_unregister_hook()将nf_hook_ops从nf_hooks中注销掉:
- void nf_unregister_hook(struct nf_hook_ops *reg)
- {
- mutex_lock(&nf_hook_mutex);
- list_del_rcu(?->list);
- mutex_unlock(&nf_hook_mutex);
- synchronize_net();
- }
这个函数更简单,从nf_hooks中删除reg。
内核同时还提供了nf_register_hooks()和nf_unregister_hooks(),将reg重复注册n次或将reg从nf_hooks中注销n次。当勾子函数注册完成后,nf_hooks的结构如图所示: