【转】Netfilter机制

本文深入探讨了Linux内核中的Netfilter模块,解释了其在网络数据包处理流程中的作用,包括如何拦截、处理及转发网络数据包,并详细介绍了Netfilter的五个关键处理点及其工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自http://ycnian.org/blog/archives/628

Netfilter是Linux内核网络子系统中的一个模块,这个模块可以拦截系统中的报文进行处理。Linux系统中的防火墙iptables就是构建在netfilter模块之上的。报文在一台Linux主机中的流通过程如下图所示:
netfilter
假设一台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
typedef unsigned  int nf_hookfn( const struct nf_hook_ops *ops,
                 struct sk_buff *skb,  // 正在处理的报文
                 const struct net_device *in,  // 输入设备
                 const struct net_device *out,  // 输出设备
                 int (*okfn)( struct sk_buff *))  // 如果通过了检查,就调用这个函数。
struct nf_hook_ops {
         struct list_head list;   // 同一个处理点的所有钩子函数链接在一条链表中
         nf_hookfn       *hook;   // 这是钩子函数
         struct module   *owner;  // 模块
         void            *priv;   // 私有指针
         u_int8_t        pf;      // 协议族
         unsigned  int    hooknum; // 钩子函数起作用的时间点
         int             priority; // 钩子函数的优先级,数值越小优先级越高.
};

在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
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/netfilter.h>
#include <linux/skbuff.h>
 
MODULE_AUTHOR( "Yanchuan Nian" );
MODULE_LICENSE( "GPL" );
 
static char target[4];
 
static unsigned  int nftest_fn( const struct nf_hook_ops *ops,
         struct sk_buff *skb,
         const struct net_device *in,
         const struct net_device *out,
         int (*okfn)( struct sk_buff *))
{
     int res;
     char    saddr[4];
     int offset;
 
     offset = skb_network_offset(skb);
     offset += 12;
     res = skb_copy_bits(skb, offset, saddr, 4);
     if (res < 0) {
         return NF_ACCEPT;
     }
     res =  memcmp (saddr, target, 4);
     if (!res)
         printk(KERN_INFO "receive message from 192.168.7.121\n" );
 
     return NF_ACCEPT;
}
 
static struct nf_hook_ops nf_test_ops = {
     .hook = nftest_fn,
     .owner = THIS_MODULE,
     .pf = NFPROTO_IPV4,
     .hooknum = NF_INET_LOCAL_IN,
     .priority = 0,
 
};
 
static int __init nftest_init( void )
{
     int res;
 
     target[0] = 192; target[1] = 168;
     target[2] = 7; target[3] = 121;
 
     res = nf_register_hook(&nf_test_ops);
     if (res < 0)
         printk(KERN_INFO "failed to register hook\n" );
     else
         printk(KERN_INFO "hello nftest\n" );
     return res;
}
 
static void __exit nftest_exit( void )
{
     printk(KERN_INFO "bye nftest\n" );
     nf_unregister_hook(&nf_test_ops);
}
 
module_init(nftest_init);
module_exit(nftest_exit);

 

转载于:https://www.cnblogs.com/hadis-yuki/p/5529737.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值