Linux Netfilter框架分析

Init0ne發表於2021-07-11

Netfilter框架

netfilter是Linux底層包處理框架,在協議棧中提供了若干hook點,可以用於對資料包進行過濾、修改、地址轉換(SNAT/DNAT)等處理。

Netfilter的5個hook點

netfilter在核心協議棧的不同位置實現了5個hook點:

---> PRE_ROUTING ---> [Routing Decision] ---> FORWARD ---> [Routing Decision] ---> POST_ROUTING --->
                                  |                                   ^
                                  |                                   |
                                  v                                   |
                                 LOCAL_IN                        LOCAL_OUT
                                  |                                   ^
                                  |                                   |
                                  v                                   |
                                             LOCAL PROCESS
  • NF_IP_PRE_ROUTING:資料包一進入協議棧即觸發,在進行任何路由判斷之前
  • NF_IP_LOCAL_IN:經過路由判斷,如果資料包目的是本機,將觸發該hook
  • NF_IP_FORWARD:經過路由判斷,如果資料包目的是其他主機,將觸發該hook轉發
  • NF_IP_LOCAL_OUT:本機準備傳送的資料包,在進入協議棧後觸發該hook
  • NF_IP_POST_ROUTING:準備發出去的包或轉發的包,經過路由判斷後,離開網路卡前的最後一個hook點
// include/uapi/linux/netfilter_ipv4.h

/* IP Hooks */
/* After promisc drops, checksum checks. */
#define NF_IP_PRE_ROUTING	0
/* If the packet is destined for this box. */
#define NF_IP_LOCAL_IN		1
/* If the packet is destined for another interface. */
#define NF_IP_FORWARD		2
/* Packets coming from a local process. */
#define NF_IP_LOCAL_OUT		3
/* Packets about to hit the wire. */
#define NF_IP_POST_ROUTING	4
#define NF_IP_NUMHOOKS		5

ip_tables等核心模組可以通過向這5個hook點註冊處理函式(handler),當資料包經過hook點時,呼叫回撥函式handler對資料包進行處理。

netfilter協議棧資料流分析

Wikipedia上關於netfilter在協議棧中的架構圖
image

連線跟蹤conntrack

  • conntracknetfilter實現的連線跟蹤機制,是NATiptables狀態匹配(-m state)的基礎,conntrack依賴的核心模組為nf_conntrack
  • conntrack在核心中的位置有兩處:PREROUTINGOUTPUT之前,進入主機的所有資料包會通過PREROUTING處的conntrack,主機本地程式產生的資料包對外發出時會通過OUTPUT處的conntrack。從netfilter協議棧架構圖可以看出,conntrack所處的位置非常靠前,僅位於raw表之後,如果raw將資料包標記為NOTRACK,則conntrack不會跟蹤該資料包連線。
  • conntrack通過連線跟蹤表來維護所有的連線資訊,當有資料包通過conntrack時,通過判斷該連線為一條新建的連線,還是已有連線的響應資訊,對於新建連線在跟蹤表中新建一條連線條目,對於已有連線資訊則更新跟蹤表中對於連線的狀態。

conntrack連線跟蹤表條目

資料包經過conntrack時,conntrack會提取相關資訊來唯一標識一條連線,對於TCP/UDP協議,一條連線資訊通過源IP、源埠、目的IP、目的埠確定,對於ICMP協議,由type、code、id欄位確定。
在使用者態可以使用命令conntrack -L來檢視系統上的連線跟蹤表:

ipv4     2 tcp      6 33 SYN_SENT src=172.16.200.119 dst=172.16.202.12 sport=54786 dport=10051 [UNREPLIED] src=172.16.202.12 dst=172.16.200.119 sport=10051 dport=54786 mark=0 zone=0 use=2

