Linux netfilter 学习笔记 之三 ip层netfilter的table、rule、match、target结构分析

来源:互联网 发布:淘宝电商卖家怎么做 编辑:程序博客网 时间:2024/05/21 13:57


分类: linux 网络 3750人阅读 评论(1)收藏 举报    

目录(?)[+]

  1. tablerulematchtarget等相关数据结构分析
    1. 1 xt_table
    2. 2 xt_table_info
    3. 3 ipt_entry
      1. 31 ipt_ip
    4. 4 ipt_entry_match
      1. 41 xt_match
    5. 5 ipt_standard_target
    6. 6 ipt_entry_target
      1. 61 xt_target

基于linux2.6.21


上一节分析了ip层hook回调函数的注册以及调用流程,本节我们就开始分析每一个模块的具体实现。 工欲善其事必先利其器,一个功能模块的代码实现与其数据结构的设计有很大的关系,所以我们本节主要是分析table、rule、match、target相关的数据结构,争取本节能把数据结构的定义以及数据结构之间的关系分析明了。

 

1. table、rule、match、target等相关数据结构分析

在分析table、rule、match、target之前,先把它们之间的联系图贴出来,看了这个图以后,我们基本上就知道xt_table里rule、match、target之间的关联了。

 

 

 总体框架图

对于一个xt_table表,通过其private指针,指向了xt_table_info结构,而xt_table_info中的entries指针数组的每一个指针成员指向xt_table表在相应cpu中的rule的首地址。

一个表中的所有rule在一个连续的内存块中,让所有rule在一个连续的内存块中,使对rule规则的遍历寻址成为了可能,因为一个规则相对于下一个规则并没有使用链表的形式连接在一起。

而一个完整的rule规则,包括了ipt_entry、ipt_entry_match、ipt_standard_target组成(其实ipt_entry就代表了一个规则,通过ipt_entry->elems即可找到该规则对应的match)。

下面我们一一分析上图提到的数据结构。

 

1.1 xt_table

 

该结构体对应于iptables中的表,目前内核注册的table有filter、mangle、nat、raw表,而这些table根据pf值添加到xt_af[pf].tables链表中。而一个xt_table中包含了该表所支持的hook点与该表里已添加的所有rule规则。

[cpp] view plaincopyprint?
  1.    
  2. struct xt_table  
  3. {  
  4. /*用于将xt_table连接在一起的链表成员*/  
  5. struct list_head list;  
  6.    
  7. /*表名*/  
  8. char name[XT_TABLE_MAXNAMELEN];  
  9.    
  10. /*该表所感兴趣的hook点*/  
  11. unsigned int valid_hooks;  
  12.    
  13. /*读写锁*/  
  14. rwlock_t lock;  
  15.    
  16. /*指针,指向xt_table_info,这个指针指向的xt_table_info才是表的重要成员 
  17. 用于存放规则链*/  
  18. void *private;  
  19.    
  20. /* 判断该表是否属于一个模块 */  
  21. struct module *me;  
  22.    
  23. /*协议号*/  
  24. int af;  
  25. };  


 

 

 


1.2 xt_table_info

上面分析xt_table时,我们只xt_table通过private指针指向了xt_table_info,xt_table_info里面包括了表中含有的规则,是表比较重要的结构体。

 


[cpp] view plaincopyprint?
  1. struct xt_table_info  
  2. {  
  3. /*在一个cpu内,该xt_table所包含的所有规则的内存总数(以字节为单位)*/  
  4. unsigned int size;/*规则的个数*/  
  5. unsigned int number;/*初始化时的规则格式*/  
  6. unsigned int initial_entries;  
  7. /*每条规则链相对于第一条规则链的偏移量*/  
  8. unsigned int hook_entry[NF_IP_NUMHOOKS];/*这个具体用在什么地方我还没有搞懂,看别人的解释为每一条规则链范围的最大 
  9. 上限,不过我看初始化时,其与hook_entry在对应规则链上的值是相等的,目前我在代码里发现对underflow的调用,基本上都是将其与hook_entry在对应规则链上的值设置为 
  10. 相同的,还需要深入分析后确认*/  
  11. unsigned int underflow[NF_IP_NUMHOOKS];  
  12.    
  13. /* ipt_entry tables: one per CPU */  
  14. /*每一个cpu里,ipt_entry指针,指向ipt_entry规则的首地址*/  
  15. char *entries[NR_CPUS];  
  16. };  


 

