linux内核netfilter模块分析之:HOOKs点的注册及调用
1: 为什么要写这个东西?
最近在找工作,之前netfilter 这一块的代码也认真地研究过,应该每个人都是这样的你懂 不一定你能很准确的表达出来。 故一定要化些时间把这相关的东西总结一下。
0:相关文档
linux 下 nf_conntrack_tuple 跟踪记录 其中可以根据内核提供的数据结构获取连接跟踪记录。
iptables 中的NAT使用总结 iptable的在防火墙上面的应用。
1:iptable中三个tables所挂接的HOOKs
其实这个问题很简单的运行iptables打开看看就知道,此处的hook与内核的hook是对应起来的。
因此在内核中注册的5个HOOK点如下:
enum nf_inet_hooks {
NF_INET_PRE_ROUTING,
NF_INET_LOCAL_IN,
NF_INET_FORWARD,
NF_INET_LOCAL_OUT,
NF_INET_POST_ROUTING,
NF_INET_NUMHOOKS
};
在向下看linux内核中的实现之前在看看一个数据包在进过linux内核中neitfilter的处理过程。
其中这5个HOOK点的执行点说明如下:
数据报从进入系统,进行IP校验以后,首先经过第一个HOOK函数NF_IP_PRE_ROUTING进行处理;
然后就进入路由代码,其决定该数据报是需要转发还是发给本机的;
若该数据报是发被本机的,则该数据经过HOOK函数NF_IP_LOCAL_IN处理以后然后传递给上层协议;
若该数据报应该被转发则它被NF_IP_FORWARD处理;
经过转发的数据报经过最后一个HOOK函数NF_IP_POST_ROUTING处理以后,再传输到网络上。
本地产生的数据经过HOOK函数NF_IP_LOCAL_OUT 处理后,进行路由选择处理,然后经过NF_IP_POST_ROUTING处理后发送出去。
上面的图可以知道,一个数据包在内核中进行的hook的处理点。
2 :proc文件下的跟踪记录
上面的就是连接跟踪记录,其中记录linux系统建立的每一条连接,其中包括源IP,目的IP,源port,目的port,协议ID,其这些可以称为5元组。有关这个在linux内含中的定义是的结构体 struct nf_conn 中的变量
/* Connection tracking(链接跟踪)用来跟踪、记录每个链接的信息(目前仅支持IP协议的连接跟踪)。
每个链接由“tuple”来唯一标识,这里的“tuple”对不同的协议会有不同的含义,例如对tcp,udp
来说就是五元组: (源IP,源端口,目的IP, 目的端口,协议号),对ICMP协议来说是: (源IP, 目
的IP, id, type, code), 其中id,type与code都是icmp协议的信息。链接跟踪是防火墙实现状态检
测的基础,很多功能都需要借助链接跟踪才能实现,例如NAT、快速转发、等等。*/
/* XXX should I move this to the tail ? - Y.K */
/* These are my tuples; original and reply */
struct nf_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX]; 此变量就保存着上面的跟踪记录,
3:hooks点的定义及注册
其中每个不同协议的不同HOOK点最终都会注册到全局的nf_hooks链表变量之中:同时注册到同一个HOOK的处理函数会根据优先级的不同的进行先后处理。
extern struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
其中定义的协议如下:
enum {
NFPROTO_UNSPEC = 0,
NFPROTO_IPV4 = 2, //ipV4
NFPROTO_ARP = 3, //ARP
NFPROTO_BRIDGE = 7, //brigde
NFPROTO_IPV6 = 10,
NFPROTO_DECNET = 12,
NFPROTO_NUMPROTO,
};
NF_MAX_HOOKS 宏的定义已经在前面说明。HOOK点的定义。
与下面的HOOK的struct nf_hook_ops对比一下就可以看到差异:变量pf的值不同,优先级不同,即内核中根据不同的协议类型可以注册不同的挂载点进行不同的优先级数据包的处理。
其注册使用函数为:nf_register_hooks()函数在内核中多个地方出现,因为用户可以根据自己的需要对特定的协议在特定的位置添加HOOK出现函数。
nf_register_hook()函数的实现就是:
- 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;遍历已经注册的的HOOK,OPS,将新加入的根据优先级添加到链表最后
- 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;
- }
上面就是HOOK点的注册函数,即根据协议类型和HOOK点注册到全局数组中nf_hooks[][]中。
4:注册的HOOK点何时被使用?
在linux内核中当需要使用注册的HOOK点时,使用函数:
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \
NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, INT_MIN)
NF_HOOK->NF_HOOK_THRESH->nf_hook_thresh->nf_hook_slow——这个是最终的执行函数。
先看看下面函数都返回值:
/* Responses from hook functions. */
#define NF_DROP 0
#define NF_ACCEPT 1
#define NF_STOLEN 2
#define NF_QUEUE 3
#define NF_REPEAT 4
#define NF_STOP 5
#define NF_MAX_VERDICT NF_STOP
- int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
- struct net_device *indev,
- struct net_device *outdev,
- int (*okfn)(struct sk_buff *),
- int hook_thresh)
- {
- struct list_head *elem;
- unsigned int verdict;
- int ret = 0;
- /* We may already have this, but read-locks nest anyway */
- rcu_read_lock();
- elem = &nf_hooks[pf][hook];//是不是很熟悉就是上面的全局变量专门用来注册全局HOOK点的变量。
- next_hook:/* 开始遍历对应的netfilter的规则,即对应的proto和hook挂载点 */
- verdict = nf_iterate(&nf_hooks[pf][hook], skb, hook, indev,outdev, &elem, okfn, hook_thresh);
- if (verdict == NF_ACCEPT || verdict == NF_STOP) {
- ret = 1;
- } else if (verdict == NF_DROP) {
- kfree_skb(skb);
- ret = -EPERM;
- } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {
- if (!nf_queue(skb, elem, pf, hook, indev, outdev, okfn,
- verdict >> NF_VERDICT_BITS))
- goto next_hook;
- }
- rcu_read_unlock();
- return ret;
- }
现在来看看nf_iterate()函数:
- unsigned int nf_iterate(struct list_head *head,
- struct sk_buff *skb,
- unsigned int hook,
- const struct net_device *indev,
- const struct net_device *outdev,
- struct list_head **i,
- int (*okfn)(struct sk_buff *),
- int hook_thresh)
- {
- unsigned int verdict;
- //其中head就是全局的2维数组nf_hooks,
- /*
- * The caller must not block between calls to this
- * function because of risk of continuing from deleted element.
- */
- list_for_each_continue_rcu(*i, head) {
- struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
- if (hook_thresh > elem->priority)
- continue;
- /* Optimization: we don't need to hold module
- reference here, since function can't sleep. --RR */
- verdict = elem->hook(hook, skb, indev, outdev, okfn);//根据协议和HOOK点执行挂在的处理函数
- if (verdict != NF_ACCEPT) {//返回结果进行判断。
- #ifdef CONFIG_NETFILTER_DEBUG
- if (unlikely((verdict & NF_VERDICT_MASK)
- > NF_MAX_VERDICT)) {
- NFDEBUG("Evil return from %p(%u).\n",
- elem->hook, hook);
- continue;
- }
- #endif
- if (verdict != NF_REPEAT)
- return verdict;
- *i = (*i)->prev;
- }
- }
- return NF_ACCEPT;
- }
对各个返回值的解释如下:
在数据包流经内核协议栈的整个过程中,在内中定义的HOOK中的如:PRE_ROUTING、LOCAL_IN、FORWARD、LOCAL_OUT和POST_ROUTING会根据数据包的协议簇PF_INET到这些关键点去查找是否注册有钩子函数。如果没有,则直接返回okfn函数指针所指向的函数继续走协议栈;如果有,则调用nf_hook_slow函数,从而进入到Netfilter框架中去进一步调用已注册在该过滤点下的钩子函数,再根据其返回值来确定是否继续执行由函数指针okfn所指向的函数
linux内核netfilter模块分析之:HOOKs点的注册及调用相关推荐
- Linux内核LED模块分析(二)
Linux内核LED模块分析(二) 上次分析到那里后,还是有些同志说看不懂,那我就继续分析一把我认为不需要继续分析的东西吧.上回分析了 led_cdev和trigger的关系后就没有继续说了.有同志还 ...
- Linux中netfilter模块编程实践
上篇我们看了netfilter的实现机制,这篇来实现下netfilter模块实操一把. 为了注册一个钩子,需要填充nf_hook_ops结构体,包括优先级,钩子地点和钩子函数.然后调用nf_regis ...
- linux内核netfilter,linux内核netfilter实现url重定向
原标题:linux内核netfilter实现url重定向 一.NetFilter NetFilter在2.4.x内核中引入,成为linux平台下进行网络应用的主要扩展,不仅包括防火墙的实现,还包括报文 ...
- Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 【转】...
原文地址:Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinauni ...
- 2018-2019-1 20189213《Linux内核原理与分析》第四周作业
<Linux内核原理与分析>第四周学习总结: 1.课本知识总结: 本章内容并不多,首先是介绍了一些Linux内核源代码的目录结构,并基于Linux内核源代码构造一个简单的操作系统MenuO ...
- Linux内核源码分析《进程管理》
Linux内核源码分析<进程管理> 前言 1. Linux 内核源码分析架构 2. 进程原理分析 2.1 进程基础知识 2.2 Linux进程四要素 2.3 进程描述符 task_stru ...
- Linux内核汇编代码分析
Linux内核汇编代码分析 1.vmlinux.lds.S文件分析 1.2 vmlinux.lds.S文件总体框架 1.3 代码段 1.4 只读数据段 1.5 init段 1.6 数据段 1.7 未初 ...
- Linux内核源代码情景分析笔记
Linux内核源代码情景分析笔记 好吧,首先我承认我要是读者的话,这篇文章我看着也头疼,因为写的太长太泛(其主要部分集中在内存管理,进程管理,文件系统)!原本是想按自己理解的精简精简的,按照操作系统中 ...
- Linux内核源码分析方法—程序员进阶必备
一.内核源码之我见 Linux内核代码的庞大令不少人"望而生畏",也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是 ...
最新文章
- apollo local 模式_Apollo 源码解析 —— 客户端配置 API(一)之一览
- SSD行业要变天了!因为这种闪存芯片要来
- 【Ubuntu-Opencv】Ubuntu14.04 Opencv3.3.0 完整卸载方案
- 计算机入门与学习回忆(一)
- 微软亚研院华刚对科研的见解
- Span中显示内容过长显示省略号---SpringCloud Alibaba_若依微服务框架改造_前端基于Vue的ElementUI---工作笔记011
- 经典并发问题:生产者-消费者
- Android应用程序层的作用,Android应用程序框架-004.bean层
- 用matlab做数据挖掘,matlab实现数据挖掘
- HTTP报文-请求报文和响应报文
- EMD+EEMD+CEEMD+CEEMDAN分解论文代码复现
- B站粉丝数显示器,播放数、获赞数失效解决。
- msm8916 lcd 相关调试点指导
- 腾讯云主机安装mysql
- 遇到的面试题基础知识
- 绿色节能环保 开启低碳生活
- Linux如何查看当前Ubuntu系统的版本
- android第三方应用商店,Android第三方应用商店成长迅猛
- 前端清除页面缓存的方法
- SpringCloud 从入门到入土
热门文章
- docker集群管理
- mysql 高版本检索外键_第05期:外键到底能不能用?
- linux sudo永久免密码,linux 免密码 使用sudo 直接使用root权限执行命令
- 嵌入式linux系统文件,嵌入式Linux文件系统知多少
- flex 平铺布局_flex布局及各种布局的总结
- python下载显示文件丢失_Microsoft.PythonTools.resources.dll
- debian dhcp服务启动不了_DHCP服务器配置
- xp系统设置锁定计算机,系统锁定时不关机的诀窍 给XP系统关闭计算机再加一把锁...
- python2 print_Python2和Python3中print的不同点
- Win32 路径操作API