当前位置:   article > 正文

netfilter分析1-钩子函数在内核的初始化_nf_hook_state

nf_hook_state

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中定义:

  1. static inline int
  2. NF_HOOK(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk, struct sk_buff *skb,
  3. struct net_device *in, struct net_device *out,
  4. int (*okfn)(struct net *, struct sock *, struct sk_buff *))
  5. {
  6. return NF_HOOK_THRESH(pf, hook, net, sk, skb, in, out, okfn, INT_MIN);
  7. }
  8. static inline int
  9. NF_HOOK_THRESH(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk,
  10.        struct sk_buff *skb, struct net_device *in,
  11.        struct net_device *out,
  12.        int (*okfn)(struct net *, struct sock *, struct sk_buff *),
  13.        int thresh)
  14. {
  15. int ret = nf_hook_thresh(pf, hook, net, sk, skb, in, out, okfn, thresh);
  16. if (ret == 1)
  17. ret = okfn(net, sk, skb);
  18. return ret;
  19. }
  20. static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
  21.  struct net *net,
  22.  struct sock *sk,
  23.  struct sk_buff *skb,
  24.  struct net_device *indev,
  25.  struct net_device *outdev,
  26.  int (*okfn)(struct net *, struct sock *, struct sk_buff *),
  27.  int thresh)
  28. {
  29. struct list_head *hook_list = &net->nf.hooks[pf][hook];
  30. if (nf_hook_list_active(hook_list, pf, hook)) {
  31. struct nf_hook_state state;
  32. nf_hook_state_init(&state, hook_list, hook, thresh,
  33.    pf, indev, outdev, sk, net, okfn);
  34. return nf_hook_slow(skb, &state);
  35. }
  36. return 1;
  37. }

最终通过链表头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进行初始化的

  1. static int __init iptable_filter_init(void)
  2. {
  3. int ret;
  4. ret = register_pernet_subsys(&iptable_filter_net_ops);
  5. if (ret < 0)
  6. return ret;
  7. /* Register hooks */
  8. filter_ops = xt_hook_link(&packet_filter, iptable_filter_hook);
  9. if (IS_ERR(filter_ops)) {
  10. ret = PTR_ERR(filter_ops);
  11. unregister_pernet_subsys(&iptable_filter_net_ops);
  12. }
  13. return ret;
  14. }

该函数中通过xt_hook_link初始化钩子函数,该函数定义在文件:

net/netfilter/x_tables.c

  1. struct nf_hook_ops *xt_hook_link(const struct xt_table *table, nf_hookfn *fn)
  2. {
  3. unsigned int hook_mask = table->valid_hooks;
  4. uint8_t i, num_hooks = hweight32(hook_mask);
  5. uint8_t hooknum;
  6. struct nf_hook_ops *ops;
  7. int ret;
  8. ops = kmalloc(sizeof(*ops) * num_hooks, GFP_KERNEL);
  9. if (ops == NULL)
  10. return ERR_PTR(-ENOMEM);
  11. for (i = 0, hooknum = 0; i < num_hooks && hook_mask != 0;hook_mask >>= 1, ++hooknum)
  12. {
  13. if (!(hook_mask & 1))
  14. continue;
  15. ops[i].hook     = fn;
  16. ops[i].pf       = table->af;
  17. ops[i].hooknum  = hooknum;
  18. ops[i].priority = table->priority;
  19. ++i;
  20. }
  21. ret = nf_register_hooks(ops, num_hooks);
  22. if (ret < 0) {
  23. kfree(ops);
  24. return ERR_PTR(ret);
  25. }
  26. return ops;
  27. }

参数table是对应的filter表,为了不偏离主题,本文只对钩子函数初始化用到的部分进行简单说明,不对表内容展开详细描述,filter表初始化申明如下:

  1. static const struct xt_table packet_filter = {
  2. .name = "filter",
  3. .valid_hooks = FILTER_VALID_HOOKS,
  4. .me = THIS_MODULE,
  5. .af = NFPROTO_IPV4,
  6. .priority = NF_IP_PRI_FILTER,
  7. };

.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是要注册的钩子函数指针,定义如下:

  1. static unsigned int
  2. iptable_filter_hook(void *priv, struct sk_buff *skb,
  3.     const struct nf_hook_state *state)
  4. {
  5. if (state->hook == NF_INET_LOCAL_OUT &&
  6.     (skb->len < sizeof(struct iphdr) ||
  7.      ip_hdrlen(skb) < sizeof(struct iphdr)))
  8. /* root is playing with raw sockets. */
  9. return NF_ACCEPT;
  10. return ipt_do_table(skb, state, state->net->ipv4.iptable_filter);
  11. }