关于ipt_tale_info与ipt_table、ipt_entry的关系见上面的总体架构图。

 

1.3 ipt_entry

下面我们分析下ipt_entry

[cpp] view plaincopyprint?
  1. struct ipt_entry  
  2. {  
  3. struct ipt_ip ip;  
  4.    
  5. /* Mark with fields that we care about. */  
  6. unsigned int nfcache;  
  7.    
  8. /* Size of ipt_entry + matches */  
  9. /*该规则中target结构相对于该ipt_entry首地址的偏移量*/  
  10. u_int16_t target_offset;  
  11. /* Size of ipt_entry + matches + target */  
  12. /* 下一个规则相对于该ipt_entry首地址的偏移量*/  
  13. u_int16_t next_offset;  
  14.    
  15. /* 这个变量的用途有两个: 
  16. 1.判断table表中的规则链是否存在环路 
  17. 2.遍历规则链链时,用于用户自定义链的规则执行完时返回到主链时使用*/  
  18. unsigned int comefrom;  
  19.    
  20. /* Packet and byte counters. */  
  21. struct xt_counters counters;  
  22.    
  23. /*由于在设计时需要match结构与ipt_entry的内存是连续的,但是一个ipt_entry包含的match个数又是可变的,所以定义了一个可变长度数组elems,主要是为了实现动态的申请match内存空间*/  
  24. unsigned char elems[0];  
  25. };  


 

1.3.1 ipt_ip

对应的ipt_ip定义如下,其主要用于标准匹配

/*

标准匹配时的匹配条件

*/

[cpp] view plaincopyprint?
  1. struct ipt_ip {  
  2. /* 源、目的ip地址 */  
  3. struct in_addr src, dst;  
  4. /* 源、目的ip地址的掩码*/  
  5. struct in_addr smsk, dmsk;  
  6. /*数据包入口、出口的网络接口名称*/  
  7. char iniface[IFNAMSIZ], outiface[IFNAMSIZ];  
  8. /*入口、出口的网络接口掩码*/  
  9. unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];  
  10.    
  11. /* Protocol, 0 = ANY */  
  12. /*协议号*/  
  13. u_int16_t proto;  
  14.    
  15. /* Flags*/  
  16. u_int8_t flags;  
  17.    
  18. /*是否是反转匹配*/  
  19. /* Inverse flags */  
  20. u_int8_t invflags;  
  21. };  


 

 

一个ipt_entry的整体结构如上图。

 

1.4 ipt_entry_match

我们使用到ipt_entry_match时,就说明这是一个扩展match,对于一个标准match是通过调用函数ip_packet_match,对ipt_entry->ip进行判断来实现的,只有扩展match才会使用该结构体在ipt_entry中添加一个ipt_entry_match变量。

 

 

 

match结构体,包括用户态与内核态联合体,通过这个联合体我们发现

只有match_size是共用的。当用户态需要添加新的规则时,对于新规则的

match,用户态只在ipt_entry_match.u.user.name中设置match的名称,而内核在添加规则时

就会根据ipt_entry_match.u.user.name在链表xt[af].match中查找符合要求的ipt_entry_match,当查找

到时就会对ipt_entry_match.u.kernel.match 进行赋值

[cpp] view plaincopyprint?
  1. struct ipt_entry_match  
  2. {  
  3. union {  
  4. struct {  
  5. /*该match所占用的内存大小(以字节为单位)*/  
  6. u_int16_t match_size;  
  7.    
  8. /*该match的名称*/  
  9. char name[IPT_FUNCTION_MAXNAMELEN-1];  
  10. /*该match的版本, 
  11. 通过match的名称与版本信息可以唯一确定一个match。 
  12. */  
  13. u_int8_t revision;  
  14. } user;  
  15. struct {  
  16. /*该match所占用的内存大小(以字节为单位)*/  
  17. u_int16_t match_size;  
  18.    
  19. /*指向ipt_match结构,对于*/  
  20. struct ipt_match *match;  
  21. } kernel;  
  22.    
  23. /* Total length */  
  24. u_int16_t match_size;  
  25. } u;  
  26.    
  27. /*可变长度数组,与下一个match或者target关联*/  
  28. unsigned char data[0];  
  29. };  


ipt_entry_match相关的结构体图如下:

 

