每一条rule包含着match和target两部分信息,该章节主要讲解rule其中match部分。报文经过Netfilter框架注册的hook时,会把该hook中table的rule逐条执行,若是命中某条rule的match部分,则会执行该rule的target动作。hook的整个处理周期,就是围绕着rule的这两部分转。
1. 扩展match部分条件信息的表示
match部分 分为 标准 和 扩展,它们代表的方式不一,前者的信息存储在ipt_entry中,包含网口、源地址、目的地址等信息,且只有一份;后者可以有0个或者多个match部分,可拥有多份;它使用ipt_entry_match结构体表示,ipt_entry_match是别名(讲解使用该昵称),真实的名称为xt_entry_match。
iptable_entry_match属性中采用了union方式共用同一块内存,来划分不同角度的配置,使用user和kernel属性表示用户空间和内核空间,在用户空间时需要一个模块的名称和该模块的match数据,它们分别存放在user.name和user.data[0]里。rule规则到达内核空间时,会根据name去netfilter框架中寻找已经注册好的match模块,并记录找到的match模块的位置,存储到kernel.match属性。
struct xt_entry_match {
union {
struct {
__u16 match_size;
/* Used by userspace */
char name[XT_EXTENSION_MAXNAMELEN]; /*match模块的名称, 如tcp、udp等模块名称*/
__u8 revision;
} user;
struct {
__u16 match_size;
/* Used inside the kernel */
struct xt_match *match;
} kernel;
/* Total length */
__u16 match_size;/*XT_ALIGN(sizeof(struct xt_entry_match)) + XT_ALIGN(data);*/
} u;
unsigned char data[0];
};
2. 注册扩展match模块
上面讲到的match模块,它是预先注册到netfilter框架中,在后续的rule的match条件信息中会使用到。若是没有注册的话,会被检查出来的。
iptables命令使用match模块时可以显式声明(-m tcp/ -m udp)或者隐式声明使用-p tcp时,会自动找到对应的match模块。
xt_match结构体代表着一个扩展match模块;
其中:
name是该扩展match模块名称;
match函数会在报文经过hook点时调用,若报文匹配成功返回true, 否则为false;
checkentry函数会在配置rule时对具体数据进行检验(指的是ipt_entry_match.data[0]属性),检验成功返回0, 否则返回非0;
destroy函数在配置rule时,如若有一个ipt_entry_match找不到match模块会被调用,销毁相应的资源;
struct xt_match {
struct list_head list;
const char name[XT_EXTENSION_MAXNAMELEN];
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. */
bool (*match)(const struct sk_buff *skb,
struct xt_action_param *);
/* Called when user tries to insert an entry of this type. */
int (*checkentry)(const struct xt_mtchk_param *);
/* Called when entry of this type deleted. */
void (*destroy)(const struct xt_mtdtor_param *);
...
unsigned short family;
};
常用的match模块有tcp、udp,实现的部分在内核源码的xt_tcpudp.c文件中定义。
所有扩展match模块,需要通过xt_register_matches
接口注册,最终存放在xt[NFPROTO_NUMPROTO]
数组变量中。
static struct xt_af *xt;
struct xt_af {
struct mutex mutex;
struct list_head match;/*存储扩展match模块*/
struct list_head target;
#ifdef CONFIG_COMPAT
struct mutex compat_mutex;
struct compat_delta *compat_tab;
unsigned int number; /* number of slots in compat_tab[] */
unsigned int cur; /* number of used slots in compat_tab[] */
#endif
};
配置rule下发内核空间时,会通过以下流程图,最终通过xt[NFPROTO_IPV4]
数组变量找到match模块。
static int
find_check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
{
struct xt_match *match;
int ret;
/*从xt[NFPROTO_IPV4]中查找match模块*/
match = xt_request_find_match(NFPROTO_IPV4, m->u.user.name,
m->u.user.revision);
if (IS_ERR(match))
return PTR_ERR(match);
m->u.kernel.match = match;/*找到name对应的match模块*/
ret = check_match(m, par);/*把配置参数传给match模块校验*/
if (ret)
goto err;
return 0;
err:
module_put(m->u.kernel.match->me);
return ret;
}
4.使用扩展match模块
报文经过的hook时,会调用各自table安装的钩子函数,最终会调用ipt_do_table函数进行匹配和决定行为。
filter table -> iptable_filter_hook
nat table -> iptable_nat_do_hook
mangle table -> iptable_mangle_hook
raw -> iptable_raw_hook
/* Returns one of the generic firewall policies, like NF_ACCEPT. */
unsigned int
ipt_do_table(struct sk_buff *skb,
const struct nf_hook_state *state,
struct xt_table *table)
{
...
do {
...
const struct xt_entry_match *ematch;
...
WARN_ON(!e);
if (!ip_packet_match(ip, indev, outdev,
&e->ip, acpar.fragoff)) { /*标准match部分: match条件不符合,则检查下一条rule*/
no_match:
e = ipt_next_entry(e);
continue;
}
/*扩展match部分, 每条rule和报文进行匹配*/
xt_ematch_foreach(ematch, e) {
acpar.match = ematch->u.kernel.match;
acpar.matchinfo = ematch->data;
if (!acpar.match->match(skb, &acpar))
goto no_match;/*某条match条件信息不符合,则检查下一条rule*/
}
...
}
以上源代码以及内容基于内核4.19.1产出。
—越简单,易接受。在折腾路上…