如上是一條conntrack條目,它代表當前已跟蹤到的某個連線,conntrack維護的所有資訊都包含在這個條目中,通過它就可以知道某個連線處於什麼狀態

  • 此連線使用ipv4協議,是一條tcp連線(tcp的協議型別程式碼是6)
  • 33是這條conntrack條目在當前時間點的生存時間(每個conntrack條目都會有生存時間,從設定值開始倒數計時,倒數計時完後此條目將被清除),可以使用sysctl -a |grep conntrack | grep timeout檢視不同協議不同狀態下生存時間設定值,當然這些設定值都可以調整,注意若後續有收到屬於此連線的資料包,則此生存時間將被重置(重新從設定值開始倒數計時),並且狀態改變,生存時間設定值也會響應改為新狀態的值
  • SYN_SENT是到此刻為止conntrack跟蹤到的這個連線的狀態(核心角度),SYN_SENT表示這個連線只在一個方向傳送了一初始TCP SYN包,還未看到響應的SYN+ACK包(只有tcp才會有這個欄位)。
  • src=172.16.200.119 dst=172.16.202.12 sport=54786 dport=10051是從資料包中提取的此連線的源目地址、源目埠,是conntrack首次看到此資料包時候的資訊。
  • [UNREPLIED]說明此刻為止這個連線還沒有收到任何響應,當一個連線已收到響應時,[UNREPLIED]標誌就會被移除
  • 接下來的src=172.16.202.12 dst=172.16.200.119 sport=10051 dport=54786地址和埠和前面是相反的,這部分不是資料包中帶有的資訊,是conntrack填充的資訊,代表conntrack希望收到的響應包資訊。意思是若後續conntrack跟蹤到某個資料包資訊與此部分匹配,則此資料包就是此連線的響應資料包。注意這部分確定了conntrack如何判斷響應包(tcp/udp),icmp是依據另外幾個欄位

連線跟蹤表大小

連線跟蹤表能夠存放的conntrack條目的最大值,即系統執行的最大連線跟蹤數記作CONNTRACK_MAX
在核心中,連線跟蹤表示一個二維陣列結構的雜湊表,雜湊表的大小記作HASHSIZE,雜湊表的每一項稱為bucket,因此雜湊表中有HASHSIZEbucket,每個bucket包含一個連結串列,每個連結串列能夠存放若干個conntrack條目(bucket size)。

因此,系統允許的最大連線跟蹤數為:
CONNTRACK_MAX = HASHSIZE * bucket size

#檢視系統當前最大連線跟蹤數CONNTRACK_MAX
sysctl -a | grep net.netfilter.nf_conntrack_max
#net.netfilter.nf_conntrack_max = 3203072

#檢視當前連線跟蹤表大小HASHSIZE
sysctl -a | grep net.netfilter.nf_conntrack_buckets
#400384
#或者這樣
cat /sys/module/nf_conntrack/parameters/hashsize
#400384 

這兩個的比值即為bucket size

對於新收到的資料包,核心使用如下步驟判斷該資料包是否屬於已有連線:

  • 核心提取此資料包資訊(源IP、源埠、目的IP、目的埠、協議號)進行hash計算得到hash值,在雜湊表中以此hash值做索引進行查詢,查詢結果即為該資料包所屬的bucket。這一步計算時間很短
  • 遍歷對應的bucket,查詢是否能匹配到conntrack條目。bucket size越大,遍歷時間越長

管理連線跟蹤表

在使用者態,使用工具conntrack實現對連線跟蹤表的增刪改查操作

#檢視連線跟蹤表所有條目  
conntrack -L
#清除連線跟蹤表
conntrack -F
#刪除連線跟蹤表中所有源地址是1.2.3.4的條目
conntrack -D -s 1.2.3.4

iptables

iptables是Linux系統上的主機防火牆,依賴於netfilter框架實現,在核心態通過ip_tables核心模組與netfilter互動。
iptables由table和chain組成,以前是四表五鏈,新增後已經不止四表了。可以說table是chain的集合,chain是iptables規則的集合。

iptables table

