在Linux内核中数据包会有连接跟踪的功能,Linux内核根据数据包的五元组创建连接跟踪表。但对于有些数据包是不建立连接的,本篇文章介绍了ICMP的回应报文reply,就不会在内核中创建连接会话。我们知道会话的建立是在NF_IP_PRE_ROUTING的HOOK点创建。
在ipv4_conntrack_in函数中调用了nf_conntrack_in函数对数据包进行会话的建立,其中最关键的函数是resolve_normal_ct(),在该函数中有下面一段代码:
-
h = nf_conntrack_find_get(net, &tuple);/*这里对会话进行查找,如果没有查找到,调用init_conntrack函数Allocate a new conntrack*/
-
if (!h) {
-
h = init_conntrack(net, &tuple, l3proto, l4proto, skb, dataoff, hooknum);
-
if (!h)
-
return NULL;
-
if (IS_ERR(h))
-
return (void *)h;
-
}
在ICMP的Request请求包经过Linux内核中会创建一条会话,如果这条会话成功的存在于会话连接表中,就没有机会调用init_conntrack函数,我们看一下该函数中做对数据包做了什么处理:该函数主要是分配一个conntrack,其中有下面的代码:
-
if (!l4proto->new(ct, skb, dataoff)) {
-
nf_conntrack_free(ct);
-
pr_debug("init conntrack: can't track with proto module\n");
-
/*return NULL; */
-
goto ERR;
-
}
如果是ICMP报文的话,调用icmp_new函数,当连接是一个新的连接时,会调用到该函数,对于Request数据报文就正常就会返回true,下面是icmp_new函数代码:
-
static bool icmp_new(struct nf_conn *ct, const struct sk_buff *skb,
-
unsigned int dataoff)
-
{
-
static const u_int8_t valid_new[] = {
-
[ICMP_ECHO] = 1,
-
[ICMP_TIMESTAMP] = 1,
-
[ICMP_INFO_REQUEST] = 1,
-
[ICMP_ADDRESS] = 1
-
};
-
-
if (ct->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new)
-
|| !valid_new[ct->tuplehash[0].tuple.dst.u.icmp.type]) {
-
/* Can't create a new ICMP `conn' with this. */
-
pr_debug("icmp: can't create new conn with type %u\n",
-
ct->tuplehash[0].tuple.dst.u.icmp.type);
-
nf_ct_dump_tuple_ip(&ct->tuplehash[0].tuple);
-
return false;
-
}
-
return true;
在上面的代码中我们看到,在if的判断中有对连接的icmp数据包类型的判断,如果不是valid_new数组中的报文类型都会返回false,也就是数据包会在该HOOK点DROP掉。在valid_new数组中并没有ICMP_ECHOREPLY类型的报文,也就是如果是reply报文的话,这里会被直接的DROP掉。所以在REQUEST数据包通过该HOOK点是,创建一条连接同时也会创建一条反方向的连接,所以,当REPLY报文被收到时,正常情况下会查找到相应的连接会话,也就是h = nf_conntrack_find_get(net, &tuple);函数返回的h不为空。因此,对于reply报文是不会重新创建会话的,如果ICMP的REPLY报文走到icmp_new函数处,说明ICMP的REQUEST会话并没有被创建,在查找会话时失败,同时造成了ICMP数据包不通的问题。
阅读(4818) | 评论(0) | 转发(4) |