Linux內核:sk_buff解析

来源:http://www.cnblogs.com/tzh36/archive/2016/04/23/5424564.html
-Advertisement-
Play Games

sk_buff 目錄 1 sk_buff介紹 2 sk_buff組成 3 struct sk_buff結構體 4 sk_buff成員變數 4.1 Layout佈局 4.2 General通用 4.3 Feature-specific功能相關 5 sk_buff管理和操作函數 5.1緩衝區操作函數sk... ...


sk_buff

目錄

1 sk_buff介紹

2 sk_buff組成

3 struct sk_buff 結構體

4 sk_buff成員變數

4.1 Layout佈局

4.2 General通用

4.3 Feature-specific功能相關

5 sk_buff管理和操作函數

5.1緩衝區操作函數skb_reserve  skb_put  skb_push  skb_pull

5.2發送tcp報文示例

5.3 緩衝區分配、克隆和釋放函數alloc_skb  skb_clone  pskb_copy  skb_copy  kfree_skb

   

1 sk_buff介紹

sk_buffsocket buffer)結構是linux網路代碼中重要的數據結構,它管理和控制接收或發送數據包的信息。

2 sk_buff組成

Packet data:通過網卡收發的報文,包括鏈路層、網路層、傳輸層的協議頭和攜帶的應用數據,包括head room,data,tail room三部分。

skb_shared_info 作為packet data的補充,用於存儲ip分片,其中sk_buff *frag_list是一系列子skbuff鏈表,而frag[]是由一組單獨的page組成的數據緩衝區。

Data buffer:用於存儲packet data的緩衝區,分為以上兩部分。

Sk_buff:緩衝區控制結構sk_buff

整個sk_buff結構圖如圖1

1 sk_buff結構圖

3 struct sk_buff 結構體

/* struct sk_buff - socket buffer */ 

struct sk_buff {

/* These two members must be first. */

struct sk_buff         *next;

struct sk_buff         *prev;

   

struct sock             *sk;

struct skb_timeval  tstamp; /* Time we arrived,記錄接收或發送報文的時間戳*/

struct net_device    *dev; /*通過該設備接收或發送,記錄網路介面的信息和完成操作

struct net_device    *input_dev; /*接收數據的網路設備

struct net_device    *curlayer_input_dev;

struct net_device    *l2tp_input_dev;

union {

struct tcphdr   *th;

struct udphdr  *uh;

struct icmphdr*icmph;

struct igmphdr       *igmph;

struct iphdr     *ipiph;

struct ipv6hdr*ipv6h;

unsigned char  *raw;

} h; //傳輸層報頭

union {

struct iphdr     *iph;

struct ipv6hdr*ipv6h;

struct arphdr   *arph;

unsigned char  *raw;

} nh; //網路層報頭

union {

unsigned char        *raw;

} mac; //鏈路層報頭

.

.

.

unsigned int           len, //len緩衝區中數據部分的長度。

data_len, //data_len只計算分片中數據的長度

mac_len, //mac頭的長度

csum; //校驗和

__u32            priority;

__u8              local_df:1,

cloned:1, //表示該結構是另一個sk_buff克隆的

ip_summed:2,

nohdr:1,

nfctinfo:3;

__u8              pkt_type:3,

fclone:2,

ipvs_property:1;

__be16                  protocol;

__u32 flag; /*packet flags*/

.

.

.

/* These elements must be at the end, see alloc_skb() for details.  */

unsigned int           truesize; //這是緩衝區的總長度,包括sk_buff結構和數據部分

atomic_t         users;

unsigned char         *head, //指向緩衝區的頭部

*data,// 指向實際數據的頭部

*tail, //指向實際數據的尾部

*end;//指向緩衝區的尾部

};

4 sk_buff成員變數

Sk_buff成員變數主要包括以下3

1 Layout佈局

2 General通用

3 Feature-specific功能相關

4.1 Layout佈局

1 struct sk_buff *next, struct sk_buff *prev

有些sk_buff成員變數的作用是方便查找,或者是連接數據結構本身內核可以把sk_buff組織成一個雙向鏈表。當然,這個鏈表的結構要比常見的雙向鏈表的結構複雜一點。就像任何一個雙向鏈表一樣,sk_buff中有兩個指針nextprev,其中,next指向下一個節點,而prev指向上一個節點。但是,這個鏈表還有另一個需求:每個sk_buff結構都必須能夠很快找到鏈表頭節點。為了滿足這個需求,在第一個節點前面會插入另一個結構sk_buff_head,這是一個輔助節點,它的定義如下