iptables規則通過table來組織,根據需要做的操作分為Filter Table、NAT Table、Mangle Table、Raw Table、Security Table等。

  • Filter Table:過濾功能,判斷一個資料包是否應該放行
  • NAT Table:地址轉換
  • Mangle Table:修改包的IP頭,如TTL、服務型別
  • Raw Table:決定資料包是否被連線跟蹤機制處理,對於不需要跟蹤的資料包可以打上NOTRACK標籤
  • Security Table:標記SELinux

iptables chain

在每個table內,規則進一步組織成chain,5個chain與netfilter的5個hook點一一對應:

  • PREROUTING: 由NF_IP_PRE_ROUTING 觸發
  • INPUT: 由NF_IP_LOCAL_IN 觸發
  • FORWARD: 由NF_IP_FORWARD 觸發
  • OUTPUT: 由NF_IP_LOCAL_OUT 觸發
  • POSTROUTING: 由NF_IP_POST_ROUTING 觸發

chain的優先順序:

  • 收到的目的為本機的包:PREROUTING->INPUT
  • 收到的目標為其他主機的包:PREROUTING->FORWARD->POSTROUTING
  • 本機產生準備發出的包:OUTPUT->POSTROUTING

table和chain的關係

以上說明了iptables有哪些table和哪些chain,接下來討論兩個問題:

  1. 每個table裡面都有哪些chain
    下面表格展示了table和chain的關係,橫向是table,縱向是chain,標記Y的表示table裡有這個chain,例如讓raw表中有PREROUTING和OUTPUT兩個chain。
  2. 註冊到同一個hook的不同chain執行的優先順序問題,例如3個table中都有PREROUTING這條chain,應該按照怎樣的順序呼叫他們?
    對應到列從上往下,就是hook點觸發時chain的呼叫順序。當一個包觸發netfilter hook點時,處理過程將沿著列從上向下執行
table/chain PREROUTING INPUT FORWARD OUTPUT POSTROUTING
[routing decision] Y
raw Y Y
連線跟蹤 Y Y
mangle Y Y Y Y Y
nat(DNAT) Y Y
[routing decision] Y Y
filter Y Y Y
security Y Y Y
nat(SNAT) Y Y

iptables狀態匹配

conntrack可以跟蹤資料包的狀態,iptables使用-m state進行狀態匹配正是使用了conntrack連線跟蹤表中標記的狀態。
資料包核心態狀態比較多,對映到使用者空間有5種狀態:

  • NEW:新到達的包為合法包並且在連線跟蹤表中關聯不到,則為這個包建立一條新連線條目
  • ESTABLISHED:接收到的包為已有連線的響應包,則將NEW狀態改為ESTABLISHED狀態。對於TCP連線來說,就是跟SYN包對應的SYN/ACK包,對於UDP、ICMP來說就是與源相反的包
  • RELATED:接收到的包不屬於已有連線,但是和已有連線存在一定的關係,稱為輔助連線,例如 FTP 資料傳輸連線,或者是其他協議試圖建立連線時的 ICMP 應答包
  • INVALID:包無法識別等原因,標記為非法
  • UNTRACKED:raw表中標記為NOTRACK

參考

https://opengers.github.io/openstack/openstack-base-netfilter-framework-overview/#conntrack條目
http://arthurchiao.art/blog/conntrack-design-and-implementation-zh/#5-個-hook-點
https://arthurchiao.art/blog/deep-dive-into-iptables-and-netfilter-arch-zh/#chain-遍歷優先順序

eBPF開發:
https://duo.com/labs/tech-notes/writing-an-xdp-network-filter-with-ebpf
https://github.com/cloudflare/cloudflare-blog/blob/master/2018-07-dropping-packets/xdp-drop-ebpf.c
https://gist.github.com/fntlnz/f6638d59e0e39f0993219684d9bf57d3
https://davidlovezoe.club/wordpress/archives/937

相關文章