该函数最终通过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成员,该结构定义如下:

  1. struct nf_hook_ops {
  2. struct list_head list;
  3. /* User fills in from here down. */
  4. nf_hookfn *hook;
  5. struct net_device *dev;
  6. void *priv;
  7. u_int8_t pf;
  8. unsigned int hooknum;
  9. /* Hooks are ordered in ascending priority. */
  10. int priority;
  11. };

然后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中

  1. int nf_register_hooks(struct nf_hook_ops *reg, unsigned int n)
  2. {
  3. unsigned int i;
  4. int err = 0;
  5. for (i = 0; i < n; i++) {
  6. err = nf_register_hook(®[i]);
  7. if (err)
  8. goto err;
  9. }
  10. return err;
  11. err:
  12. if (i > 0)
  13. nf_unregister_hooks(reg, i);
  14. return err;
  15. }

进一步通过nf_register_hook对钩子函数进行注册,定义如下:

  1. int nf_register_hook(struct nf_hook_ops *reg)
  2. {
  3. struct net *net, *last;
  4. int ret;
  5. rtnl_lock();
  6. for_each_net(net) {
  7. ret = nf_register_net_hook(net, reg);
  8. if (ret && ret != -ENOENT)
  9. goto rollback;
  10. }
  11. list_add_tail(®->list, &nf_hook_list);
  12. rtnl_unlock();
  13. return 0;
  14. rollback:
  15. last = net;
  16. for_each_net(net) {
  17. if (net == last)
  18. break;
  19. nf_unregister_net_hook(net, reg);
  20. }
  21. rtnl_unlock();
  22. return ret;
  23. }

其中进一步通过nf_register_net_hook注册到net->nf.hooks[reg->pf][reg->hooknum]挂载点

  1. int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
  2. {
  3. struct list_head *hook_list;
  4. struct nf_hook_entry *entry;
  5. struct nf_hook_ops *elem;
  6. entry = kmalloc(sizeof(*entry), GFP_KERNEL);
  7. if (!entry)
  8. return -ENOMEM;
  9. //将nf_hook_ops转存到entty中,这里之所以转存是为了后面方便删除。
  10. entry->orig_ops = reg;
  11. entry->ops = *reg;
  12. hook_list = nf_find_hook_list(net, reg);
  13. if (!hook_list) {
  14. kfree(entry);
  15. return -ENOENT;
  16. }
  17. mutex_lock(&nf_hook_mutex);
  18. list_for_each_entry(elem, hook_list, list) {
  19. if (reg->priority < elem->priority)
  20. break;
  21. }
  22. list_add_rcu(&entry->ops.list, elem->list.prev);
  23. mutex_unlock(&nf_hook_mutex);
  24. #ifdef CONFIG_NETFILTER_INGRESS
  25. if (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS)
  26. net_inc_ingress_queue();
  27. #endif
  28. #ifdef HAVE_JUMP_LABEL
  29. static_key_slow_inc(&nf_hooks_needed[reg->pf][reg->hooknum]);
  30. #endif
  31. return 0;
  32. }

其中通过nf_find_hook_list找到挂载点,没错就是函数中的&net->nf.hooks[reg->pf][reg->hooknum],终于又遇见你。

  1. static struct list_head *nf_find_hook_list(struct net *net,
  2.    const struct nf_hook_ops *reg)
  3. {
  4. struct list_head *hook_list = NULL;
  5. if (reg->pf != NFPROTO_NETDEV)
  6. hook_list = &net->nf.hooks[reg->pf][reg->hooknum];
  7. else if (reg->hooknum == NF_NETDEV_INGRESS) {
  8. #ifdef CONFIG_NETFILTER_INGRESS
  9. if (reg->dev && dev_net(reg->dev) == net)
  10. hook_list = ®->dev->nf_hooks_ingress;
  11. #endif
  12. }
  13. return hook_list;
  14. }

找到挂载点后就可以对钩子函数进行挂载了,挂载过程需要枷锁,然后挂载到对应优先级所在的位置,遍历执行钩子函数时是按优先级执行的,钩子函数中priority越小优先级越高,因此钩子函数也是按照优先级从低到高进行挂载。挂载过程很简单,如下所示:

  1. mutex_lock(&nf_hook_mutex);
  2. list_for_each_entry(elem, hook_list, list) {
  3. if (reg->priority < elem->priority)
  4. break;
  5. }
  6. list_add_rcu(&entry->ops.list, elem->list.prev);
  7. 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表中的规则进行处理。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/160435
推荐阅读
相关标签
  

闽ICP备14008679号