Linux netfilter 学习笔记 之三 ip层netfilter的table、rule、ma

基于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.1xt_table

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

 struct xt_table{/*用于将xt_table连接在一起的链表成员*/struct list_head list; /*表名*/char name[XT_TABLE_MAXNAMELEN]; /*该表所感兴趣的hook点*/unsigned int valid_hooks; /*读写锁*/rwlock_t lock; /*指针,指向xt_table_info,这个指针指向的xt_table_info才是表的重要成员用于存放规则链*/void *private; /* 判断该表是否属于一个模块 */struct module *me; /*协议号*/int af;};

1.2xt_table_info

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

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

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

1.3ipt_entry

下面我们分析下ipt_entry

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

1.3.1ipt_ip

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

/*

标准匹配时的匹配条件

*/

struct ipt_ip {/* 源、目的ip地址 */struct in_addr src, dst;/* 源、目的ip地址的掩码*/struct in_addr smsk, dmsk;/*数据包入口、出口的网络接口名称*/char iniface[IFNAMSIZ], outiface[IFNAMSIZ];/*入口、出口的网络接口掩码*/unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ]; /* Protocol, 0 = ANY *//*协议号*/u_int16_t proto; /* Flags*/u_int8_t flags; /*是否是反转匹配*//* Inverse flags */u_int8_t invflags;};

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

1.4ipt_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进行赋值

struct ipt_entry_match{union {struct {/*该match所占用的内存大小(以字节为单位)*/u_int16_t match_size; /*该match的名称*/char name[IPT_FUNCTION_MAXNAMELEN-1];/*该match的版本,通过match的名称与版本信息可以唯一确定一个match。*/u_int8_t revision;} user;struct {/*该match所占用的内存大小(以字节为单位)*/u_int16_t match_size; /*指向ipt_match结构,对于*/struct ipt_match *match;} kernel; /* Total length */u_int16_t match_size;} u; /*可变长度数组,与下一个match或者target关联*/unsigned char data[0];};

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.1xt_match

而xt_match的定义如下:

struct xt_match{struct list_head list; //链表,使该match添加到match链表中 const char name[XT_FUNCTION_MAXNAMELEN-1];//match名称 u_int8_t revision; /* Return true or false: return FALSE and set *hotdrop = 1 to           force immediate packet drop. *//* Arguments changed since 2.6.9, as this must now handle   non-linear skb, using skb_header_pointer and   skb_ip_make_writable. */ /* match处理函数*/int (*match)(const struct sk_buff *skb,     const struct net_device *in,     const struct net_device *out,     const void *matchinfo,     int offset,     unsigned int protoff,     int *hotdrop); /* Called when user tries to insert an entry of this type. *//* Should return true or false. *//*合法性检查函数,在创建一个ipt_entry时,在为其match指针赋值后,即会调用该函数进行合法性检查,当检查失败后,即会返回失败,且说明ipt_table创建失败*/int (*checkentry)(const char *tablename,  const void *ip,  void *matchinfo,  unsigned int matchinfosize,  unsigned int hook_mask); /* Called when entry of this type deleted. *//*销毁函数*/void (*destroy)(void *matchinfo, unsigned int matchinfosize); /* Set this to THIS_MODULE if you are a module, otherwise NULL */struct module *me;}; 

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

1.5ipt_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的动作,又为用户链的存在并生效提供了可能。

struct ipt_standard_target{struct ipt_entry_target target;int verdict;}; 

1.6ipt_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进行赋值

*/

struct ipt_entry_target{union {struct {u_int16_t target_size; //target 所占用的内存大小 /* Used by userspace */char name[IPT_FUNCTION_MAXNAMELEN-1];//target 的名字/*target的版本号,这个值也有很大的作用,这个值让target的向 上兼容成为了可能。存在以下情况:对于target名称为"ABC ",revision为0的target,我们想对这个 target的扩展target函数做新的架构修改,但是又不想改target的 名称,也不想直接改原target的扩展target函数,这时我们可以重 新添加一个target名称为"ABC",revision为1,且扩展target函数为我们新编写的target。这样既保证了针对原来target "ABC"的 iptables规则能正确执行,又能满足我们新的需求。通过name与revision可以唯一确定一个target*/u_int8_t revision;} user;struct {/*target 所占用的内存大小*/u_int16_t target_size;/* 扩展target使用,用于指向xt_target */struct ipt_target *target;} kernel; /*target 所占用的内存大小*/u_int16_t target_size;} u;/*可变长数组,与下一个ipt_entry关联*/unsigned char data[0];}; 

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.1xt_target

xt_target的定义如下:

struct xt_target{struct list_head list;//链表,使该match添加到target链表中 const char name[XT_FUNCTION_MAXNAMELEN-1];//target 名称 u_int8_t revision;  /*target处理函数,对于SNAT、DNAT即在其target函数里,更新request或者reply方向 ip_conntrack_tuple值*/unsigned int (*target)(struct sk_buff **pskb,       const struct net_device *in,       const struct net_device *out,       unsigned int hooknum,       const void *targinfo,       void *userdata); /*合法性检查函数,在创建一个ipt_entry时,在为其target指针赋值后,即会调用该函数进行合法性检查,当检查失败后,即会返回失败,且说明ipt_table创建失败*/int (*checkentry)(const char *tablename,  const void *entry,  void *targinfo,  unsigned int targinfosize,  unsigned int hook_mask); /*销毁函数,当删除一个规则时调用*/void (*destroy)(void *targinfo, unsigned int targinfosize); /* 该target是否属于一个模块 */struct module *me;};

当我们想要添加一个扩展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等具体实现了。只要对数据结构之间的关系清楚了,分析这些功能的实现就顺理成章了。

感受最美的风景。你曾经说,

Linux netfilter 学习笔记 之三 ip层netfilter的table、rule、ma

相关文章:

你感兴趣的文章:

标签云: