作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
微博:weibo.com/glinuxer
QQ技术群:4367710
github:
背景介绍
Netfilter是Linux防火墙内核部分的实现,其对应的用户空间工具即大名鼎鼎的iptables。
由于Netfilter代码及框架具有非常好的扩展性,所以很多中小公司都是基于Netfilter进行二次开发,即使其性能不是特别理想——这是由
Netfilter自身的设计决定的。
连接跟踪是状态防火墙或者七层网关的基础。这就要求对Netfilter连接跟踪模块要比较熟悉,才能做好二次开发。本文主要解析了Netfilter连接跟踪状态,更准确的说是skb数据包的连接状态。
本文是基于Linux 3.3.8内核代码进行解析的。
连接跟踪状态
下面代码是Netfilter定义的各种连接状态:
enum ip_conntrack_info {
/* Part of an established connection (either direction). */
IP_CT_ESTABLISHED,
/* Like NEW, but related to an existing connection, or ICMP error
(in either direction). */
IP_CT_RELATED,
/* Started a new connection to track (only
IP_CT_DIR_ORIGINAL); may be a retransmission. */
IP_CT_NEW,
/* >= this indicates reply direction */
IP_CT_IS_REPLY,
IP_CT_ESTABLISHED_REPLY = IP_CT_ESTABLISHED + IP_CT_IS_REPLY,
IP_CT_RELATED_REPLY = IP_CT_RELATED + IP_CT_IS_REPLY,
IP_CT_NEW_REPLY = IP_CT_NEW + IP_CT_IS_REPLY,
/* Number of distinct IP_CT types (no NEW in reply dirn). */
IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1
};
接下来将对每一种具体状态进行解析。
IP_CT_ESTABLISHED
毫无疑问,表示连接建立。请注意不要与TCP连接搞混,这里的连接是广义的连接。
什么时候内核判定为连接已经建立了呢?在连接匹配函数resolve_normal_ct
中:
/* It exists; we have (non-exclusive) reference. */
if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) {
*ctinfo = IP_CT_ESTABLISHED_REPLY;
*set_reply = 1;
} else {
/* Once we've had two way comms, always ESTABLISHED. */
if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
pr_debug("nf_conntrack_in: normal packet for %p\n", ct);
*ctinfo = IP_CT_ESTABLISHED;
}
...... ......
}
从上面的代码中,可以看成,在内核收到ORIGINAL方向的数据包,并且设置了IPS_SEEN_REPLY_BIT
(看到过REPLY方向的数据包)时,就将连接状态设置为IP_CT_ESTABLISHED
。也就是说,在内核收到双方向数据包后,并再次收到ORIGINAL方向数据包时,即视连接已经建立。
那么IPS_SEEN_REPLY_BIT
什么时候被设置上的呢?上面的代码中可以看到*set_reply被设置为1。在nf_conntrack_in
中,
if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status))
nf_conntrack_event_cache(IPCT_REPLY, ct);
如果发现set_reply被赋值为1,则设置IPS_SEEN_REPLY_BIT
标志位。
IP_CT_RELATED
表示一个与其它连接关联的新建连接。
1)在icmp_error_message
中,如果收到的是ICMP报文,并且根据其payload找到了已有连接,则将连接状态设置为IP_CT_RELATED
。
2)在init_conntrack
中新建连接时,发现其匹配一个expect连接,则将其设置上IPS_EXPECTED_BIT
标志。在resolve_normal_ct
中发现数据包是ORIGINAL方向并且没有收到过REPLY方向的数据包,并且连接设置了IPS_EXPECTED_BIT
标志,则该数据包的连接状态则设置为IP_CT_RELATED
。
if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
...... ......
} else if (test_bit(IPS_EXPECTED_BIT, &ct->status)) {
pr_debug("nf_conntrack_in: related packet for %p\n",
ct);
*ctinfo = IP_CT_RELATED;
} else {
pr_debug("nf_conntrack_in: new packet for %p\n", ct);
*ctinfo = IP_CT_NEW;
}
IP_CT_NEW
表示一个新建连接.
与IP_CT_RELATED
相似,只不过在新建连接时没有匹配中expect连接,从而没有设置上EXPECTED标志。这样当收到ORIGINAL方向的数据包,并且没有收到过REPLY方向数据包,连接又没有EXPECTED标志,则数据包的连接状态为IP_CT_NEW
。
IP_CT_IS_REPLY
这并不是一个单一状态,更像一个标志位作用。目前只有在icmp_error_message
中使用:
if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY)
*ctinfo += IP_CT_IS_REPLY;
当发现关联的tuple是REPLY方向,则给数据包的连接状态设置上IP_CT_IS_REPLY
状态。
IP_CT_ESTABLISHED_REPLY
在resolve_normal_ct
中,只要收到REPLY方向的数据包就设置为IP_CT_ESTABLISHED_REPLY
。
IP_CT_NEW_REPLY
从字面上看是一个REPLY方向的新建连接状态。本身这就是一个矛盾,或者说不可能存在的状态。对于Netfilter来说,发起连接的方向,被认为是ORIGINAL方向。那么就不可能存在REPLY方向上的新建连接。目前在内核代码中也没有使用这个状态。