本篇主要从三层协议栈调用函数NF_HOOK说起,不断深入,分析某个钩子点中所有钩子函数的调用流程,但是本文不包含规则介绍和核心的规则匹配流程,后续文章将继续分析;

NF_HOOK函数先调用了nf_hook继续执行调用钩子函数处理,处理之后,如果接受,则调用输入的回调函数okfn,继续数据包的下一步处理;

  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. /* 先执行钩子函数 */
  7. int ret = nf_hook(pf, hook, net, sk, skb, in, out, okfn);
  8.  
  9. /* 返回成功,则继续执行成功回调 */
  10. if (ret == )
  11. ret = okfn(net, sk, skb);
  12. return ret;
  13. }

NF_HOOK_COND增加了一个输入掉价cond,当不满足条件的时候,直接调用okfn,满足条件的时候,才会继续调用nf_hook进行后续的钩子函数调用流程,如果nf_hook返回1,则调用okfn;

  1. static inline int
  2. NF_HOOK_COND(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk,
  3. struct sk_buff *skb, struct net_device *in, struct net_device *out,
  4. int (*okfn)(struct net *, struct sock *, struct sk_buff *),
  5. bool cond)
  6. {
  7. int ret;
  8.  
  9. if (!cond ||
  10. ((ret = nf_hook(pf, hook, net, sk, skb, in, out, okfn)) == ))
  11. ret = okfn(net, sk, skb);
  12. return ret;
  13. }

nf_hook函数首先找到钩子点函数入口,如果有钩子函数,则进一步初始化nf_hook_state结构,然后调用nf_hook_slow进入钩子函数调用流程;

  1. static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net,
  2. struct sock *sk, struct sk_buff *skb,
  3. struct net_device *indev, struct net_device *outdev,
  4. int (*okfn)(struct net *, struct sock *, struct sk_buff *))
  5. {
  6. struct nf_hook_entry *hook_head;
  7. int ret = ;
  8.  
  9. #ifdef HAVE_JUMP_LABEL
  10. if (__builtin_constant_p(pf) &&
  11. __builtin_constant_p(hook) &&
  12. !static_key_false(&nf_hooks_needed[pf][hook]))
  13. return ;
  14. #endif
  15.  
  16. rcu_read_lock();
  17. /* 找到钩子点 */
  18. hook_head = rcu_dereference(net->nf.hooks[pf][hook]);
  19. if (hook_head) {
  20. struct nf_hook_state state;
  21.  
  22. /* 初始化nf_hook_state结构 */
  23. nf_hook_state_init(&state, hook, pf, indev, outdev,
  24. sk, net, okfn);
  25.  
  26. /* 执行钩子函数 */
  27. ret = nf_hook_slow(skb, &state, hook_head);
  28. }
  29. rcu_read_unlock();
  30.  
  31. return ret;
  32. }

在分析nf_hook_slow之前,我们先看下该函数中涉及到的钩子函数执行结果的返回值字段的含义;

  1. /* Responses from hook functions. */
  2. #define NF_DROP 0 /* 丢包,不再传输 */
  3. #define NF_ACCEPT 1 /* 接受数据包,继续正常传输 */
  4. #define NF_STOLEN 2 /* 数据包已经被接管,回调函数处理该包,NF不再处理 */
  5. #define NF_QUEUE 3 /* 将数据包交给用户空间的进程处理 */
  6. #define NF_REPEAT 4 /* 再次调用钩子函数 */
  7. #define NF_STOP 5 /* Deprecated, for userspace nf_queue compatibility. */
  8. #define NF_MAX_VERDICT NF_STOP

nf_hook_slow会遍历当前钩子点上的钩子函数,通过函数nf_hook_entry_hookfn调用钩子函数,并根据返回值判断如何进行下一步处理;

  1. int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state,
  2. struct nf_hook_entry *entry)
  3. {
  4. unsigned int verdict;
  5. int ret;
  6.  
  7. do {
  8. /* 调用钩子函数 */
  9. verdict = nf_hook_entry_hookfn(entry, skb, state);
  10. switch (verdict & NF_VERDICT_MASK) {
  11. case NF_ACCEPT:
  12. /* 获取下一个钩子函数 */
  13. entry = rcu_dereference(entry->next);
  14. break;
  15. case NF_DROP:
  16. /* 丢包 */
  17. kfree_skb(skb);
  18. ret = NF_DROP_GETERR(verdict);
  19. if (ret == )
  20. ret = -EPERM;
  21. return ret;
  22. case NF_QUEUE:
  23. /* 通过netfilter_queue交给应用层nf_queue处理 */
  24. ret = nf_queue(skb, state, &entry, verdict);
  25. if (ret == && entry)
  26. continue;
  27. return ret;
  28. default:
  29. /* Implicit handling for NF_STOLEN, as well as any other
  30. * non conventional verdicts.
  31. */
  32. return ;
  33. }
  34.  
  35. /* 继续执行下一个钩子函数 */
  36. } while (entry);
  37.  
  38. return ;
  39. }

nf_hook_entry_hookfn函数调用当前钩子函数结构entry中的钩子函数hook,返回执行结果;

  1. static inline int
  2. nf_hook_entry_hookfn(const struct nf_hook_entry *entry, struct sk_buff *skb,
  3. struct nf_hook_state *state)
  4. {
  5. return entry->hook(entry->priv, skb, state);
  6. }