sk_buffsk_buff_head的前兩個元素是一樣的:nextprev指針。這使得它們可以放到同一個鏈表中,儘管sk_buff_head要比sk_buff小得多。另外,相同的函數可以同樣應用於sk_buffsk_buff_head

2

2 struct sock *sk

這是一個指向擁有這個sk_buffsock結構的指針。這個指針在網路包由本機發出或者由本機進程接收時有效,因為插口相關的信息被L4TCPUDP)或者用戶空間程式使用。如果sk_buff只在轉發中使用(這意味著,源地址和目的地址都不是本機地址),這個指針是NULL

3 unsigned int len

這是緩衝區中數據部分的長度。它包括主緩衝區中的數據長度(data指針指向它)和分片中的數據長度。它的值在緩衝區從一個層向另一個層傳遞時改變,因為往上層傳遞,舊的頭部就沒有用了,而往下層傳遞,需要添加本層的頭部。len同樣包含了協議頭的長度。

4 unsigned int data_len

len不同,data_len只計算分片中數據的長度。

5 unsigned int mac_len

這是mac頭的長度。

6 atomic_t users

這是一個引用計數,用於計算有多少實體引用了這個sk_buff緩衝區。它的主要用途是防止釋放sk_buff後,還有其他實體引用這個sk_buff。因此,每個引用這個緩衝區的實體都必須在適當的時候增加或減小這個變數。這個計數器只保護sk_buff結構本身,而緩衝區的數據部分由類似的計數器(dataref)來保護。有時可以用atomic_incatomic_dec函數來直接增加或減小users,但是,通常還是使用函數skb_getkfree_skb來操作這個變數。

7 unsigned int truesize

這是緩衝區的總長度,包括sk_buff結構和數據部分。如果申請一個len位元組的緩衝區,alloc_skb函數會把它初始化成len+sizeof(sk_buff)

8 unsigned char *head *end *data *tail

它們表示緩衝區和數據部分的邊界。在每一層申請緩衝區時,它會分配比協議頭或協議數據大的空間。headend指向緩衝區的頭部和尾部,而datatail指向實際數據的頭部和尾部,參見圖3。每一層會在headdata之間填充協議頭,或者在tailend之間添加新的協議數據。圖3中右邊數據部分會在尾部包含一個附加的頭部。

3

9 void (*destructor)(...)

這個函數指針可以初始化成一個在緩衝區釋放時完成某些動作的函數。如果緩衝區不屬於一個socket,這個函數指針通常是不會被賦值的。如果緩衝區屬於一個socket,這個函數指針會被賦值為sock_rfreesock_wfree(分別由skb_set_owner_rskb_set_owner_w函數初始化)。這兩個sock_xxx函數用於更新socket隊列中的記憶體容量。

4.2 General通用

本節描述sk_buff的主要成員變數,這些成員變數與特定的內核功能無關。

1struct timeval tstamp

這個變數只對接收到的包有意義。它代表包接收時的時間戳,或者有時代表包准備發出時的時間戳。它在netif_rx裡面由函數net_timestamp設置,而netif_rx是設備驅動收到一個包後調用的函數。

2  struct net_device *dev

這個變數的類型是net_devicenet_device它代表一個網路設備。dev的作用與這個包是準備發出的包,還是剛接收的包有關。當收到一個包時,設備驅動會把sk_buffdev指針指向收到這個包的設備的數據結構,就像下麵的vortex_rx里的一段代碼所做的一樣,這個函數屬於3c59x系列乙太網卡驅動,用於接收一個幀。(drivers/net/3c59x.c)

當一個包被髮送時,這個變數代表將要發送這個包的設備。在發送網路包時設置這個值的代碼要比接收網路包時設置這個值的代碼複雜。有些網路功能可以把多個網路設備組成一個虛擬的網路設備(也就是說,這些設備沒有和物理設備直接關聯),並由一個虛擬網路設備驅動管理。當虛擬設備被使用時,dev指針指向虛擬設備的net_device結構。而虛擬設備驅動會在一組設備中選擇一個設備並把dev指針修改為這個設備的net_device結構。因此,在某些情況下,指向傳輸設備的指針會在包處理過程中被改變。

3 struct net_device *input_dev

這是收到包的網路設備的指針。如果包是本地生成的,這個值為NULL。對乙太網設備來說,這個值由eth_type_trans初始化,它主要被流量控制代碼使用。

4 struct net_device *real_dev

這個變數只對虛擬設備有意義,它代表與虛擬設備關聯的真實設備。例如,BondingVLAN設備都使用它來指向收到包的真實設備。

5 union {...} h  union {...} nh  union {...} mac

