linux内核netfilter的实现以及ipset

netfilter的实现机制基于四个层次的匹配,数据包在每个层次都要经过一个过滤链表,第一个层次就是hook,众所周知linux内核中一共拥有5个hooks,当然你也可以自己修改内核在任何地方添加hook;第二个层次就是每个hook下面的tables,每一个hook都过挂载零个或者若干个tables,数据包要一个一个经过这些tables;第三个层次就是rule,每个table下面拥有零个或者若干个rule,数据包要依次经过这些rules,只要有一个rule对数据包进行了裁决,那么将不再经过该hook的该table的对应rule的后面rules;第四个层次是matchs,和rules的遍历正好相反,数据包只有在对应rule下面的所有的matchs都匹配后才算匹配。
在内核中,netfilter是由下面4个核心结构体支撑起来的:
struct xt_table_info
{
unsigned int size;
unsigned int number;
unsigned int initial_entries;
unsigned int hook_entry[NF_IP_NUMHOOKS];
unsigned int underflow[NF_IP_NUMHOOKS];
char *entries[NR_CPUS];
};
entries[cpu]指向了一个ipt_entry数组,该数组的内存组织形式是平坦的,通过ipt_entry的next_offset字段进行遍历:
struct ipt_entry
{
struct ipt_ip ip;
unsigned int nfcache;
u_int16_t target_offset;
u_int16_t next_offset;
unsigned int comefrom;
struct xt_counters counters;
unsigned char elems[0];
};
elems指向了一块平坦内存模式的,包含了若干个matchs和一个target,target通过target_offset来定位,各个matchs正如上面所说,通过next_offset来进行遍历的:
struct xt_entry_match
{
union {
struct {
u_int16_t match_size;
char name[XT_FUNCTION_MAXNAMELEN-1];
u_int8_t revision;
} user;
struct {
u_int16_t match_size;
struct xt_match *match;
} kernel;
u_int16_t match_size;
} u;
unsigned char data[0];
};
联合体u在数据从用户空间进入内核空间的时候职能改变,xt_entry_match完全从用户空间拷贝而入,进入内核后,netfilter会根据u.user.name来查找已经注册的match,然后初始化u.kernel的数据。target也是一样的,和match不同的是,target对于每条rule只有一个:
struct xt_entry_target
{
union {
struct {
u_int16_t target_size;
char name[XT_FUNCTION_MAXNAMELEN-1];
u_int8_t revision;
} user;
struct {
u_int16_t target_size;
struct xt_target *target;
} kernel;
u_int16_t target_size;
} u;
unsigned char data[0];
};
如果抛开任何其它的逻辑不谈,netfilter对数据包的过滤过程可以简化为下面的嵌套循环:
hook{
table tables[num];
}
table{
rule rules[num];
}
rule{
match matchs[num];
}
for-each hook in hooks
for-each table in hooks[i]
for-each rule in tables[j]
for-each match in rules[i]
如果写成c语言的形式的话就要嵌套四层循环,当然hook点是分布的,而不是在一个循环中的,可仅从逻辑上展开内核代码看的话,却是是嵌套了四层循环,可见netfilter是比较耗时的,即使netfilter在实现的过程中使用了一些小技巧来优化性能。
对应于用户空间的iptables命令简述一下,每条命令都会增加很多层的很多次遍历,当然固定的INPUT,OUTPUT,FORWARD等内置的最外层遍历是不会增加的,-A/D/I后面的参数指示对应的hook,设为h,而-t后面的参数指示h下的table,设为t,然后整个命令就是一条rule,接下来-s,-d,-p等等每个-x以及后面的参数都是一个match,一条iptables命令影响一个hook,一个tables,在一个table追加/删除/插入一条rule,并且为该rule设置若干match,最后指出一旦匹配则执行什么动作,也就是target。
正是因为netfilter的遍历很耗时,策略上应该尽可能的减少需要遍历的次数,因此ipset就有了用武之地,ipset用可以将任意的ip地址加入到一个集合,然后将这个集合看作一个整体,这样就省去了很多的rule以及其下match的遍历,ipset用可插拔可替换的多种算法对ip地址进行增删查改等等,这些算法包括哈希,链表,map等等,有了ipset,如果需要匹配多个不连续的ip地址,不再需要书写多个规则了,只需要一条规则就可以了,务必保证有一条match指向ipset注册的match,剩下的工作ipset可以高效的完成,另外ipset可以在用户空间动态配置,这样就可以轻量级高效率的完成动态ip过滤,必要时还可以找数据库来帮忙。

你可能感兴趣的:(filter)