上面分析了table,chain,rule的存储结构,现在我们来看一看ipv4 Netfilter是怎么根据这些配置处理报文的。
报文被hook函数拦截,开始在相应的表中进行rule的遍历,如果匹配上了一条rule,就由该rule中的target来进行处理。根据target的返回值来决定报文的命运。
首先我们来看看Ipv4 Netfilter中实现的表遍历函数 ipt_do_table的处理流程。该函数是处理报文的核心函数。
函数流程图的几点说明:
1、假设从chain1 goto到 user chain时,遍历完user chain中的rule后,如果报文没有被匹配处理,就使用chain1 中的默认rule来处理报文。
如果rule配置时使用的是-j user chain,跳到user chain开始遍历,如果没有被匹配处理,就会返回 chain1中下一条rule继续执行。
以上两种情况,back记录的返回指针不一样。
2、如果rule中的target执行结果返回return,就使用chain中默认的rule来处理报文。
net/ipv4/netfilter/ip_tables.c
unsigned int
ipt_do_table(struct sk_buff *skb,
unsigned int hook,
const struct net_device *in,
const struct net_device *out,
struct xt_table *table)
{
#define tb_comefrom ((struct ipt_entry *)table_base)->comefrom
static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
const struct iphdr *ip;
bool hotdrop = false;
/* 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;
struct xt_match_param mtpar;
struct xt_target_param tgpar;
/* Initialization */
/*根据报文来记录match和target使用的参数*/
ip = ip_hdr(skb);
indev = in ? in->name : nulldevname;
outdev = out ? out->name : nulldevname;
mtpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
mtpar.thoff = ip_hdrlen(skb);
mtpar.hotdrop = &hotdrop;
mtpar.in = tgpar.in = in;
mtpar.out = tgpar.out = out;
mtpar.family = tgpar.family = NFPROTO_IPV4;
mtpar.hooknum = tgpar.hooknum = hook;
IP_NF_ASSERT(table->valid_hooks & (1 << hook));
//加锁,现在各个cpu使用的存储区是独立的,所以不存在多个cpu锁竞争
xt_info_rdlock_bh();
private = table->private;
table_base = private->entries[
smp_processor_id()];
e = get_entry(table_base, private->hook_entry[hook]);
/* For return from builtin chain */
back = get_entry(table_base, private->underflow[hook]);
/*循环遍历表的rule*/
do {
struct ipt_entry_target *t;
IP_NF_ASSERT(e);
IP_NF_ASSERT(back);
if (!ip_packet_match(ip, indev, outdev,
&e->ip, mtpar.fragoff) ||
IPT_MATCH_ITERATE(e,
do_match, skb, &mtpar) != 0)
//遍历匹配match
{
e = ipt_next_entry(e);
continue;
}
ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1);
/*取得target*/
t = ipt_get_target(e);
IP_NF_ASSERT(t->u.kernel.target);
/* Standard target? */
/*如果是标准Target*/
if (!t->u.kernel.target->target)
{
int v;
v = ((struct ipt_standard_target *)t)->verdict;
/*如果v大于0,记录是跳转偏移量,小于0,是标准target*/
if (v < 0)
{
/* Pop from stack? */
/*如果不等于return,直接返回*/
if (v != IPT_RETURN) {
verdict = (unsigned)(-v) - 1;
break;
}
/*如果是return,执行chain中默认rule处理报文*/
e = back;
back = get_entry(table_base, back->comefrom);
continue;
}
if (table_base + v != ipt_next_entry(e)
//跳转到下一条rule不是紧挨着的这个rule*/
&& !(e->ip.flags & IPT_F_GOTO))
{
/* Save old back ptr in next entry */
struct ipt_entry *next = ipt_next_entry(e);
next->comefrom = (void *)back - table_base;
/* set back pointer to next entry */
back = next;
}
/*根据verdict的偏移量找到跳转的rule*/
e = get_entry(table_base, v);
continue;
}
/* Targets which reenter must return abs. verdicts */
/*如果是扩展target,就执行扩展targe的target处理函数*/
tgpar.target = t->u.kernel.target;
tgpar.targinfo = t->data;
verdict = t->u.kernel.target->target(skb, &tgpar);
/* Target might have changed stuff. */
ip = ip_hdr(skb);
if (verdict == IPT_CONTINUE)
e = ipt_next_entry(e);
else
/* Verdict */
break;
} while (!hotdrop);
//释放锁
xt_info_rdunlock_bh();
#ifdef DEBUG_ALLOW_ALL
return NF_ACCEPT;
#else
if (hotdrop)
return NF_DROP;
else return verdict;
#endif
#undef tb_comefrom
}
(未完待续)