這些是指向TCP/IP各層協議頭的指針:h指向L4nh指向L3mac指向L2。每個指針的類型都是一個聯合,包含多個數據結構,每一個數據結構都表示內核在這一層可以解析的協議。例如,h是一個包含內核所能解析的L4協議的數據結構的聯合。每一個聯合都有一個raw變數用於初始化,後續的訪問都是通過協議相關的變數進行的。

當接收一個包時,處理n層協議頭的函數從n-1層收到一個緩衝區,它的skb->data指向n層協議的頭。處理n層協議的函數把本層的指針(例如,L3對應的是skb->nh指針)初始化為skb->data,因為這個指針的值會在處理下一層協議時改變(skb->data將被初始化成緩衝區里的其他地址)。在處理n層協議的函數結束時,在把包傳遞給n+1層的處理函數前,它會把skb->data指針指向n層協議頭的末尾,這正好是n+1層協議的協議頭(參見圖4)

發送包的過程與此相反,但是由於要為每一層添加新的協議頭,這個過程要比接收包的過程複雜。

4

6 struct dst_entry dst  這個變數在路由子系統中使用。

7 char cb[40]

這是一個控制緩存,或者說是一個私有信息的存儲空間,由每一層自己維護並使用。它在分配sk_buff結構時分配(它目前的大小是40位元組,已經足夠為每一層存儲必要的私有信息了)。在每一層中,訪問這個變數的代碼通常用巨集實現,以增強代碼的可讀性。例如,TCP用這個變數存儲tcp_skb_cb結構,這個結構在include/net/tcp.h中定義:

下麵這個巨集被TCP代碼用來訪問cb變數。在這個巨集裡面,有一個簡單的類型轉換:

#define TCP_SKB_CB(__skb)      ((struct tcp_skb_cb *)&((__skb)->cb[0]))

下麵的例子是TCP子系統在收到一個分段時填充相關數據結構的代碼:

int tcp_v4_rcv(struct sk_buff *skb)

8 unsigned int csum  unsigned char ip_summed

表示校驗和以及相關狀態標記。

unsigned char cloned

一個布爾標記,當被設置時,表示這個結構是另一個sk_buff的克隆。

9 unsigned char pkt_type

這個變數表示幀的類型,分類是由L2的目的地址來決定的。可能的取值都在include/linux/if_packet.h中定義。對乙太網設備來說,這個變數由eth_type_trans函數初始化。

10 __u32 priority

這個變數描述發送或轉發包的QoS類別。如果包是本地生成的,socket層會設置priority變數。如果包是將要被轉發的,rt_tos2priority函數會根據ip頭中的Tos域來計算賦給這個變數的值。這個變數的值與DSCP(DiffServ CodePoint)沒有任何關係。

unsigned short protocol

這個變數是高層協議從二層設備的角度所看到的協議。典型的協議包括IPIPV6ARP。完整的列表在 include/linux/if_ether.h中。由於每個協議都有自己的協議處理函數來處理接收到的包,因此,這個域被設備驅動用於通知上層調用哪個協議處理函數。每個網路驅動都調用netif_rx來通知上層網路協議的協議處理函數,因此protocol變數必須在這些協議處理函數調用之前初始化。

unsigned short security

這是包的安全級別。這個變數最初由IPSec子系統使用,但現在已經作廢了。

   

4.3 Feature-specific功能相關

linux內核是模塊化的,你可以選擇包含或者刪除某些功能。因此,sk_buff結構裡面的一些成員變數只有在內核選擇支持某些功能時才有效,比如防火牆(netfilter)或者qos

1 unsigned long nfmark  __u32 nfcache  __u32 nfctinfo  struct nf_conntrack *nfct

unsigned int nfdebug   struct nf_bridge_info *nf_bridge

這些變數被netfilter使用(防火牆代碼),內核編譯選項是"Device Drivers->Networking support-> Networking options-> Network packet filtering"和兩個子選項"Network packet filtering debugging"和"Bridged IP/ARP packets filtering"

2 union {...} private

這個聯合結構被高性能並行介面(HIPPI)使用。相應的內核編譯選項是"Device->Drivers ->Networking support ->Network device support ->HIPPI driver support"

3 __u32 tc_index  __u32 tc_verd  __u32 tc_classid

這些變數被流量控制代碼使用,內核編譯選項是"Device Drivers ->Networking->support ->Networking options ->QoS and/or fair queueing"和它的子選項"Packetclassifier API"

4 struct sec_path *sp

這個變數被IPSec協議用於跟蹤傳輸的信息。

5 sk_buff管理和操作函數

5.1緩衝區操作函數

