在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数据包不通的问题。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请
点击举报。