分类: LINUX
2010-01-22 16:34:19
本章介绍了表的过滤操作:
1. 从挂载点函数如何找到表包含的规则;
2. 如何遍历规则;
3. 如何对规则的基本match和扩展match进行匹配与否的判定,涉及到.match函数指针;
4. 以及匹配后,如何调用target进行包的处理,涉及到.target函数指针;
/* Returns one of the generic firewall policies, like NF_ACCEPT. */
unsigned int
ipt_do_table(struct sk_buff **pskb,
unsigned int hook,
const struct net_device *in,
const struct net_device *out,
struct ipt_table *table,
void *userdata)
{
static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
u_int16_t offset;
struct iphdr *ip;
u_int16_t datalen;
int hotdrop = 0;
/* Initializing verdict to NF_DROP keeps gcc happy. */
unsigned int verdict = NF_DROP;
const char *indev, *outdev;
void *table_base;
struct ipt_entry *e, *back;
struct xt_table_info *private;
/* Initialization */
ip = (*pskb)->nh.iph; // IP头指针
datalen = (*pskb)->len - ip->ihl * 4;
indev = in ? in->name : nulldevname; // net_device指针
outdev = out ? out->name : nulldevname;
/* We handle fragments by dealing with the first fragment as
* if it was a normal packet. All other fragments are treated
* normally, except that they will NEVER match rules that ask
* things we don't know, ie. tcp syn flag or ports). If the
* rule is also a fragment-specific rule, non-fragments won't
* match it. */
offset = ntohs(ip->frag_off) & IP_OFFSET;
read_lock_bh(&table->lock); // 对此table加锁
IP_NF_ASSERT(table->valid_hooks & (1 << hook)); // 调式语句,查看此hook点对合法否
private = table->private; // 取得table的实际数据区xt_table_info
table_base = (void *)private->entries[smp_processor_id()]; // 本CPU的规则基趾
e = get_entry(table_base, private->hook_entry[hook]); // 根据本HOOK点规则的偏移量找到本链规则基趾
/* For return from builtin chain */
back = get_entry(table_base, private->underflow[hook]);//对这个underflow不是很理解
do {
IP_NF_ASSERT(e);
IP_NF_ASSERT(back);
if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
参数:
ip是本次要匹配的skb里面的包的ip头;
e是每个ipt_entry(每条规则的最初部分),所以&e->ip就是ipt_ip结构体。基础匹配内容。
offset先不看。
概要:
确定IP数据报是否匹配规则,若不匹配则跳到下一条规则(这个函数的主要工作大致为:依次处理源/目的IP地址、输入输出接口,然后对基本的规则进行匹配)。
返回值:
和扩展的ipt_match里面的match()函数一样,0表示不匹配,1(非0)表示匹配。
struct ipt_entry_target *t;
if (IPT_MATCH_ITERATE(e, do_match, // 参考下面的详细说明
*pskb, in, out,
offset, &hotdrop) != 0) // 非0表示检查到某条扩展匹配不满足,
goto no_match; // 继续下条规则的检查
ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1); // 更新bype计数和packet计数
t = ipt_get_target(e); // return (void *)e + e->target_offset;
IP_NF_ASSERT(t->u.kernel.target); //
/* Standard target? */
if (!t->u.kernel.target->target) { // ipt_standard_target没有设置.target
int v;
v = ((struct ipt_standard_target *)t)->verdict; // 标准target或者jump
if (v < 0) {
说明是内建规则:
iptables:
TC_APPEND_ENTRY()
-> iptcc_map_target():
else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
return iptcc_standard_map(r, -NF_ACCEPT - 1);
/* Pop from stack? */
if (v != IPT_RETURN) {
verdict = (unsigned)(-v) - 1;
break;
}
e = back;
back = get_entry(table_base,
back->comefrom);
continue;
}
if (table_base + v != (void *)e + e->next_offset
&& !(e->ip.flags & IPT_F_GOTO)) {
/* Save old back ptr in next entry */
struct ipt_entry *next
= (void *)e + e->next_offset;
next->comefrom
= (void *)back - table_base;
/* set back pointer to next entry */
back = next;
}
e = get_entry(table_base, v);
} else { // 存在自定义的target()
/* Targets which reenter must return
abs. verdicts */
#ifdef CONFIG_NETFILTER_DEBUG
((struct ipt_entry *)table_base)->comefrom
= 0xeeeeeeec;
#endif
verdict = t->u.kernel.target->target(pskb, // 调用target()处理
in, out,
hook,
t->u.kernel.target,
t->data,
userdata);
#ifdef CONFIG_NETFILTER_DEBUG
if (((struct ipt_entry *)table_base)->comefrom
!= 0xeeeeeeec
&& verdict == IPT_CONTINUE) {
printk("Target %s reentered!\n",
t->u.kernel.target->name);
verdict = NF_DROP;
}
((struct ipt_entry *)table_base)->comefrom
= 0x57acc001;
#endif
/* Target might have changed stuff. */
ip = (*pskb)->nh.iph;
datalen = (*pskb)->len - ip->ihl * 4;
if (verdict == IPT_CONTINUE) // 如果target()函数返回的是IPT_CONTINUE
e = (void *)e + e->next_offset; // 那么继续下一条规则ipt_entry
else
/* Verdict */
break; // 否则不在处理规则
}
} else {
no_match: // 如果包连基本规则部分都不匹配,或扩展部分
e = (void *)e + e->next_offset; // 全部不匹配,那么就直接检查下一条规则
}
} while (!hotdrop); // 这个hotdrop直接(最终)是传递到用户自定义的match()函数里面的
// 过程IPT_MATCH_ITERATE()->do_match()->m->u.kernel.match->match()
// 因为本来它是0。所以,看来是用户强制终止,设置为NF_DROP。
// 问题是之后ipt_do_table()的返回值,又当作ipt_hook()之类hook函数的
// 返回值,这个返回值有什么用呢?看hook部分后再给出答案。
答案如下:
#define NF_DROP 0
okfn()将得不到执行。举例如下(ip_rcv_finish不能执行):
return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
ip_rcv_finish);
#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh) \
({int __ret; \
if ((__ret=nf_hook_thresh(pf, hook, &(skb), indev, outdev, okfn, thresh, 1)) == 1)\
__ret = (okfn)(skb); \
__ret;})
read_unlock_bh(&table->lock);
#ifdef DEBUG_ALLOW_ALL
return NF_ACCEPT;
#else
if (hotdrop)
return NF_DROP;
else return verdict;
#endif
}
应用实例:
/* The work comes in here from netfilter.c. */
static unsigned int
ipt_hook(unsigned int hook,
struct sk_buff **pskb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
return ipt_do_table(pskb, hook, in, out, &packet_filter, NULL);
}
filter表的NF_IP_LOCAL_IN和NF_IP_FORWARD点的钩子函数。
static struct nf_hook_ops ipt_ops[] = {
{
.hook = ipt_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
.priority = NF_IP_PRI_FILTER,
},
{
.hook = ipt_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_FORWARD,
.priority = NF_IP_PRI_FILTER,
},
{
.hook = ipt_local_out_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_OUT,
.priority = NF_IP_PRI_FILTER,
},
};
if (IPT_MATCH_ITERATE(e, do_match,
*pskb, in, out,
offset, &hotdrop) != 0)
goto no_match; // 返回0表示扩展规则全部匹配,所以继续下面的处理
/* fn returns 0 to continue iteration */
#define IPT_MATCH_ITERATE(e, fn, args...) \ // 对某一条规则的全部扩展匹配进行检查
e是ipt_entry结构体,参考下面。
fn是函数do_match()。这个函数的返回值和具体进行匹配检查的函数的返回值是相反的。参考下面。
如果传进来的fn函数,对某个ipt_entry_match返回一个非0值,那么IPT_MATCH_ITERATE就直接以此值返回。所以可以说,IPT_MATCH_ITERATE返回的就是它的函数指针参数fn的返回值。
({ \
unsigned int __i; \
int __ret = 0; \
struct ipt_entry_match *__match; \
\
for (__i = sizeof(struct ipt_entry); \ // 初始偏移量为sizeof(struct ipt_entry),第一个
__i < (e)->target_offset; \ // 如果还不到target_offset,说明还有扩展匹配
__i += __match->u.match_size) { \ // 满足某条扩展匹配后,找到下一条的地址
__match = (void *)(e) + __i; \ // 获得当前要检查的ipt_entry_match
\
__ret = fn(__match , ## args); \
if (__ret != 0) \ // 返回0继续别的扩展匹配检查。表明此条扩展匹配满足。
break; \ // 返回1跳出别的扩展匹配检查。表明此条扩展匹配不满足。
} \ // 因为跳出别的也就是说,当前规则中只有有任何一个
__ret; \ // 扩展匹配不满足,就相当于这条规则不满足。
})
e是ipt_entry结构体:
struct ipt_entry
{
struct ipt_ip ip;
/* Mark with fields that we care about. */
unsigned int nfcache;
/* Size of ipt_entry + matches */
u_int16_t target_offset;
/* Size of ipt_entry + matches + target */
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];
};
static inline
int do_match(struct ipt_entry_match *m,
const struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int offset,
int *hotdrop)
对一个扩展匹配,进行匹配与否的检查,并返回检查结果。
返回0表明此条扩展匹配满足。
返回1表明此条扩展匹配不满足。
检查是通过调用此扩展匹配的函数指针m->u.kernel.match->match()来进行的。
两个扩展匹配的例子如下:
[root@RHEL5 yuzg]# iptables-save
# Generated by iptables-save v
*filter
:INPUT ACCEPT [177106:36087403]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [38811:9792053]
-A INPUT -m addrtype --src-type BROADCAST -j ACCEPT
-A INPUT -s 192.168.0.1 -m addrtype --dst-type BROADCAST -j ACCEPT
-A INPUT -s 192.168.0.1 -p udp -m udp --sport 80 -m addrtype --dst-type BROADCAST -j ACCEPT
-A INPUT -s 192.168.0.1 -p udp -m udp --sport 400:65535 -m addrtype --dst-type BROADCAST -j ACCEPT
-A INPUT -s 192.168.0.1 -p udp -m udp --sport 0:5 -m addrtype --dst-type BROADCAST -j ACCEPT
{
/* Stop iteration if it doesn't match */
if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data,
offset, skb->nh.iph->ihl*4, hotdrop))
具体match()函数的第四个参数是指函数所在的ipt_match;
第5个参数是指当前ipt_entry_match的数据域,也就是和用户空间约定好的数据结构。
具体参考下面的addrtype的match()函数。
return 1; // 1表示不匹配。但m->u.kernel.match->match()返回的是0。
else
return 0; // 0表示匹配。但m->u.kernel.match->match()返回的是1。
}
也就是说m->u.kernel.match->match()对匹配的返回值,和do_match()以及IPT_MATCH_ITERATE()是正好相反的。
static int match(const struct sk_buff *skb,
const struct net_device *in, const struct net_device *out,
const struct xt_match *match, const void *matchinfo,
int offset, unsigned int protoff, int *hotdrop) // hotdrop的用法还不理解。
{
const struct ipt_addrtype_info *info = matchinfo; // 这里直接取得了约定好的数据结构。
const struct iphdr *iph = skb->nh.iph;
int ret = 1; // 默认匹配
if (info->source)
ret &= match_type(iph->saddr, info->source)^info->invert_source;
if (info->dest)
ret &= match_type(iph->daddr, info->dest)^info->invert_dest;
return ret; // 0:不匹配;1:匹配
}