有很多函數,通常都比較短小而且簡單,內核用這些函數操作sk_buff的成員變數或者sk_buff鏈表。首先來看分配和釋放緩衝區的函數,然後是一些通過移動指針在緩衝區的頭部或尾部預留空間的函數。如果你看過include/linux/skbuff.hnet/core/skbuff.c中的函數,你會發現,基本上每個函數都有兩個版本,名字分別是do_something__do_something。通常第一種函數是一個包裝函數,它會在第二種函數的基礎上增加合法性檢查或者鎖。一般來說,類似__do_something的函數不能被直接調用(除非滿足特定的條件,比如說鎖)。那些違反這條規則而直接引用這些函數的不良代碼會最終被更正。

各操作函數緩衝區與移動指針變化如圖5所示。

操作前與操作後指針變化圖: (a)skb_put, (b)skb_push, (c)skb_pull, and (d)skb_reserve

1 unsigned char *skb_put(struct sk_buff *skb, unsigned int len)

在緩衝區的尾部空間擴充len位元組數據區l,將tail指針下移,並增加skblen值。datatail之間的空間就是可以存放網路報文的空間。這個操作增加了可以存儲網路報文的空間,但是增加不能使 tail的值大於end的值,skblen值大於truesize 的值。

2 unsigned char *skb_push(struct sk_buff *skb, unsigned int len)

在緩衝區的頭部空間擴充len位元組的數據區。將data指針上移,並增加skblen值。這個操作在存儲空間的頭部增加了一段可以存儲網路報文的空間,但是增加不能使data的值小於 head的值,skblen值大於truesize的值。

3 unsigned char * skb_pull(struct sk_buff *skb, unsigned int len)

從緩衝區的數據區刪除len位元組,把騰出的記憶體歸還給頭部空間。將data指針下移,並減小skblen值。這個操作使data指針指向下一層網路報文的頭部。

4 void skb_reserve(struct sk_buff *skb, unsigned int len)

從空白緩衝區中分配len位元組的數據區,通過減少尾部空間,增加一個空&sk_buff的首部空間,將data指針和tail指針同時下移。這個操作在存儲空間的頭部預留len長度的空隙。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • HTTP原理 1. 什麼是URL 在介紹HTTP之前,我們對URL有一定的瞭解的,因為只有通過URL我們才能拿到網路上的資源。那麼究竟什麼是URL? URL(Uniform Resource Locator 統一資源定位符):URL其實就是資源在互聯網上的地址、位置,互聯網上的每一個資源都有一個唯一 ...
  • <TextView android:id="@+id/desc" android:layout_width="match_parent" android:layout_height="130px" android:lineSpacingExtra="2px" android:textColor="@ ...
  • 1、表的修改: 1.1 刪除列:ALTER TABLE 表名 DROP COLUMN 列名 1.2 添加列:ALTER TABLE 表名 ADD(列名 數據類型) 1.3 修改列名:ALTER TABLE 表名 RENAME COLUMN 舊列名 TO 新列名 1.4 修改列的數據類型:ALTER ...
  • oracle_11g有32位和64位兩個版本,64位的電腦可以裝32位的版本,但是32位的電腦不可以裝64位,如果你是64位的,建議你裝64位的版本。 安裝包下載 32位 http://download.oracle.com/otn/nt/oracle11g/112010/win32_11gR2 ...
  • 1.慢SQL消耗了70%~90%的資料庫CPU資源; 2.SQL語句獨立於程式設計邏輯,相對於對程式源代碼的優化,對SQL語句的優化在時間成本和風險上的代價都很低; 3.SQL語句可以有不同的寫法; 下麵是我總結的一些SQL常見的優化方法,每個案例都簡單易懂,在開發過程中可以作為參考: 不使用子查詢 ...
  • 前提: 搭建好集群環境(zookeeper、hadoop、hbase)。 搭建方法這裡就不進行介紹了,網上有很多博客在介紹這些。 簡單需求: WordCount單詞計數,號稱Hadoop的HelloWorld。所以,我打算通過這個來初體驗一下Hadoop。需求如下: ①、計算文件中出現每個單詞的頻數 ...
  • IK分詞器地址:https://github.com/medcl/elasticsearch-analysis-ik 安裝好ES之後就可以安裝分詞器插件了 記住選擇ES對應的版本 對應的有版本選擇下載: 下載之後了,解壓之後,使用maven,mvn clean package 打包 找到目錄/ela ...
  • OS:CentOS6.3 ant版本:apache-ant-1.9.2-bin 第1步:下載ant apache-ant-1.9.2-bin.tar.gz 第2步:解壓 tar -zxvf apache-ant-1.9.2-bin.tar.gz 第3步:將ant複製或移動到usr目錄 mv apac ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...