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_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方向上的新建連接。目前在內核代碼中也沒有使用這個狀態。
原文地址:http://blog.chinaunix.net/uid-23629988-id-5165391.html