Netfilter 之 钩子函数调用
时间:2019-10-28
本文章向大家介绍Netfilter 之 钩子函数调用,主要包括Netfilter 之 钩子函数调用使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
本篇主要从三层协议栈调用函数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 == 1) 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)) == 1)) 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 = 1; 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 1; 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 == 0) 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 == 1 && 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 0; 33 } 34 35 /* 继续执行下一个钩子函数 */ 36 } while (entry); 37 38 return 1; 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 }
原文地址:https://www.cnblogs.com/wanpengcoder/p/11755587.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 高通量数据中批次效应的鉴定和处理(六)- 直接校正表达矩阵
- Go viper 配置文件读取工具
- spring security默认访问权限判定源码
- Springboot+mybatis最简单的增删改查写法
- Head First设计模式——观察者模式
- spring security oauth2 资源服务/客户端无法正确获取权限
- EXTJS grid.column.renderer绑定失效
- Head First设计模式——装饰者模式
- spring security oauth2 资源服务器WebAsyncTask/DeferredResult接口调用报错InsufficientAuthenticationException
- Head First设计模式——简单工厂、工厂、抽象工厂
- Golang | 简介channel常见用法,完成goroutin通信
- Head First设计模式——单例模式
- 【日拱一卒】链表——两个有序的链表合并
- Head First设计模式——命令模式
- Flask表单之WTForms和flask-wtf