kernel筆記 網路收發包流程

2021-09-20 22:20:17 字數 4343 閱讀 5439

本文將介紹網路連線建立的過程、收發包流程,以及其中應用層、tcp層、ip層、裝置層和驅動層各層發揮的作用。

應用層

對於使用socket進行網路連線的伺服器端程式,我們會先呼叫socket函式建立乙個套接字:

fd = socket(af_inet, sock_stream, 0);

以上指定了連線協議,socket呼叫返回乙個檔案控制代碼,與socket檔案對應的inode不在磁碟上,而是存在於記憶體。

之後我們指定監聽的埠、允許與哪些ip建立連線,並呼叫bind完成埠繫結:

server_addr.sin_family = af_inet;

server_addr.sin_port   = htons(port);

server_addr.sin_addr.s_addr = inaddr_any;

bind(fd, (struct sockaddr_in *)&server_addr, sizeof(struct sockaddr_in));

埠作為程序的標識,客戶端根據伺服器ip和埠號就能找到相應程序。

接著我們呼叫listen函式,對埠進行監聽:

listen(fd, backlog);

backlog值指定了監聽佇列的長度,以下核心引數限制了backlog可設定的最大值:

linux # sysctl -a | grep somaxconn

net.core.somaxconn = 128

監聽埠在listen呼叫後變為listen狀態:

linux # netstat -antp | grep 9999

proto  recv-q send-q local address  foreign address  state  pid/program name

tcp         0      0  0.0.0.0:9999        0.0.0.0:* listen       8709/server

相應地,客戶端呼叫connect進行連線,tcp三次握手在connect呼叫返回之前完成:

如果伺服器端向客戶端傳送syn+ack後,客戶端不返回ack,則伺服器保持半連線(syn_recv)狀態:

linux # netstat -np | grep syn_recv

tcp      0        0     0.0.0.0:9999  127.0.0.0.1:5334   syn_recv  - 

若佇列中的連線均處於半連線狀態,伺服器將不能處理正常的請求,syn泛洪攻擊(syn flood)就是利用這個特點完成dos(拒絕服務攻擊)。

當連線數超過佇列長度backlog時,超出的連線也保持為半連線狀態,直到數量達到核心引數tcp_max_syn_backlog值,超出該值的連線請求將被丟棄:

linux # sysctl -a | grep tcp_max_syn

net.ipv4.tcp_max_syn_backlog = 1024

accept呼叫用於處理新到來的連線:

new_fd = accept(fd, (struct sockaddr*)&client_addr, &sin_size);

其返回乙個檔案描述符,後續我們可以對該檔案描述符呼叫write、read等操作函式,原監聽埠仍處於listen狀態:

linux # netstat -antp | grep 9999

tcp     0    0    0.0.0.0:9999       0.0.0.0:*      listen  8709/server

tcp     0    0  127.0.0.1:9999 127.0.0.1:52274 established  -

以上為網路連線建立過程中,應用層所做的工作,server端完成了socket建立、埠繫結、埠監聽、連線和收發包任務,而client端相對簡單,只需包含連線和收發包。

tcp層

核心**中,tcp_sendmsg是tcp發包的主入口函式,該函式中struct sk_buff結構用於描述乙個資料報。

對於超過mtu(maximum transmission unit, 最大傳輸單元)的資料報,tcp層會對資料報進行拆分,若開啟了網口的tcp segmentation offload功能,則拆分工作由網絡卡完成:

linux # ethtool -k ether

offload parameters for eth1:

rx-checksumming: on

tx-checksumming: on

scatter-gather: on

tcp segmentation offload: on

以下核心引數是核心為tcp socket預留的用於傳送資料報的緩衝區大小,單位為byte:

linux # sysctl -a | grep tcp_wmem

net.ipv4.tcp_wmem = 4096 16384 131072

預設的用於包傳送的緩衝區大小為16m。

除了用於緩衝收發資料報,對於每個socket,核心還要分配一些資料結構用於保持連線狀態,核心對tcp層可使用的記憶體大小進行了限制:

linux # sysctl -a | grep tcp_mem

net.ipv4.tcp_mem = 196608 262144 393216

以上值以頁為單位,分別對應最小值、壓力值和最大值,並在系統啟動、tcp棧初始化時根據記憶體總量設定。通過proc提供的介面,我們可以查到tcp已用的記憶體頁數:

