Linux netfilter 学习笔记 之三 ip层netfilter的table、rule、match、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规则。

 
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.2 xt_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.3 ipt_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.1 ipt_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.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 进行赋值

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

 

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


 

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

 

*/

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.1 xt_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等具体实现了。只要对数据结构之间的关系清楚了,分析这些功能的实现就顺理成章了。

  • 11
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值