上图是一个ipt_entry_match与ipt_match以及一个ipt_match与xt_af[2].match之间的关联,当我们定义了一个xt_match(xt_match与ipt_match是同一个结构)时,需要将其插入到相应的xt_af[pf].match链表中。而当我们为一个rule规则添加一个扩展match时,根据ipt_entry_match.u.user.name查找xt_af[pf].match链表,当查找到相应的xt_match时,就将其地址赋值给ipt_entry_match.u.kernel.match(将该图与总体框架图搭配使用,就基本明了ipt_table、ipt_table_info、ipt_entry、ipt_entry_match、xt_match、xt_af[pf].match这几个数据结构之间的关系)。


1.4.1 xt_match

而xt_match的定义如下:

[cpp] view plaincopyprint?
  1. struct xt_match  
  2. {  
  3. struct list_head list; //链表,使该match添加到match链表中  
  4.    
  5. const char name[XT_FUNCTION_MAXNAMELEN-1];//match名称  
  6.    
  7. u_int8_t revision;  
  8.    
  9. /* Return true or false: return FALSE and set *hotdrop = 1 to 
  10.            force immediate packet drop. */  
  11. /* Arguments changed since 2.6.9, as this must now handle 
  12.    non-linear skb, using skb_header_pointer and 
  13.    skb_ip_make_writable. */  
  14.  /* 
  15.  match处理函数 
  16. */  
  17. int (*match)(const struct sk_buff *skb,  
  18.      const struct net_device *in,  
  19.      const struct net_device *out,  
  20.      const void *matchinfo,  
  21.      int offset,  
  22.      unsigned int protoff,  
  23.      int *hotdrop);  
  24.    
  25. /* Called when user tries to insert an entry of this type. */  
  26. /* Should return true or false. */  
  27. /*合法性检查函数,在创建一个ipt_entry时,在为其match指针赋值后,即会调用该函数 
  28. 进行合法性检查,当检查失败后,即会返回失败,且说明ipt_table创建失败*/  
  29. int (*checkentry)(const char *tablename,  
  30.   const void *ip,  
  31.   void *matchinfo,  
  32.   unsigned int matchinfosize,  
  33.   unsigned int hook_mask);  
  34.    
  35. /* Called when entry of this type deleted. */  
  36. /*销毁函数*/  
  37. void (*destroy)(void *matchinfo, unsigned int matchinfosize);  
  38.    
  39. /* Set this to THIS_MODULE if you are a module, otherwise NULL */  
  40. struct module *me;  
  41. };  
  42.    


如果我们想要添加一个扩展match,就要初始化一个xt_match结构,并且将插入到xt_af[pf].match链表

 

1.5 ipt_standard_target

 

该结构体主要是对ipt_entry_target的封装,且增加了变量verdict。

 

这个verdict起到了很大的作用。

对于target,我们需要知道target有两大类,标准target与扩展target

a)对于标准target,其ipt_entry_target.u.kernel.target为NULL,其只需返回NF_ACCEPT/NF_DROP等操作。

b)对于扩展target,需要调用ipt_entry_target.u.kernel.target,执行扩展target操作,并根据返回值决定是否允许数据通行。

 

即扩展匹配是根据ipt_entry_target.u.kernel.target决定数据包下一步的执行操作,那标准匹配是根据什么决定数据包下一步的操作的呢?

 

答案就是verdict,当verdict为小于0时,则-verdict则为数据包下一步的执行操作;当verdict大于0时,则说明该target需要跳转到一个用户自定义链的链首地址,其值为用户自定义链相对于表的第一条链规则的偏移量。

  基于以上,我们知道verdict的存在即指明了标准target的动作,又为用户链的存在并生效提供了可能。

 

[cpp] view plaincopyprint?
  1. struct ipt_standard_target  
  2. {  
  3. struct ipt_entry_target target;  
  4. int verdict;  
  5. };  
  6.    


 

1.6 ipt_entry_target

 

虽然与ipt_entry_match定义相同,但是ipt_entry_target既可以表示一个标准target,也可以表示一个扩展target。当是一个标准的target时,其ipt_entry_target.u.kernel.target为NULL。

 

下面我们分析一下这个结构体:

/*

target结构体,包括用户态与内核态联合体,通过这个联合体我们发现

只有target_size是共用的。当用户态需要添加新的规则时,对于新规则的

target,用户态只在ipt_entry_target.u.user.name中设置target的名称,而内核在添加规则时

就会根据ipt_entry_target.u.user.name在链表xt[af].target中查找符合要求的ipt_standard_target,当查找

到时就会对ipt_entry_target.u.kernel.target 进行赋值

 

*/