linux # cat /proc/net/sockstat

sockets : used 91

tcp : inuse 8 orphan 0 tw 11 alloc 13 mem 2

ip層

核心**中,ip_queue_xmit函式是ip層的主入口函式,注意ip層與tcp層操作的都是同一塊記憶體(sk_buff結構),期間並沒有發生資料報相關的記憶體拷貝。

ip層主要完成查詢路由的任務,其根據路由表配置,決定資料報發往哪個網口,另外,該層實現netfilter的功能。

網路裝置層

dev_queue_xmit是網路裝置層的主入口函式,該層為每個網口維護一條資料報佇列,由ip層下發的資料報放入對應網口的佇列中。在該層中,資料報不是直接交給網絡卡,而是先緩衝起來,再通過軟中斷(net_tx_softirq)呼叫qdisc_run函式,該函式將資料報進一步交由網絡卡處理。我們執行ifconfig時,txqueuelen指示了網路裝置層中,網口佇列的長度。

驅動層

使用不同驅動的網絡卡,相應的驅動層**就不一樣,這裡以e1000網絡卡為例。e1000_xmit_frame是該層的主入口函式,該層利用環形佇列進行資料報管理,由兩個指標負責維護環形佇列。執行ethtool命令,我們可以查詢網口驅動層環形佇列長度:

linux # ethtool -g eth1

ring parameters for ether

pre-set maximums:

rx : 511

rx mini : 0

rx jumbo : 0

tx : 511

current hardware settings:

rx : 200

rx mini : 0

rx jumbo : 0

tx : 511

以上rx與tx分別指示收包佇列與發包佇列長度,單位為包個數。

網絡卡接收到資料報時將產生中斷,以通知cpu資料報到來的訊息,而網絡卡接收包又非常繁忙,如果每次收發包都向cpu傳送硬中斷,那cpu將忙於處理網絡卡中斷。

相應的優化方案是napi(new api)模式,其關閉網絡卡硬中斷,使網絡卡不傳送中斷,而非使cpu不接收網絡卡中斷。在e1000驅動**中,由e1000_clean函式實現napi模式。

不像寫檔案的過程,磁碟裝置層完成記憶體資料到磁碟拷貝後,會將訊息層層上報,這裡的網絡卡驅動層發包後不會往上層傳送通知訊息。

收包過程

以上為網路發包所需經過的層次結構,以及各層的大體功能,下面我們簡單看下收包過程。

網絡卡接收到資料報後,通知上層,該過程不會發生拷貝,資料報丟給ip層。

核心**中,ip_rcv是ip層收包的主入口函式,該函式由軟中斷呼叫。存放資料報的sk_buff結構包含有目的地ip和埠資訊,此時ip層進行檢查,如果目的地ip不是本機,則將包丟棄,如果配置了netfilter,則按照配置規則對包進行**。

tcp_v4_rcv是tcp層收包的接收入口,其呼叫__inet_lookup_skb函式查到資料報需要往哪個socket傳送,之後將資料報放入tcp層收包佇列中,如果應用層有read之類的函式呼叫,佇列中的包將被取出。

linux # sysctl -a | grep rmem

net.ipv4.tcp_rmem = 4096 16384 131072

DPDK收發包處理流程 網絡卡初始化

本文基於dpdk 1.8.0分析。網絡卡驅動模型一般包含三層,即,pci 匯流排裝置 網絡卡裝置以及網絡卡裝置的私有資料結構,即將裝置的共性一層層的抽象,pci 匯流排裝置包含網絡卡裝置,網絡卡裝置又包含其私有資料結構。在 dpdk 中,首先會註冊裝置驅動,然後查詢當前系統有哪些 pci設 備,並通...

linux c自定義網路層協議收發包

本文使用rawsocket收發包,也提供使用libpcap和libnet來收發包的 經過測試發現,libpcap的收包能力比rawsocket強。傳送方發出10000個資料鏈路層幀,使用rawsocket大概能收到2000 3000多個,而使用libpcap抓包能收到5000多個。這大概是因為lib...

Linux網路(網路模型和收發流程)

為了解決網路互聯中異構裝置的相容性問題,並解耦複雜的網路包處理流程,國際標準化組織制定的開放式系統互聯通訊參考模型 open system interconnection reference model 簡稱為 osi 網路模型。osi 模型把網路互聯的框架分為應用層 表示層 會話層 傳輸層 網路層...