我们暂且看一下filter表的钩子函数,可见,其核心流程为ipt_do_table,也就是进入filter表的规则匹配流程,ipt_do_table函数后续文章我们单独分析;

  1. static unsigned int
  2. iptable_filter_hook(void *priv, struct sk_buff *skb,
  3. const struct nf_hook_state *state)
  4. {
  5. /* LOCAL_OUT && (数据长度不足ip头 || 实际ip头部长度不足最小ip头),在使用raw socket */
  6. if (state->hook == NF_INET_LOCAL_OUT &&
  7. (skb->len < sizeof(struct iphdr) ||
  8. ip_hdrlen(skb) < sizeof(struct iphdr)))
  9. /* root is playing with raw sockets. */
  10. return NF_ACCEPT;
  11.  
  12. /* 核心规则匹配流程 */
  13. return ipt_do_table(skb, state, state->net->ipv4.iptable_filter);
  14. }

Netfilter 之 钩子函数调用的更多相关文章

  1. netfilter的钩子——数据包在内核态得捕获、修改和转发

    转发:http://blog.csdn.net/stonesharp/article/details/27091391 数据包在内核态得捕获.修改和转发(基于 netfilter)    忙活了好几天 ...

  2. Linux Netfilter注册钩子点

    注册钩子点首先要包含响应的头文件,因为这应该已经属于对kernel的编程了. #include <linux/module.h> #include <linux/kernel.h&g ...

  3. Netfilter 之 钩子函数与钩子点关系图

    概述 通过钩子点和优先级的代码追溯,得到如下对应关系图,图中横坐标为钩子点,纵坐标为优先级,每个钩子点上的钩子函数按照优先级排布: 详细分析 5个钩子点如下所示,在这个五个钩子点上的钩子函数按照上面的 ...

  4. Netfilter 之 钩子函数注册

    通过注册流程代码的分析,能够明确钩子函数的注册流程,理解存储钩子函数的数据结构,如下图(点击图片可查看原图): 废话不多说,开始分析: nf_hook_ops是注册的钩子函数的核心结构,字段含义如下所 ...

  5. Netfilter的使用和实现

    本文主要内容:Netfilter的原理和实现浅析,以及示例模块. 内核版本:2.6.37 Author:zhangskd @ csdn blog 概述 Netfilter为多种网络协议(IPv4.IP ...

  6. Linux内核project导论——网络:Netfilter概览

    简单介绍 最早的内核包过滤机制是ipfwadm.后来是ipchains.再后来就是iptables/netfilter了. 再往后,也就是如今是nftables. 只是nftables与iptable ...

  7. Netfilter/iptables防火墙

    http://os.51cto.com/art/201107/273443.htm [51CTO独家特稿]Linux系统管理员们都接触过Netfilter/iptables,这是Linux系统自带的免 ...

  8. c#调用钩子

    1 概述 在c++中有钩子程序,但是在C#还没有对其进行封装,所以需要自己根据实际情况调用钩子.钩子在我的理解下是,通过初始化钩子与系统中消息映射建立某种关系,当点击鼠标或者键盘,就会通过钩子中的回调 ...

  9. Pytest权威教程19-编写钩子(Hooks)方法函数

    目录 编写钩子(Hooks)函数 钩子函数验证和执行 firstresult: 遇到第一个有效(非None)结果返回 hookwrapper:在其他钩子函数周围执行 钩子(Hooks)函数排序/调用示 ...

随机推荐

  1. VBA学习资料分享-5

    工作中经常要从数据库把数据跑出来放到EXCEL上,才能进行下一步的操作,那么除了ADO,还有什么方法可以导入数据库数据呢? 推荐使用QueryTable对象 Dim qt As querytable ...

  2. spark2.0的10个特性介绍

    1. Spark 2.0 ! 还记得我们的第七篇 Spark 博文里吗?里面我用三点来总结 spark dataframe 的好处: 当时是主要介绍 spark 里的 dataframe,今天是想总结 ...

  3. uni-app的H5版使用注意事项

    使用方式 打开uni-app项目下的vue文件 点击菜单 运行->运行到浏览器->Chrome 在Chrome內打开调试模式(右键->检查)开启设备模拟,模拟移动设备(如果UI变形刷 ...

  4. Python诞生以来意义菜谱

    自Python诞生以来,它被誉为最简单的编程语言.进入人工智能时代后,它逐渐成为编程领域的主导语言. Python是一种快速.强大.高效和灵活的编程语言家常菜做法大全.学习后,无论您是想进入数据分析菜 ...

  5. canvas-绘制矩形-读书笔记

    使用<canvas>元素,必须先设置其width和height属性,指定可以绘图的区域大小. 要在画布上绘图,需要取得绘图上下文,也就是要调用getContext()方法并传入上下文的名字 ...

  6. oracle wm_concat函数用法

    在Oracle中使用wm_concat(column)可以实现字段的分组合并,逗号分隔.

  7. Linux下mysql不区分大小写设置

    Linux环境下的MySQL数据库的表名默认是区分大小写的 Windows环境下的MySQL数据库的表名默认是不区分大小写的 所以Linux下想mysql不区分下大写可以查看/etc/my.cnf文件 ...

  8. 在线预览word、excel文件

    直接使用微软提供的在线预览服务. 免费 文件必须为网可访问地址,因为微软的服务器需要访问该文件

  9. u-boot移植易用性设置

    u-boot移植易用性设置 以下设置使用的u-boot版本为u-boot-2012.04.01 环境参数 在Flash上划分了一块区域用于存储环境变量,所以当u-boot启动时会有如下操作: 读取Fl ...

  10. PAT Basic 1063 计算谱半径 (20 分)

    在数学中,矩阵的“谱半径”是指其特征值的模集合的上确界.换言之,对于给定的 n 个复数空间的特征值 { , },它们的模为实部与虚部的平方和的开方,而“谱半径”就是最大模. 现在给定一些复数空间的特征 ...