转自http://ycnian.org/blog/archives/628
Netfilter是Linux内核网络子系统中的一个模块,这个模块可以拦截系统中的报文进行处理。Linux系统中的防火墙iptables就是构建在netfilter模块之上的。报文在一台Linux主机中的流通过程如下图所示:
假设一台Linux主机的eth0端口接收到了一个报文,Linux主机首先会查找路由表,判断这个报文的目的地址是否是自己。如果是自己,那么就逐层剥掉报文头,然后提交给应用程序处理。如果目的地址不是自己,那么通过查找路由表可以确定将报文从哪个网口转发出去。除了接收报文外,Linux主机还会主动向外发送报文。发送报文前也需要查找路由表,确定发送报文的网口。
Netfilter在报文流通的路径中设置了5个处理点,位置如上图所示。在这5个处理点,netfilter会截获经过的每个报文,调用一系列函数进行处理。我们可以在这些处理点增加自己的处理函数,从而实现特定功能。这5个处理点分别是:
NF_INET_PRE_ROUTING(位置1):可以截获接收的所有报文,包括目的地址是自己的报文和需要转发的报文。
NF_INET_LOCAL_IN(位置2):可以截获目的地址是自己的报文。
NF_INET_FORWARD(位置3):可以截获所有转发的报文。
NF_INET_LOCAL_OUT(位置4):可以截获自身发出的所有问题。
NF_INET_POST_ROUTING(位置5):可以截获发送的所有报文,包括自身发出的报文和转发的报文。
Netfilter模块对报文的处理结果可能如下:
#define NF_DROP 0 // 丢弃该报文,不再继续传输
#define NF_ACCEPT 1 // 继续正常传输报文
#define NF_STOLEN 2 //Netfilter 模块接管该报文,不再继续传输
#define NF_QUEUE 3 // 对该数据报进行排队,通常用于将数据报提交给用户空间进程处理
#define NF_REPEAT 4 // 再次调用该钩子函数
#define NF_STOP 5 // 继续正常传输报文
其中NF_ACCEPT和NF_STOP都表示报文通过了检查,可以正常向下流通,但是NF_STOP效果强于NF_ACCEPT。在每个处理点可以注册多个钩子函数,这些钩子函数按照优先级排列。优先级高的钩子函数先处理报文,优先级低的报文后处理报文。NF_ACCEPT表示报文通过了某个钩子函数的处理,下一个钩子函数可以接着处理了。而NF_STOP表示报文通过了某个钩子函数的处理,后面的钩子函数你们就不要处理了,谁让你的优先级低呢,我就可以替你做主。假设NF_INET_LOCAL_IN中注册了两个钩子函数hook1和hook2,hook1的优先级高于hook2,hook2设定的处理结果是NF_DROP。如果hook1设定的处理结果是NF_ACCEPT,那么报文就不会递交给应用程序,因为hook2会把报文丢弃掉。如果hook1设定的处理结果是NF_STOP,那么报文就会提交给应用程序,因为hook1放行了,根本不会给hook2处理的机会。
Netfilter中,一个钩子函数的数据结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
在nf_hookfn()中,我们不需要关心okfn(),因为一般情况下不会用到这个参数。我们可以编写自己的hook函数,然后调用nf_register_hook()将hook函数注册到netfilter模块,这样我们就可以截获每个报文进行处理了。下面是一个例子,这个例子向NF_INET_LOCAL_IN注册了hook函数,如果接收到192.168.7.121发送的报文就打印一条信息“receive message from 192.168.7.121”,然后放行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
|