[cpp] view plaincopyprint?
  1. struct ipt_entry_target  
  2. {  
  3. union {  
  4. struct {  
  5. u_int16_t target_size; //target 所占用的内存大小  
  6.    
  7. /* Used by userspace */  
  8. char name[IPT_FUNCTION_MAXNAMELEN-1];//target 的名字  
  9. /*target的版本号,这个值也有很大的作用,这个值让target的向 上兼容成为了可能。 
  10. 存在以下情况: 
  11. 对于target名称为"ABC ",revision为0的target,我们想对这个 target的扩展target函数做新的架构修改,但是又不想改target的 名称,也不想直接改原target的扩展target函数,这时我们可以重 新添加一个target名称为"ABC",revision为1,且扩展target函数 
  12. 为我们新编写的target。这样既保证了针对原来target "ABC"的 iptables规则能正确执行,又能满足我们新的需求。 
  13. 通过name与revision可以唯一确定一个target 
  14. */  
  15. u_int8_t revision;  
  16. } user;  
  17. struct {  
  18. /*target 所占用的内存大小*/  
  19. u_int16_t target_size;  
  20. /* 扩展target使用,用于指向xt_target */  
  21. struct ipt_target *target;  
  22. } kernel;  
  23.    
  24. /*target 所占用的内存大小*/  
  25. u_int16_t target_size;  
  26. } u;  
  27. /*可变长数组,与下一个ipt_entry关联*/  
  28. unsigned char data[0];  
  29. };  
  30.    


ipt_entry_target相关的结构体图如下:

 

上图是一个ipt_entry_target与ipt_target以及一个ipt_target与xt_af[2].target之间的关联,当我们定义了一个xt_target(xt_target与ipt_target是同一个结构)时,需要将其插入到相应的xt_af[pf].target链表中。而当我们为一个rule规则添加一个扩展target时,根据ipt_entry_target.u.user.name查找xt_af[pf].target链表,当查找到相应的xt_target时,就将其地址赋值给ipt_entry_target.u.kernel.tagret(将该图与总体框架图搭配使用,就基本明了ipt_table、ipt_table_info、ipt_entry、ipt_entry_target、xt_target、xt_af[pf].target这几个数据结构之间的关系)。


1.6.1 xt_target

xt_target的定义如下:

[cpp] view plaincopyprint?
  1. struct xt_target  
  2. {  
  3. struct list_head list;//链表,使该match添加到target链表中  
  4.    
  5. const char name[XT_FUNCTION_MAXNAMELEN-1];//target 名称  
  6.    
  7. u_int8_t revision;  
  8.    
  9.    
  10. /* 
  11. target处理函数,对于SNAT、DNAT即在其target函数里,更新request或者reply方向 ip_conntrack_tuple值 
  12. */  
  13. unsigned int (*target)(struct sk_buff **pskb,  
  14.        const struct net_device *in,  
  15.        const struct net_device *out,  
  16.        unsigned int hooknum,  
  17.        const void *targinfo,  
  18.        void *userdata);  
  19.    
  20. /*合法性检查函数,在创建一个ipt_entry时,在为其target指针赋值后,即会调用该函数 
  21. 进行合法性检查,当检查失败后,即会返回失败,且说明ipt_table创建失败*/  
  22. int (*checkentry)(const char *tablename,  
  23.   const void *entry,  
  24.   void *targinfo,  
  25.   unsigned int targinfosize,  
  26.   unsigned int hook_mask);  
  27.    
  28. /*销毁函数,当删除一个规则时调用*/  
  29. void (*destroy)(void *targinfo, unsigned int targinfosize);  
  30.    
  31. /* 该target是否属于一个模块 */  
  32. struct module *me;  
  33. };  


当我们想要添加一个扩展target,就要初始化一个xt_target结构,并且将插入到xt_af[pf].target链表

 

 

至此,基本上就把三层netfilter相关的数据结构一一介绍完了,综合以上分析,结合上面的结构关联图,就把结构体ipt_table、ipt_table_info、ipt_entry、ipt_entry_target、xt_target、xt_af[pf].target、xt_af[pf].match、xt_af[pf].tables、xt_match、ipt_ip之间的关联也介绍清楚了。至此,万事俱备,就差分析表的创建、初始化、添加规则、删除规则、遍历表规则、添加match、添加target等具体实现了。只要对数据结构之间的关系清楚了,分析这些功能的实现就顺理成章了。

0 0