Linux Netfilter框架分析


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM