赞
踩
Linux内核中网络防火墙是通过NF_HOOK宏调用钩子函数进行报文处理,本文基于内核版本4.4对钩子函数的初始化流程进行描述。
以过滤本地报文的钩子函数为例。本地报文过滤钩子函数调用宏:
NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN,
net, NULL, skb, skb->dev, NULL,
ip_local_deliver_finish);
这个宏在文件/include/linux/netfilter.h中定义:
- static inline int
-
- NF_HOOK(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk, struct sk_buff *skb,
-
- struct net_device *in, struct net_device *out,
-
- int (*okfn)(struct net *, struct sock *, struct sk_buff *))
-
- {
-
- return NF_HOOK_THRESH(pf, hook, net, sk, skb, in, out, okfn, INT_MIN);
-
- }
-
-
-
- static inline int
-
- NF_HOOK_THRESH(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk,
-
- struct sk_buff *skb, struct net_device *in,
-
- struct net_device *out,
-
- int (*okfn)(struct net *, struct sock *, struct sk_buff *),
-
- int thresh)
-
- {
-
- int ret = nf_hook_thresh(pf, hook, net, sk, skb, in, out, okfn, thresh);
-
- if (ret == 1)
-
- ret = okfn(net, sk, skb);
-
- return ret;
-
- }
-
-
-
- static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
-
- struct net *net,
-
- struct sock *sk,
-
- struct sk_buff *skb,
-
- struct net_device *indev,
-
- struct net_device *outdev,
-
- int (*okfn)(struct net *, struct sock *, struct sk_buff *),
-
- int thresh)
-
- {
-
- struct list_head *hook_list = &net->nf.hooks[pf][hook];
-
-
-
- if (nf_hook_list_active(hook_list, pf, hook)) {
-
- struct nf_hook_state state;
-
-
-
- nf_hook_state_init(&state, hook_list, hook, thresh,
-
- pf, indev, outdev, sk, net, okfn);
-
- return nf_hook_slow(skb, &state);
-
- }
-
- return 1;
-
- }
最终通过链表头struct list_head *hook_list = &net->nf.hooks[pf][hook],这个链表头实际就是所谓的挂载点,后面钩子函数是通过链表的形式挂载到这个链表头下面。
例如过滤本地报文通过遍历执行钩子函数挂载点net->nf.hooks [NFPROTO_IPV4][NF_INET_LOCAL_IN]上的所有钩子函数来实现对报文的过滤处理。
net->nf.hooks [NFPROTO_IPV4][NF_INET_LOCAL_IN]挂载点钩子函数是通过定义在:
net/ipv4/netfilter/iptable_netfilter.c文件中定义的iptable_filter_init进行初始化的
- static int __init iptable_filter_init(void)
-
- {
-
- int ret;
-
-
-
- ret = register_pernet_subsys(&iptable_filter_net_ops);
-
- if (ret < 0)
-
- return ret;
-
-
-
- /* Register hooks */
-
- filter_ops = xt_hook_link(&packet_filter, iptable_filter_hook);
-
- if (IS_ERR(filter_ops)) {
-
- ret = PTR_ERR(filter_ops);
-
- unregister_pernet_subsys(&iptable_filter_net_ops);
-
- }
-
-
-
- return ret;
-
- }
该函数中通过xt_hook_link初始化钩子函数,该函数定义在文件:
net/netfilter/x_tables.c
- struct nf_hook_ops *xt_hook_link(const struct xt_table *table, nf_hookfn *fn)
-
- {
-
- unsigned int hook_mask = table->valid_hooks;
-
- uint8_t i, num_hooks = hweight32(hook_mask);
-
- uint8_t hooknum;
-
- struct nf_hook_ops *ops;
-
- int ret;
-
-
-
- ops = kmalloc(sizeof(*ops) * num_hooks, GFP_KERNEL);
-
- if (ops == NULL)
-
- return ERR_PTR(-ENOMEM);
-
-
-
- for (i = 0, hooknum = 0; i < num_hooks && hook_mask != 0;hook_mask >>= 1, ++hooknum)
- {
-
- if (!(hook_mask & 1))
-
- continue;
-
- ops[i].hook = fn;
-
- ops[i].pf = table->af;
-
- ops[i].hooknum = hooknum;
-
- ops[i].priority = table->priority;
-
- ++i;
- }
-
-
-
- ret = nf_register_hooks(ops, num_hooks);
-
- if (ret < 0) {
-
- kfree(ops);
-
- return ERR_PTR(ret);
-
- }
-
-
-
- return ops;
- }
参数table是对应的filter表,为了不偏离主题,本文只对钩子函数初始化用到的部分进行简单说明,不对表内容展开详细描述,filter表初始化申明如下:
- static const struct xt_table packet_filter = {
-
- .name = "filter",
-
- .valid_hooks = FILTER_VALID_HOOKS,
-
- .me = THIS_MODULE,
-
- .af = NFPROTO_IPV4,
-
- .priority = NF_IP_PRI_FILTER,
-
- };
.priority = NF_IP_PRI_FILTER,对钩子函数优先级进行设置。
.valid_hooks = FILTER_VALID_HOOKS,钩子函数会在以下几个点进行挂载:
#define FILTER_VALID_HOOKS ((1 << NF_INET_LOCAL_IN) | \
(1 << NF_INET_FORWARD) | \
(1 << NF_INET_LOCAL_OUT))
参数fn是要注册的钩子函数指针,定义如下:
- static unsigned int
-
- iptable_filter_hook(void *priv, struct sk_buff *skb,
-
- const struct nf_hook_state *state)
-
- {
-
- if (state->hook == NF_INET_LOCAL_OUT &&
-
- (skb->len < sizeof(struct iphdr) ||
-
- ip_hdrlen(skb) < sizeof(struct iphdr)))
-
- /* root is playing with raw sockets. */
-
- return NF_ACCEPT;
-
-
-
- return ipt_do_table(skb, state, state->net->ipv4.iptable_filter);
-
- }
该函数最终通过ipt_do_table用filter表中的规则对报文进行处理。本文不对表处理进行描述。针对filter表,初始化是只注册了一个相同的钩子函数iptable_filter_hook,该钩子函数注册到了NF_INET_LOCAL_IN、NF_INET_FORWARD、NF_INET_LOCAL_OUT三个挂载点。
钩子函数先存储到struct nf_hook_ops *ops结构中的hook成员,该结构定义如下:
- struct nf_hook_ops {
-
- struct list_head list;
-
-
-
- /* User fills in from here down. */
-
- nf_hookfn *hook;
-
- struct net_device *dev;
-
- void *priv;
- u_int8_t pf;
-
- unsigned int hooknum;
-
- /* Hooks are ordered in ascending priority. */
-
- int priority;
-
- };
然后nf_register_hooks函数通过结构中的list将钩子函数分别挂载到上面说的
net->nf.hooks [NFPROTO_IPV4][NF_INET_LOCAL_IN]、
net->nf.hooks [NFPROTO_IPV4][ NF_INET_FORWARD]、
net->nf.hooks [NFPROTO_IPV4][ NF_INET_LOCAL_OUT]、
钩子挂载点。
nf_register_hooks定义在文件/net/netfiler/core.c中
- int nf_register_hooks(struct nf_hook_ops *reg, unsigned int n)
-
- {
-
- unsigned int i;
-
- int err = 0;
-
-
-
- for (i = 0; i < n; i++) {
-
- err = nf_register_hook(®[i]);
-
- if (err)
-
- goto err;
-
- }
-
- return err;
-
-
-
- err:
-
- if (i > 0)
-
- nf_unregister_hooks(reg, i);
-
- return err;
-
- }
进一步通过nf_register_hook对钩子函数进行注册,定义如下:
- int nf_register_hook(struct nf_hook_ops *reg)
-
- {
-
- struct net *net, *last;
-
- int ret;
-
-
-
- rtnl_lock();
-
- for_each_net(net) {
-
- ret = nf_register_net_hook(net, reg);
-
- if (ret && ret != -ENOENT)
-
- goto rollback;
-
- }
-
- list_add_tail(®->list, &nf_hook_list);
-
- rtnl_unlock();
-
-
-
- return 0;
-
- rollback:
-
- last = net;
-
- for_each_net(net) {
-
- if (net == last)
-
- break;
-
- nf_unregister_net_hook(net, reg);
-
- }
-
- rtnl_unlock();
-
- return ret;
-
- }
其中进一步通过nf_register_net_hook注册到net->nf.hooks[reg->pf][reg->hooknum]挂载点
- int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
-
- {
-
- struct list_head *hook_list;
-
- struct nf_hook_entry *entry;
-
- struct nf_hook_ops *elem;
-
-
-
- entry = kmalloc(sizeof(*entry), GFP_KERNEL);
-
- if (!entry)
-
- return -ENOMEM;
-
-
-
- //将nf_hook_ops转存到entty中,这里之所以转存是为了后面方便删除。
-
- entry->orig_ops = reg;
-
- entry->ops = *reg;
-
-
-
- hook_list = nf_find_hook_list(net, reg);
-
- if (!hook_list) {
-
- kfree(entry);
-
- return -ENOENT;
-
- }
-
-
-
- mutex_lock(&nf_hook_mutex);
-
- list_for_each_entry(elem, hook_list, list) {
-
- if (reg->priority < elem->priority)
-
- break;
-
- }
-
- list_add_rcu(&entry->ops.list, elem->list.prev);
-
- mutex_unlock(&nf_hook_mutex);
-
- #ifdef CONFIG_NETFILTER_INGRESS
-
- if (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS)
-
- net_inc_ingress_queue();
-
- #endif
-
- #ifdef HAVE_JUMP_LABEL
-
- static_key_slow_inc(&nf_hooks_needed[reg->pf][reg->hooknum]);
-
- #endif
-
- return 0;
-
- }
其中通过nf_find_hook_list找到挂载点,没错就是函数中的&net->nf.hooks[reg->pf][reg->hooknum],终于又遇见你。
-
- static struct list_head *nf_find_hook_list(struct net *net,
-
- const struct nf_hook_ops *reg)
-
- {
-
- struct list_head *hook_list = NULL;
-
-
-
- if (reg->pf != NFPROTO_NETDEV)
-
- hook_list = &net->nf.hooks[reg->pf][reg->hooknum];
-
- else if (reg->hooknum == NF_NETDEV_INGRESS) {
-
- #ifdef CONFIG_NETFILTER_INGRESS
-
- if (reg->dev && dev_net(reg->dev) == net)
-
- hook_list = ®->dev->nf_hooks_ingress;
-
- #endif
-
- }
-
- return hook_list;
-
- }
找到挂载点后就可以对钩子函数进行挂载了,挂载过程需要枷锁,然后挂载到对应优先级所在的位置,遍历执行钩子函数时是按优先级执行的,钩子函数中priority越小优先级越高,因此钩子函数也是按照优先级从低到高进行挂载。挂载过程很简单,如下所示:
- mutex_lock(&nf_hook_mutex);
-
- list_for_each_entry(elem, hook_list, list) {
-
- if (reg->priority < elem->priority)
-
- break;
-
- }
-
- list_add_rcu(&entry->ops.list, elem->list.prev);
-
- mutex_unlock(&nf_hook_mutex);
到此钩子函数初始化就结束了,通过上面的分析可以得出钩子函数、filter表关系如下图:
该图表示针对NF_INET_LOCAL_IN、NF_INET_FORWARD、NF_INET_LOCAL_OUT挂载点注册了优先级为NF_IP_PRI_FILTER 的钩子函数:iptable_filter_hook,该钩子函数通过对将报文根据filter表中的规则进行处理。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。