匹配规则
iptables的rule中,匹配规则包含标准匹配和扩展匹配。
- 标准匹配
标准匹配使用如下结构体表示,包含源目的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 */
__u16 proto;
/* Flags word */
__u8 flags;
/* Inverse flags */
__u8 invflags;
};
通过下面几个选项指定标准匹配规则
-p, --protocol [!] protocol
The specified protocol can be one of tcp, udp, icmp, or all, or it can be a
numeric value, representing one of these protocols or a different one. A
protocol name from /etc/protocols is also allowed
-s, --source [!] address[/mask]
-d, --destination [!] address[/mask]
-i, --in-interface [!] name
-o, --out-interface [!] name
- 扩展匹配
有两种方法指定扩展匹配规则:
a. 通过 -p/--protocol 隐式的指定。即-p既指定了标准匹配的协议,也顺便指定了扩展匹配,但不是所有协议都支持扩展匹配,只有注册了扩展匹配的协议才可以,比如tcp,udp在函数tcpudp_mt_init中注册成为了扩展匹配。
b. 通过 -m/--match 显示的指定。
可以使用 -h/--help查看指定扩展匹配的帮助信息。如下查看state扩展匹配的帮助信息:
# iptables -m state -h
...
state match options:
[!] --state [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED][,...]
State(s) to match
下面这条规则中,-i eth0指定了标准匹配的入接口,-p既指定了标准匹配的协议号,又指定了扩展规则为udp,--dport为扩展规则udp的参数。
iptables –A INPUT –i eth0 –p udp –dport 137:138 –j ACCEPT
目标target
报文如果匹配到规则,就要执行此规则指定的target。
target也分为标准target和扩展target。
标准target:用户自定义链和内建target ACCEPT, DROP, QUEUE or RETURN。
扩展target:extention模块。已经支持很多扩展target,包括DNAT,MARK,LOG等。可以使用 -h/--help查看指定扩展target的帮助信息,如下查看SNAT模块的帮助信息:
# iptables -j SNAT -h
...
SNAT target options:
--to-source [<ipaddr>[-<ipaddr>]][:port[-port]]
Address to map source to.
[--random] [--random-fully] [--persistent]
下面这条规则,指定的target为扩展target SNAT。
iptables –t nat –A POSTROUTING –s 192.168.10.10 –o eth1 –j SNAT --to-source 111.196.221.212
可通过 -j 指定所有target,但是 -g 只能指定自定义链。
扩展匹配规则和扩展目标是以kernel module形式存在的,所以需要提前加载到内核中,否则添加规则时会报错。
注册扩展匹配规则和扩展目标
首先看一个全局变量xt,其为数组链表,类型为struct xt_af,match成员用来保存注册的扩展匹配规则,target成员用来保存注册的扩展目标。xt在xt_init初始化时分配内存,大小为NFPROTO_NUMPROTO乘sizeof(struct xt_af),这说明每种协议使用一个链表。
struct xt_af {
struct mutex mutex;
struct list_head match;
struct list_head target;
};
static struct xt_af *xt;
xt_init
xt = kmalloc(sizeof(struct xt_af) * NFPROTO_NUMPROTO, GFP_KERNEL);
if (!xt)
return -ENOMEM;
for (i = 0; i < NFPROTO_NUMPROTO; i++) {
mutex_init(&xt[i].mutex);
INIT_LIST_HEAD(&xt[i].target);
INIT_LIST_HEAD(&xt[i].match);
}
扩展匹配注册
扩展匹配规则的结构体为xt_match,其定义如下
struct xt_match {
//链表节点
struct list_head list;
//添加规则时,首先根据name和revision到xt[af].match中查找
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. */
//数据包匹配扩展规则时,调用match进行匹配
bool (*match)(const struct sk_buff *skb,
struct xt_action_param *);
//添加规则时,调用checkentry做校验
/* 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 *);
/* Set this to THIS_MODULE if you are a module, otherwise NULL */
struct module *me;
//只对指定的table生效
const char *table;
unsigned int matchsize;
#ifdef CONFIG_COMPAT
unsigned int compatsize;
#endif
//只在指定的hook点上生效
unsigned int hooks;
//只对指定协议生效
unsigned short proto;
//此扩展规则所属地址族
unsigned short family;
};
扩展匹配规则通过xt_register_matches或者xt_register_match注册到全局变量xt[af].match中,可以在kernel代码中搜索这俩函数查看当前注册了哪些扩展匹配规则。
//xt_register_matchs可以一次性注册多个扩展match
int
xt_register_matches(struct xt_match *match, unsigned int n)
{
unsigned int i;
int err = 0;
for (i = 0; i < n; i++) {
err = xt_register_match(&match[i]);
if (err)
goto err;
}
return err;
err:
if (i > 0)
xt_unregister_matches(match, i);
return err;
}
int xt_register_match(struct xt_match *match)
{
u_int8_t af = match->family;
mutex_lock(&xt[af].mutex);
list_add(&match->list, &xt[af].match);
mutex_unlock(&xt[af].mutex);
return 0;
}
扩展匹配规则在kernel中注册后,用户态iptables命令就可以在下发命令时指定match了。
iptables添加规则指定match时需要另一个结构体struct xt_entry_match ,它是union类型。用户态需要设置 u.user 里的字段u.user.name和u.user.revision,指定使用哪个match,在内核态用u.user.name和u.user.revision在链表xt[af].match上进行查找,如果找到match了就设置到u.kernel.match = match。
struct xt_entry_match {
union {
struct {
__u16 match_size;
/* Used by userspace */
char name[XT_EXTENSION_MAXNAMELEN];
__u8 revision;
} user;
struct {
__u16 match_size;
/* Used inside the kernel */
struct xt_match *match;
} kernel;
/* Total length */
__u16 match_size;
} u;
//扩展匹配规则的参数
unsigned char data[0];
};
下发规则时会调用函数find_check_entry遍历ipt_entry(代表一条rule)里的扩展规则,在链表xt[af].match上进行查找,如果查找到了则调用扩展规则的函数checkentry进行检查。接着获取扩展target,进行检查。
static int
find_check_entry(struct ipt_entry *e, struct net *net, const char *name,
unsigned int size)
{
struct xt_entry_target *t;
struct xt_target *target;
int ret;
unsigned int j;
struct xt_mtchk_param mtpar;
struct xt_entry_match *ematch;
j = 0;
mtpar.net = net;
mtpar.table = name;
mtpar.entryinfo = &e->ip;
mtpar.hook_mask = e->comefrom;
mtpar.family = NFPROTO_IPV4;
//遍历e指定的扩展匹配规则,每个扩展匹配规则定义为struct xt_entry_match
xt_ematch_foreach(ematch, e) {
//在链表xt[af].match上查找扩展匹配规则
find_check_match(ematch, &mtpar);
++j;
}
//获取target,查找target,并做检查
t = ipt_get_target(e);
target = xt_request_find_target(NFPROTO_IPV4, t->u.user.name,
t->u.user.revision);
t->u.kernel.target = target;
ret = check_target(e, net, name);
return 0;
}
find_check_match在链表xt[af].match上查找扩展匹配规则,如果查找到了则调用函数check_match进行一系列的检查。
static int
find_check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
{
struct xt_match *match;
int ret;
//根据name和revison查找match
match = xt_request_find_match(NFPROTO_IPV4, m->u.user.name,
m->u.user.revision);
//查找失败,返回error
if (IS_ERR(match)) {
duprintf("find_check_match: `%s' not found\n", m->u.user.name);
return PTR_ERR(match);
}
//查找成功,将查找结果match赋到u.kernel.match
m->u.kernel.match = match;
//根据match本身的要求检查下发的扩展匹配规则是否被允许
ret = check_match(m, par);
if (ret)
goto err;
return 0;
err:
module_put(m->u.kernel.match->me);
return ret;
}
check_match再调用xt_check_match对下发的扩展匹配规则进行如下检查:
a. 检查添加的match规则的size是不是扩展规则模块指定的matchsize
b. 检查添加的match规则的table是不是扩展规则模块指定的
c. 检查添加的match规则的hook是不是扩展规则模块指定的
d. 检查添加的match规则中proto是不是扩展规则模块指定的
e. 调用扩展匹配规则的checkentry检查参数特定的参数
static int
check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
{
const struct ipt_ip *ip = par->entryinfo;
int ret;
par->match = m->u.kernel.match;
par->matchinfo = m->data;
ret = xt_check_match(par, m->u.match_size - sizeof(*m),
ip->proto, ip->invflags & IPT_INV_PROTO);
if (ret < 0) {
duprintf("check failed for `%s'.\n", par->match->name);
return ret;
}
return 0;
}
int xt_check_match(struct xt_mtchk_param *par,
unsigned int size, u_int8_t proto, bool inv_proto)
{
int ret;
//检查添加的match规则的size是不是扩展规则模块指定的matchsize
if (XT_ALIGN(par->match->matchsize) != size &&
par->match->matchsize != -1) {
/*
* ebt_among is exempt from centralized matchsize checking
* because it uses a dynamic-size data set.
*/
pr_err("%s_tables: %s.%u match: invalid size "
"%u (kernel) != (user) %u\n",
xt_prefix[par->family], par->match->name,
par->match->revision,
XT_ALIGN(par->match->matchsize), size);
return -EINVAL;
}
//检查添加的match规则的table是不是扩展规则模块指定的
if (par->match->table != NULL &&
strcmp(par->match->table, par->table) != 0) {
pr_err("%s_tables: %s match: only valid in %s table, not %s\n",
xt_prefix[par->family], par->match->name,
par->match->table, par->table);
return -EINVAL;
}
//检查添加的match规则的hook是不是扩展规则模块指定的
if (par->match->hooks && (par->hook_mask & ~par->match->hooks) != 0) {
char used[64], allow[64];
pr_err("%s_tables: %s match: used from hooks %s, but only "
"valid from %s\n",
xt_prefix[par->family], par->match->name,
textify_hooks(used, sizeof(used), par->hook_mask,
par->family),
textify_hooks(allow, sizeof(allow), par->match->hooks,
par->family));
return -EINVAL;
}
//检查添加的match规则中proto是不是扩展规则模块指定的
if (par->match->proto && (par->match->proto != proto || inv_proto)) {
pr_err("%s_tables: %s match: only valid for protocol %u\n",
xt_prefix[par->family], par->match->name,
par->match->proto);
return -EINVAL;
}
//最后调用扩展匹配规则的checkentry检查参数特定的参数
if (par->match->checkentry != NULL) {
ret = par->match->checkentry(par);
if (ret < 0)
return ret;
else if (ret > 0)
/* Flag up potential errors. */
return -EIO;
}
return 0;
}
扩展目标注册
扩展目标的结构体为struct xt_target,其定义如下
/* Registration hooks for targets. */
struct xt_target {
//链表节点
struct list_head list;
//target名字和版本
const char name[XT_EXTENSION_MAXNAMELEN];
u_int8_t revision;
//数据包匹配规则后,执行的target
/* Returns verdict. Argument order changed since 2.6.9, as this
must now handle non-linear skbs, using skb_copy_bits and
skb_ip_make_writable. */
unsigned int (*target)(struct sk_buff *skb,
const struct xt_action_param *);
/* Called when user tries to insert an entry of this type:
hook_mask is a bitmask of hooks from which it can be
called. */
/* Should return 0 on success or an error code otherwise (-Exxxx). */
//添加规则时,调用checkentry检查target指定的参数是否正确
int (*checkentry)(const struct xt_tgchk_param *);
/* Called when entry of this type deleted. */
void (*destroy)(const struct xt_tgdtor_param *);
/* Set this to THIS_MODULE if you are a module, otherwise NULL */
struct module *me;
//只对指定的table生效
const char *table;
unsigned int targetsize;
//只对指定的hook点生效
unsigned int hooks;
//只对指定的协议生效
unsigned short proto;
//只对指定的地址族生效
unsigned short family;
};
扩展目标通过xt_register_targets或者xt_register_target注册到全局变量xt[af].target中,可以在kernel代码中搜索这俩函数查看当前注册了哪些扩展目标。
//xt_register_targets可以一次性注册多个扩展目标。
int
xt_register_targets(struct xt_target *target, unsigned int n)
{
unsigned int i;
int err = 0;
for (i = 0; i < n; i++) {
err = xt_register_target(&target[i]);
if (err)
goto err;
}
return err;
err:
if (i > 0)
xt_unregister_targets(target, i);
return err;
}
/* Registration hooks for targets. */
int xt_register_target(struct xt_target *target)
{
u_int8_t af = target->family;
mutex_lock(&xt[af].mutex);
list_add(&target->list, &xt[af].target);
mutex_unlock(&xt[af].mutex);
return 0;
}
扩展目标在kernel中注册后,用户态iptables命令就可以在下发命令时指定扩展目标了。
iptables命令添加扩展目标时还需要另一个结构体xt_entry_target ,它是union类型。用户态需要设置 u.user 里的字段u.user.name和u.user.revision,指定使用哪个扩展目标,在内核态用u.user.name和u.user.revision在链表xt[af].match上进行查找,如果找到了就设置到u.kernel.target= target。
struct xt_entry_target {
union {
struct {
__u16 target_size;
/* Used by userspace */
char name[XT_EXTENSION_MAXNAMELEN];
__u8 revision;
} user;
struct {
__u16 target_size;
/* Used inside the kernel */
struct xt_target *target;
} kernel;
/* Total length */
__u16 target_size;
} u;
//扩展目标的参数
unsigned char data[0];
};
下发规则后,在kernel中也会调用find_check_entry查找target,并做一系列参数校验,和match流程类似就不再多说了。
网友评论