table->chain->rule(match:target)
从上图所示,在内核空间,每个CPU上维护了一份rule的拷贝。这样做是为了减少锁的使用及增加硬件L1 cache的命中次数,以空间换时间
include/linux/netfilter/x_table.h
struct xt_table
{
struct list_head list;
unsigned int valid_hooks; /*该表中有效的hook点位图*/
struct xt_table_info *private; //指向真正存储rule的结构体
struct module *me;
u_int8_t af; /* address/protocol family */
const char name[XT_TABLE_MAXNAMELEN];//表名字
};
struct xt_table_info
{
unsigned int size; //表的大小
unsigned int number; //表中存的rule个数
unsigned int initial_entries;//初始化表时创建的默认rule个数
//各个hook(chain)在表中的偏移量
unsigned int hook_entry[NF_INET_NUMHOOKS];
//各个hook(chain)中默认规则在表中的偏移量
unsigned int underflow[NF_INET_NUMHOOKS];
//数组,存储各个cpu上自己rule拷贝的内存首地址
void *entries[1];
};
表的注册:
每个网络命名空间管理自己Netfilter中创建的table。并把这些表链接到相应的协议族链表里。
这样就可以通过用户空间传下来的表名查找到相应的表了。
struct net
{
struct netns_xt xt;
}
struct netns_xt
{
struct list_head tables[NFPROTO_NUMPROTO];
#if defined(CONFIG_BRIDGE_NF_EBTABLES) || \
defined(CONFIG_BRIDGE_NF_EBTABLES_MODULE)
struct ebt_table *broute_table;
struct ebt_table *frame_filter;
struct ebt_table *frame_nat;
#endif
};
struct xt_table *xt_register_table(struct net *net,
const struct xt_table *input_table,
struct xt_table_info *bootstrap,
struct xt_table_info *newinfo)
{
int ret;
struct xt_table_info *private;
struct xt_table *t, *table;
/*申请x_table内存*/
table = kmemdup(input_table, sizeof(struct xt_table), GFP_KERNEL);
if (!table) {
ret = -ENOMEM;
goto out;
}
ret = mutex_lock_interruptible(&xt[table->af].mutex);
if (ret != 0)
goto out_free;
/* 在命名空间里查找是否有相同名字的表 */
list_for_each_entry(t, &net->xt.tables[table->af], list) {
if (strcmp(t->name, table->name) == 0) {
ret = -EEXIST;
goto unlock;
}
}
/* 初始化table_private*/
table->private = bootstrap;
if (!xt_replace_table(table, 0, newinfo, &ret))
goto unlock;
private = table->private;
/* save number of initial entries */
private->initial_entries = private->number;
//把表挂到对应网络命名空间管理的链表上
list_add(&table->list, &net->xt.tables[table->af]);
mutex_unlock(&xt[table->af].mutex);
return table;
unlock:
mutex_unlock(&xt[table->af].mutex);
out_free:
kfree(table);
out:
return ERR_PTR(ret);
}
不同的协议族定义的rule不同。为了通用,netfilter把各个协议族的一些共性的数据结构提取出来单独定义,各个协议族再自己定义自己独有的数据结构,并包含netfilter提供的通用数据结构。
我们以IPv4为例来看一下,通过用户空间的iptables所配置到内核中的每条rule是怎么存储的。
如图,ip_entry 是标准的匹配规则,ipt_entry_match是扩展的匹配规则,是可选项。每个rule最后带一个target。Traget有系统自带的,也有扩展的。
1、标准的匹配规则
struct ipt_entry
{
struct ipt_ip ip; //五元组,ipv4报文通用的匹配元素
/* Mark with fields that we care about. */
unsigned int nfcache;
/*详见上图,rule首地址到target的偏移量*/
/* Size of ipt_entry + matches */
u_int16_t target_offset;
/* Size of ipt_entry + matches + target */
/*下一条rule的偏移量*/
u_int16_t next_offset;
/* Back pointer */
unsigned int comefrom;
/*命中报文的统计计数*/
/* Packet and byte counters. */
struct xt_counters counters;
/* The matches (if any), then the target. */
unsigned char elems[0];
};
ipv4报文通用的匹配元素,源/目的ip地址/掩码,源/目的接口名/接口名掩码,传输层协议类型
struct ipt_ip
{
/* Source and destination IP addr */
struct in_addr src, dst;
/* Mask for src and dest IP addr */
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 word */
u_int8_t flags;
/* Inverse flags */
//位图,每一位代表以上匹配元素配置时是否是带!符号(取反)
u_int8_t invflags;
};
4. 扩展的 Macth
#define ipt_entry_match xt_entry_match
netfilter定义了一个通用的match数据结构struct xt_match
struct xt_match
{
struct list_head list;
//match名字,和iptables配置的-m参数相同
const char name[XT_FUNCTION_MAXNAMELEN-1];
u_int8_t revision;
//规则匹配函数
bool (*match)(const struct sk_buff *skb,
const struct xt_match_param *);
/* 匹配函数入参检查函数 */
bool (*checkentry)(const struct xt_mtchk_param *);
/* Called when entry of this type deleted. */
void (*destroy)(const struct xt_mtdtor_param *);
。。。。。。
const char *table;
//match的自定义数据长度
unsigned int matchsize;
unsigned int compatsize;
unsigned int hooks;
unsigned short proto;
unsigned short family;
};
每个struct xt_match代表一个扩展match,netfilter中各个扩展match自己定义自己的匹配参数数据结构,自己实现匹配函数。
在netfilter中,每个扩展match 被注册到全局变量xt管理的match链表上,可根据match的名字来找到相应的struct xt_match。
用户使用-m 来配置的扩展match下发给内核,在内核中以struct xt_entry_match数据结构来存储,该结构后紧跟着存储着扩展match自定义的匹配参数数据。
Match的基本处理流程
1、取到rule中存储的xt_entry_match,这里记录着iptables下发给内核的值,同时找到内核中注册的xt_match,从xt_match里找到相应的match函数.
2、根据xt_entry_match中记录的配置值初始化xt_match_param,
3、把报文和xt_match_param传给match函数进行匹配处理。
注册match
Int xt_register_match(struct xt_match *match)
{
u_int8_t af = match->family;
int ret;
ret = mutex_lock_interruptible(&xt[af].mutex);
if (ret != 0)
return ret;
list_add(&match->list, &xt[af].match);//加入Netfilter管理的全局Match链表上
mutex_unlock(&xt[af].mutex);
return ret;
}
Netfilter中对扩展target 和扩展match的管理非常相似
target分扩展target和标准target
#define ipt_entry_target xt_entry_target
标准target
#define ipt_standard_target xt_standard_target
struct xt_standard_target
{
struct xt_entry_target target;
//verdict 可以是指定返回值,也可以是goto到下一个rule的偏移量,
也可以是queue的队列号。
int verdict;
};
如果struct xt_entry->target 值为空,表示是标准target,根据verdict值来处理报文。
如果struct xt_entry->target不为空,表示不是标准target,就使用target的target函数返回值来处理报文。
调用函数xt_register_target()来注册扩展target。