Linux Netfilter实现机制和扩展技术-Netfilter-iptables Extensions
- UID
- 1066743
|
Linux Netfilter实现机制和扩展技术-Netfilter-iptables Extensions
3. Netfilter-iptables ExtensionsNetfilter提供的是一套HOOK框架,其优势是就是易于扩充。可供扩充的Netfilter构件主要包括Table、Match、Target和Connection Track Protocol Helper四类,分别对应四套扩展函数。所有扩展都包括核内、核外两个部分,核内部分置于<kernel-root>/net/ipv4/netfilter/下,模块名为ipt_'name'.o;核外部分置于<iptables-root>/extensions/下,动态链接库名为libipt_'name'.so。
3.1 TableTable在以上章节中已经做过介绍了,它作为规则存储的媒介,决定了该规则何时能起作用。系统提供的filter、nat、mangle涵盖了所有的HOOK点,因此,大部分应用都可以围绕这三个已存在的表进行,但也允许编程者定义自己的拥有特殊目的的表,这时需要参考已有表的struct ipt_table定义创建新的ipt_table数据结构,然后调用ipt_register_table()注册该新表,并调用ipt_register_hook()将新表与Netfilter HOOK相关联。
对表进行扩展的情形并不多见,因此这里也不详述。
3.2 Match & TargetMatch和Target是Netfilter-iptables中最常使用的功能,灵活使用Match和Target,可以完成绝大多数报文处理功能。
3.2.1 Match数据结构
核心用struct ipt_match表征一个Match数据结构:
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
| struct ipt_match
{
struct list_head list;
/* 通常初始化成{NULL,NULL},由核心使用 */
const char name[IPT_FUNCTION_MAXNAMELEN];
/* Match的名字,同时也要求包含该Match的模块文件名为ipt_'name'.o */
int (*match)(const struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
const void *matchinfo,
int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop);
/* 返回非0表示匹配成功,如果返回0且hotdrop设为1,
则表示该报文应当立刻丢弃 */
int (*checkentry)(const char *tablename,
const struct ipt_ip *ip,
void *matchinfo,
unsigned int matchinfosize,
unsigned int hook_mask);
/* 在使用本Match的规则注入表中之前调用,进行有效性检查,
/* 如果返回0,规则就不会加入iptables中 */
void (*destroy)(void *matchinfo, unsigned int matchinfosize);
/* 在包含本Match的规则从表中删除时调用,
与checkentry配合可用于动态内存分配和释放 */
struct module *me;
/* 表示当前Match是否为模块(NULL为否) */
};
|
定义好一个ipt_match结构后,可调用ipt_register_match()将本Match注册到ipt_match链表中备用,在模块方式下,该函数通常在init_module()中执行。
3.2.2 Match的用户级设置
要使用核心定义的Match(包括已有的和自定义的),必须在用户级的iptables程序中有所说明,iptables源代码也提供了已知的核心Match,但未知的Match则需要自行添加说明。
在iptables中,一个Match用struct iptables_match表示:
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
| struct iptables_match
{
struct iptables_match *next;
/* Match链,初始为NULL */
ipt_chainlabel name;
/* Match名,和核心模块加载类似,作为动态链接库存在的
Iptables Extension的命名规则为libipt_'name'.so(对于ipv6为libip6t_'name'.so),
以便于iptables主程序根据Match名加载相应的动态链接库 */
const char *version;
/* 版本信息,一般设为NETFILTER_VERSION */
size_t size;
/* Match数据的大小,必须用IPT_ALIGN()宏指定对界 */
size_t userspacesize;
/*由于内核可能修改某些域,因此size可能与确切的用户数据不同,
这时就应该把不会被改变的数据放在数据区的前面部分,
而这里就应该填写被改变的数据区大小;一般来说,这个值和size相同 */
void (*help)(void);
/* 当iptables要求显示当前match的信息时(比如iptables -m ip_ext -h),
就会调用这个函数,输出在iptables程序的通用信息之后 */
void (*init)(struct ipt_entry_match *m, unsigned int *nfcache);
/* 初始化,在parse之前调用 */
int (*parse)(int c, char **argv, int invert, unsigned int *flags,
const struct ipt_entry *entry,
unsigned int *nfcache,
struct ipt_entry_match **match);
/* 扫描并接收本match的命令行参数,正确接收时返回非0,flags用于保存状态信息 */
void (*final_check)(unsigned int flags);
/* 当命令行参数全部处理完毕以后调用,如果不正确,应该退出(exit_error()) */
void (*print)(const struct ipt_ip *ip,
const struct ipt_entry_match *match, int numeric);
/* 当查询当前表中的规则时,显示使用了当前match的规则的额外的信息 */
void (*save)(const struct ipt_ip *ip,
const struct ipt_entry_match *match);
/* 按照parse允许的格式将本match的命令行参数输出到标准输出,
用于iptables-save命令 */
const struct option *extra_opts;
/* NULL结尾的参数列表,struct option与getopt(3)使用的结构相同 */
/* 以下参数由iptables内部使用,用户不用关心 */
unsigned int option_offset;
struct ipt_entry_match *m;
unsigned int mflags;
unsigned int used;
}
struct option {
const char *name;
/* 参数名称,用于匹配命令行输入 */
int has_arg;
/* 本参数项是否允许带参数,0表示没有,1表示有,2表示可有可无 */
int *flag;
/* 指定返回的参数值内容,如果为NULL,则直接返回下面的val值,
否则返回0,val存于flag所指向的位置 */
int val;
/* 缺省的参数值 */
}
|
如对于--opt <value>参数来讲,在struct option中定义为{"opt",1,0,'1'},表示opt带参数值,如果出现-opt <value>参数,则返回'1'用于parse()中的int c参数。
实际使用时,各个函数都可以为空,只要保证name项与核心的对应Match名字相同就可以了。在定义了iptables_match之后,可以调用register_match()让iptables主体识别这个新Match。当iptables命令中第一次指定使用名为ip_ext的Match时,iptables主程序会自动加载libipt_ip_ext.so,并执行其中的_init()接口,所以register_match()操作应该放在_init()中执行。
3.2.3 Target数据结构
Target数据结构struct ipt_target和struct ipt_match基本相同,不同之处只是用target函数指针代替match函数指针:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| struct ipt_target
{
……
unsigned int (*target)(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
const void *targinfo,
void *userdata);
/* 如果需要继续处理则返回IPT_CONTINUE(-1),
否则返回NF_ACCEPT、NF_DROP等值,
它的调用者根据它的返回值来判断如何处理它处理过的报文*/
……
}
|
与ipt_register_match()对应,Target使用ipt_register_target()来进行注册,但文件命名、使用方法等均与Match相同。
3.2.4 Target的用户级设置
Target的用户级设置使用struct iptables_target结构,与struct iptables_match完全相同。register_target()用于注册新Target,方法也与Match相同。
3.3 Connection Track Protocol Helper前面提到,NAT仅对一个连接(TCP或UDP)的第一个报文进行处理,之后就依靠Connection Track机制来完成对后续报文的处理。Connection Track是一套可以和NAT配合使用的机制,用于在传输层(甚至应用层)处理与更高层协议相关的动作。
关于Connection Track,Netfilter中的实现比较复杂,而且实际应用频率不高,因此这里就不展开了,以后专文介绍。
3.4 iptables patch机制对于Netfilter-iptables扩展工作,用户当然可以直接修改源代码并编译安装,但为了标准化和简便起见,在iptables源码包提供了一套patch机制,希望用户按照其格式要求进行扩展,而不必分别修改内核和iptables代码。
和Netfilter-iptables的结构特点相适应,对iptables进行扩展也需要同时修改内核和iptables程序代码,因此patch也分为两个部分。在iptables-1.2.8中,核内补丁由patch-o-matic包提供,iptables-1.2.8的源码中的extensions目录则为iptables程序本身的补丁。
patch-o-matic提供了一个'runme'脚本来给核心打patch,按照它的规范,核内补丁应该包括五个部分,且命名有一定的规范,例如,如果Target名为ip_ext,那么这五个部分的文件名和功能分别为:
- ip_ext.patch
主文件,内容为diff格式的核心.c、.h源文件补丁,实际使用时类似给内核打patch(patch -p0 <ip_ext.patch); - ip_ext.patch.config.in
对<kernel-root>/net/ipv4/netfilter/Config.in文件的修改,第一行是原Config.in中的一行,以指示补丁添加的位置,后面则是添加在以上匹配行之后的内容。这个补丁的作用是使核心的配置界面中支持新增加的补丁选项; - ip_ext.patch.configure.help
对<kernel-root>/Documentation/Configure.help的修改,第一行为原Configure.help中的一行帮助索引,以下几行的内容添加在这一行相关的帮助之后。这个补丁的作用是补充内核配置时对新增加的选项的说明; - ip_ext.patch.help
用于runme脚本显示本patch的帮助信息; - ip_ext.patch.makefile
- 对<kernel-root>/net/ipv4/netfilter/Makefile的修改,和前两个文件的格式相同,用于在指定的位置上添加用于生成ipt_ip_ext.o的make指令。
示例可以参看patch-o-matic下的源文件。
iptables本身的扩展稍微简单一些,那就是在extensions目录下增加一个libipt_ip_ext.c的文件,然后在本子目录的Makefile的PF_EXT_SLIB宏中附加一个ip_ext字符串。
第一次安装时,可以在iptables的根目录下运行make pending-patches命令,此命令会自动调用runme脚本,将所有patch-o-matic下的patch文件打到内核中,之后需要重新配置和编译内核。
如果只需要安装所要求的patch,可以在patch-o-matic目录下直接运行runme ip_ext,它会完成ip_ext patch的安装。之后,仍然要重编内核以使patch生效。
iptables本身的make/make install过程可以编译并安装好libipt_ip_ext.so,之后,新的iptables命令就可以通过加载libipt_ip_ext.so来识别ip_ext target了。
Extensions还可以定义头文件,一般这个头文件核内核外都要用,因此,通常将其放置在<kernel-root>/include/linux/netfilter_ipv4/目录下,在.c文件里指定头文件目录为linux/netfilter_ipv4/。
灵活性是Netfilter-iptables机制的一大特色,因此,扩展Netfilter-iptables也是它的应用的关键。为了与此目标相适应,Netfilter-iptables在结构上便于扩展,同时也提供了一套扩展的方案,并有大量扩展样例可供参考。 |
|
|
|
|
|