協(xié)議棧的細節(jié)
下面將介紹一些內(nèi)核網(wǎng)絡協(xié)議棧中常常涉及到的概念。
sk_buff
內(nèi)核顯然需要一個數(shù)據(jù)結構來表示報文,這個結構就是 sk_buff ( socket buffer 的簡稱),它等同于在<TCP/IP詳解 卷2>中描述的 BSD 內(nèi)核中的 mbuf。
sk_buff 結構自身并不存儲報文內(nèi)容,它通過多個指針指向真正的報文內(nèi)存空間:
sk_buff 是一個貫穿整個協(xié)議棧層次的結構,在各層間傳遞時,內(nèi)核只需要調(diào)整 sk_buff 中的指針位置就行。
net_device
內(nèi)核使用 net_device 表示網(wǎng)卡。網(wǎng)卡可以分為物理網(wǎng)卡和虛擬網(wǎng)卡。物理網(wǎng)卡是指真正能把報文發(fā)出本機的網(wǎng)卡,包括真實物理機的網(wǎng)卡以及VM虛擬機的網(wǎng)卡,而像 tun/tap,vxlan、veth pair 這樣的則屬于虛擬網(wǎng)卡的范疇。
如下圖所示,每個網(wǎng)卡都有兩端,一端是協(xié)議棧(IP、TCP、UDP),另一端則有所區(qū)別,對物理網(wǎng)卡來說,這一端是網(wǎng)卡生產(chǎn)廠商提供的設備驅(qū)動程序,而對虛擬網(wǎng)卡來說差別就大了,正是由于虛擬網(wǎng)卡的存在,內(nèi)核才能支持各種隧道封裝、容器通信等功能。
socket & sock
用戶空間通過 socket()、bind()、listen()、accept() 等庫函數(shù)進行網(wǎng)絡編程。而這里提到的 socket 和 sock 是內(nèi)核中的兩個數(shù)據(jù)結構,其中 socket 向上面向用戶,而 sock 向下面向協(xié)議棧。
如下圖所示,這兩個結構實際上是一一對應的。
注意到,這兩個結構上都有一個叫 ops 的指針, 但它們的類型不同。socket 的 ops 是一個指向 struct proto_ops 的指針,sock 的 ops 是一個指向 struct proto 的指針, 它們在結構被創(chuàng)建時確定。
回憶網(wǎng)絡編程中 socket() 函數(shù)的原型:
實際上, socket->ops 和 sock->ops 由前兩個參數(shù) socket_family 和 socket_type 共同確定。
如果 socket_family 是最常用的 PF_INET 協(xié)議簇, 則 socket->ops 和 sock->ops 的取值就記錄在 INET 協(xié)議開關表中:
L3->L4
我們知道網(wǎng)絡協(xié)議棧是分層的,但實際上,具體到實現(xiàn),內(nèi)核協(xié)議棧的分層只是邏輯上的,本質(zhì)還是函數(shù)調(diào)用。發(fā)送流程(上層調(diào)用下層)通常是直接調(diào)用(因為沒有不確定性,比如TCP知道下面一定IP),但接收過程不一樣了,比如報文在 IP 層時,它上面可能是 TCP,也可能是 UDP,或者是 ICMP 等等,所以接收過程使用的是注冊-回調(diào)機制。
還是以 INET 協(xié)議簇為例,注冊接口是:
在內(nèi)核網(wǎng)絡子系統(tǒng)初始化時,L4 層協(xié)議(如下面的 TCP 和 UDP)會被注冊:
而在IP層,查詢過路由后,如果該報文是需要上送本機的,則會根據(jù)報文的 L4 協(xié)議,送給不同的 L4 處理:
L2->L3
L2->L3 如出一轍。只不過注冊接口變成了:
誰會注冊呢?顯然至少 IP 會:
而在報文接收過程中,設備驅(qū)動程序會將報文的 L3 類型設置到 skb->protocol,然后在內(nèi)核 netif_receive_skb 收包時,會根據(jù)這個 protocol 調(diào)用不同的回調(diào)函數(shù):
Netfilter
Netfilter 是報文在內(nèi)核協(xié)議棧必然會通過的路徑,我們從下面這張圖就可以看到,Netfilter 在內(nèi)核的 5 個地方設置了 HOOK 點,用戶可以通過配置 iptables 規(guī)則,在 HOOK 點對報文進行過濾、修改等操作。
在內(nèi)核代碼中,我們時??梢?NF_HOOK 這樣的調(diào)用。我的建議是,如果你暫時不考慮 Netfilter,那么就直接跳過, 跟蹤 okfn 就行。
dst_entry
內(nèi)核需要確定收到的報文是應該本地上送(local deliver)還是轉發(fā)(forward),對本機發(fā)送(local out)的報文需要確定是從哪個網(wǎng)卡發(fā)送出去,這都是內(nèi)核通過查詢 fib (forward information base, 轉發(fā)信息表) 確定。fib 可以理解為一個數(shù)據(jù)庫,數(shù)據(jù)來源是用戶配置或者內(nèi)核自動生成的路由。
fib 查詢的輸入是報文 sk_buff,輸出是 dst_entry. dst_entry 會被設置到 skb 上:
而 dst_entry 中最重要的是一個 input 指針和 output 指針:
更多信息可以來這里獲取==>>電子技術應用-AET<<