linux接收网络数据并存存储,linux网络数据包数据结构 Socket Buffer
Linux網(wǎng)絡(luò)核心數(shù)據(jù)結(jié)構(gòu)是套接字緩存(socket buffer),簡(jiǎn)稱skb。它代表一個(gè)要發(fā)送或處理的報(bào)文,并貫穿于整個(gè)協(xié)議棧。1、套接字緩存skb由兩部分組成:(1)報(bào)文數(shù)據(jù):它保存了實(shí)際在網(wǎng)絡(luò)中傳輸?shù)臄?shù)據(jù);(2)管理數(shù)據(jù):供內(nèi)核處理報(bào)文的額外數(shù)據(jù),這些數(shù)據(jù)構(gòu)成了協(xié)議之間交換的控制信息。發(fā)送網(wǎng)絡(luò)數(shù)據(jù)包:當(dāng)應(yīng)用程序向一個(gè)socket傳輸數(shù)據(jù)之后,該socket將創(chuàng)建相應(yīng)的套接字緩存,并將用戶數(shù)據(jù)拷貝到緩存中。當(dāng)報(bào)文在各協(xié)議層傳達(dá)輸?shù)倪^程中,每一導(dǎo)的報(bào)文頭將插入到用戶數(shù)據(jù)之前。相應(yīng)的,在sk_buff結(jié)構(gòu)中描述協(xié)議頭信息的地址指針會(huì)被賦值。
接收網(wǎng)絡(luò)數(shù)據(jù)包:網(wǎng)絡(luò)適配器接收到發(fā)送給本機(jī)的網(wǎng)絡(luò)數(shù)據(jù)后,首先產(chǎn)生中斷通知內(nèi)核收到網(wǎng)絡(luò)數(shù)據(jù)幀。接著在網(wǎng)絡(luò)適配器的中斷處理程序中調(diào)用dev_alloc_skb函數(shù)向系統(tǒng)申請(qǐng)一個(gè)Socket Buffer,將接收到的網(wǎng)絡(luò)數(shù)據(jù)幀從設(shè)備硬件的緩沖區(qū)復(fù)制到Socket Buffer的數(shù)據(jù)包緩沖區(qū);填寫sk_buff結(jié)構(gòu)中的地址、接收時(shí)間和協(xié)議等。
skb為報(bào)文頭申請(qǐng)了足夠的空間,所以避免了由于插入報(bào)文頭而對(duì)報(bào)文進(jìn)行多次拷貝。用戶數(shù)據(jù)只拷貝了兩次:一是從用戶空間拷貝到內(nèi)核;二是報(bào)文數(shù)據(jù)從內(nèi)核傳送到網(wǎng)絡(luò)適配器。
2.Sk_buff數(shù)據(jù)域分類
@結(jié)構(gòu)管理
@常規(guī)數(shù)據(jù)域
@網(wǎng)絡(luò)功能配置相關(guān)域
2.1.1 sk_buff的結(jié)構(gòu)管理域
1.*next和*prev
sk_buff是一個(gè)復(fù)雜的雙向鏈表,在他結(jié)構(gòu)中有next和prev指針,分別指向鏈表的下一個(gè)節(jié)點(diǎn)和前一個(gè)節(jié)點(diǎn)。并且為了某些需求(不知道是哪些目前)需要很快定位到鏈表頭部,所以還有一個(gè)指向鏈表頭部的指針list(我在2.6.25內(nèi)核沒有發(fā)現(xiàn)這個(gè)指針)。
sk_buff_head結(jié)構(gòu)是:
struct sk_buff_head {
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev;
__u32 qlen; //代表元素節(jié)點(diǎn)數(shù)目
spinlock_t lock; //加鎖,防止對(duì)表的并發(fā)訪問
};
2.struct sock *sk
這個(gè)指針指向一個(gè)套接字sock數(shù)據(jù)結(jié)構(gòu)。當(dāng)數(shù)據(jù)在本地產(chǎn)生或者本地進(jìn)程接受時(shí),需要這個(gè)指針;里面的數(shù)據(jù)會(huì)有tcp/udp和用戶態(tài)程序使用。如果是轉(zhuǎn)發(fā)此指針為NULL
3.unsigned int len
緩沖區(qū)中數(shù)據(jù)塊大小。長(zhǎng)度包括:主要緩沖區(qū)(head所指)的數(shù)據(jù)以及一些片斷(fragment)的數(shù)據(jù)。當(dāng)包在協(xié)議棧向上或向下走時(shí),其大小會(huì)變,因?yàn)橛蓄^部的丟棄和添加。
unsigned int data_len
片段中數(shù)據(jù)大小
unsigned int mac_len
mac包頭大小
atomic_t users
引用計(jì)數(shù),使用這個(gè)sk_buff的使用者的數(shù)目,可能有多個(gè)函數(shù)要使用同一個(gè)sk_buff所以防止提前釋放掉,設(shè)置此計(jì)數(shù)
unsigned int truesize
此緩沖區(qū)總大小,包括sk_buff。sk_buff只不過是個(gè)指針的集合,他所指的才是真正的數(shù)據(jù)區(qū),所以是兩部分(sk_buff數(shù)據(jù)結(jié)構(gòu)的長(zhǎng)度和數(shù)據(jù)包的長(zhǎng)度和)。(見下圖)
sk_buff_data_t tail;
sk_buff_data_t end;
unsigned char *head, *data;
這些指針很重要,他們指向的是真正的數(shù)據(jù)區(qū),他們的邊界。head和end指向的是數(shù)據(jù)區(qū)的開端和尾端,指向整個(gè)數(shù)據(jù)包緩沖區(qū)的起始和結(jié)束地址,data和tail指向的是實(shí)際數(shù)據(jù)的開頭和結(jié)尾。
因?yàn)閿?shù)據(jù)區(qū)在協(xié)議棧走的時(shí)候要一層層添加或去掉一些數(shù)據(jù)(比如報(bào)頭)所以申請(qǐng)一塊大的足夠的內(nèi)存,然后在往里放東西。真實(shí)的實(shí)際數(shù)據(jù)可能用不了這么多,所以用data,tail指向真實(shí)的,head,tail指向邊界。剛開始沒填充數(shù)據(jù)時(shí)前三個(gè)指針指向的是一個(gè)地方。
void (*destructor) (…….)
此函數(shù)指針被初始化一個(gè)函數(shù),當(dāng)此緩沖區(qū)刪除時(shí),完成某些工作。
2.1.2常規(guī)數(shù)據(jù)域
1.struct ktime_t tstamp
描述接收數(shù)據(jù)包到達(dá)內(nèi)核的時(shí)間。網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序收到網(wǎng)絡(luò)數(shù)據(jù)時(shí)調(diào)用接收數(shù)據(jù)包處理函數(shù)netif_rx,再調(diào)用net_timestamp(skb)對(duì)該數(shù)據(jù)域賦值。
時(shí)間戳,表示何時(shí)被接受或有時(shí)表示包預(yù)定的傳輸時(shí)間
2.struct net_device *dev
dev是指向代表設(shè)備數(shù)據(jù)結(jié)構(gòu)的指針,表明該數(shù)據(jù)包是通過那個(gè)網(wǎng)絡(luò)設(shè)備接收或傳送的。
3.
sk_buff_data_t transport_header; //L4傳輸層協(xié)議頭在網(wǎng)絡(luò)數(shù)據(jù)包中的地址;
sk_buff_data_t network_header; //L3網(wǎng)絡(luò)層協(xié)議頭在網(wǎng)絡(luò)數(shù)據(jù)包中的地址;
sk_buff_data_t mac_header; //L2數(shù)據(jù)鏈路層協(xié)議頭在網(wǎng)絡(luò)數(shù)據(jù)包中的地址;
如果上述的協(xié)議頭在網(wǎng)絡(luò)數(shù)據(jù)包中的地址以skb->head為起始的偏移量(64位的CPU體系結(jié)構(gòu)),否則sk_buff_data_t的類型為指針(32位的CPU體系結(jié)構(gòu)),存放各協(xié)議層在網(wǎng)絡(luò)數(shù)據(jù)包中的起始地址。
這些指針分別指向報(bào)文頭部,和2.4版本比較有了變化,不再是聯(lián)合體,使用更加方便了,Linux給出了很方便的函數(shù)直接定位到各層的頭部。下圖是2.4版本的,只是說明一下。
4 union
{
.? structdst_entry *dst;
Structrtable?? *rtable;
};
路由子系統(tǒng)使用。該數(shù)據(jù)域指明如何將數(shù)據(jù)包向前發(fā)送。其中存放了數(shù)據(jù)包的發(fā)送目標(biāo)IP地址等。
5.char cb[48]
緩沖控制區(qū),用來存儲(chǔ)私有信息的空間。比如tcp/udp用這個(gè)空間存儲(chǔ)一個(gè)結(jié)構(gòu)體tcp_skb_cb/udp_skb_cp ,可以用宏TCP_SKB_CB(__skb)/UDP_SKB_CB(_skb)定位到他,然后使用里面的變量。
6.csum
_u8 Ip_summed
Cusm域存放數(shù)據(jù)包的校驗(yàn)和,檢驗(yàn)數(shù)據(jù)包在接收或發(fā)送過程中是否有損壞。其中必須包含csum_start/csum_offset對(duì)。
csum_start:以skb_head為起始地址的偏移量。指出檢驗(yàn)和從數(shù)據(jù)的什么位置開始計(jì)算。
csum_offset:以csum_start為起始地址的偏移量,指明檢驗(yàn)和存放的位置。
Ip_summed 描述網(wǎng)絡(luò)設(shè)備是否可用硬件對(duì)IP數(shù)據(jù)進(jìn)行檢驗(yàn)編碼或解碼。
7.unsigned char pkt_type
數(shù)據(jù)包的類型根據(jù)L2層幀的目的地址進(jìn)行類型劃分。
8.fclone:sk_buff克隆類別。
9.__u32 priority; priority數(shù)據(jù)域?qū)崿F(xiàn)質(zhì)量服務(wù)QoS功能特性。
10.QoS等級(jí):描述數(shù)據(jù)包傳送的優(yōu)先級(jí)別。
11.__be16 protocol;
接收數(shù)據(jù)包的網(wǎng)絡(luò)層協(xié)議,表明網(wǎng)絡(luò)數(shù)據(jù)包應(yīng)該傳給TCP/IP協(xié)議棧網(wǎng)絡(luò)層的哪個(gè)協(xié)議處理函數(shù)。(include/linux/if_ether.h)
12._u16 queue_mapping:描述發(fā)送網(wǎng)絡(luò)數(shù)據(jù)包的所在隊(duì)列與設(shè)備硬件發(fā)送隊(duì)列的映射關(guān)系。
13._32 mark:數(shù)據(jù)包為常規(guī)數(shù)據(jù)包的標(biāo)志。
14._u16 vlan_tci:虛擬局域網(wǎng)的標(biāo)記控制信息。、
15.struct sec_path *sp:IPsec協(xié)議跟蹤網(wǎng)絡(luò)數(shù)據(jù)包的傳送路徑。
2.1.3 Sk_buff的網(wǎng)絡(luò)功能配置域
操作sk_buff相關(guān)的函數(shù)(net/core/skbuff.c和incldue/linux/skbuff.h)與sk_buff相關(guān)的函數(shù)涉及到網(wǎng)絡(luò)報(bào)文存儲(chǔ)結(jié)構(gòu)和控制結(jié)構(gòu)的分配、復(fù)制、釋放,以及控制結(jié)構(gòu)里的各指針的操作,還有各種標(biāo)志的檢查。重要的函數(shù)說明如下:內(nèi)核在系統(tǒng)初始化時(shí)創(chuàng)建兩個(gè)sk_buff的內(nèi)存對(duì)象池。Skbuff_head_cache和skbuff_fclone_cache。1.(1)_alloc_skb(unsigned int size,int gfp_t gfp_mask,int fclone,int node)(skbuff.c)分配大小為size的存儲(chǔ)空間存放網(wǎng)絡(luò)報(bào)文,同時(shí)分配它的控制結(jié)構(gòu)。size的值是16字節(jié)對(duì)齊的,gfp_mask是內(nèi)存分配的優(yōu)先級(jí)。常見的內(nèi)存分配優(yōu)先級(jí)有GFP_ATOMIC,代表分配過程不能被中斷,一般用于中斷上下文中分配內(nèi)存;GFP_KERNEL,代表分配過程可以被中斷,相應(yīng)的分配請(qǐng)求被放到等待隊(duì)列中。fclone為1,表示從內(nèi)存對(duì)象池skbuff_fclone_cache中獲取sk_buff數(shù)據(jù)結(jié)構(gòu)所需的內(nèi)存空間,為0,則從內(nèi)存對(duì)象池Skbuff_head_cache。
alloc_skb(unsigned int size,gfp_t priority)和alloc_skb_fclone(unsigned int size,gfp_t priority)是_alloc_skb的包裝函數(shù),前者fclone=0,后者fclone=1
(2)dev_alloc_skb(unsigned int lenth)(skbuff.c)
_dev_alloc_skb(unsigned int lenth,gfp_t gfp_mask)
_dev_alloc_skb是給網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序使用的函數(shù),當(dāng)網(wǎng)絡(luò)設(shè)備從網(wǎng)絡(luò)上收到一個(gè)數(shù)據(jù)包時(shí),它調(diào)用該函數(shù)向系統(tǒng)申請(qǐng)緩沖區(qū)來存放數(shù)據(jù)包,調(diào)用alloc_skb來分配SocketBuffer。dev_alloc_skb是_dev_alloc_skb的包裝函數(shù)。由于網(wǎng)絡(luò)數(shù)據(jù)包是中斷處理程序中接收的,中斷不能休眠,dev_alloc_skb用GFP_ATOMIC標(biāo)志傳給_dev_alloc_skb來分配內(nèi)存。
(3)netdev_alloc_skb(struct net_decice *dev,unsigned int lenth)
_netdev_alloc_skb(struct net_decice *dev,unsigned int lenth,gfp_tgfp_mask)
_netdev_alloc_skb與_dev_alloc_skb類似,指定了接收數(shù)據(jù)包的網(wǎng)絡(luò)設(shè)備用_alloc_skb來分配Socket Buffer,返回分配的sk_buff前,初始化sk_buff的設(shè)備指針*dev域。同上面一樣,netdev_alloc_skb是包裝函數(shù)。2.struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask)
當(dāng)同一個(gè)scoket buffer由不同的進(jìn)程獨(dú)立處理,但進(jìn)程只操作sk_buff數(shù)據(jù)結(jié)構(gòu)描述符,內(nèi)核只對(duì)sk_buff數(shù)據(jù)結(jié)構(gòu)做完全復(fù)制。從控制結(jié)構(gòu)skb中clone出一個(gè)新的控制結(jié)構(gòu),它們都指向同一個(gè)網(wǎng)絡(luò)報(bào)文。clone成功之后,將新的控制結(jié)構(gòu)和原來的控制結(jié)構(gòu)的is_clone,cloned兩個(gè)標(biāo)記都置位。同時(shí)還增加網(wǎng)絡(luò)報(bào)文的引用計(jì)數(shù)(這個(gè)引用計(jì)數(shù)存放在存儲(chǔ)空間的結(jié)束地址的內(nèi)存中,由函數(shù)atomic_t *skb_datarefp(struct sk_buff *skb)訪問,引用計(jì)數(shù)記錄了這個(gè)存儲(chǔ)空間有多少個(gè)控制結(jié)構(gòu))。由于存在多個(gè)控制結(jié)構(gòu)指向同一個(gè)存儲(chǔ)空間的情況,所以在修改存儲(chǔ)空間里面的內(nèi)容時(shí),先要確定這個(gè)存儲(chǔ)空間的引用計(jì)數(shù)為1,或者用下面的拷貝函數(shù)復(fù)制一個(gè)新的存儲(chǔ)空間,然后才可以修改它里面的內(nèi)容。
克隆的sk_buff具有以下特點(diǎn):不放入任何的sk_buff的管理隊(duì)列,不屬于任何套接字,兩個(gè)sk_buff結(jié)構(gòu)的skb->cloned域都置為1,當(dāng)一個(gè)sk_buff被克隆后,它的數(shù)據(jù)包的值為只讀。
3. 多個(gè)進(jìn)程對(duì)同一個(gè)scoket buffer修改sk_buff數(shù)據(jù)結(jié)構(gòu)中的內(nèi)容,也要修改數(shù)據(jù)包內(nèi)容,對(duì)scoket buffer進(jìn)行復(fù)制。struct sk_buff *skb_copy(struct sk_buff *skb, int gfp_mask)
既修改主數(shù)據(jù)包的內(nèi)容,又要操作分片數(shù)據(jù)的值。
struct sk_buff *pskb_copy(struct sk_buff *skb, int gfp_mask)
只修改主數(shù)據(jù)包的內(nèi)容復(fù)制控制結(jié)構(gòu)skb和它所指的存儲(chǔ)空間的內(nèi)容。復(fù)制成功之后,新的控制結(jié)構(gòu)和存儲(chǔ)空間與原來的控制結(jié)構(gòu)和存儲(chǔ)空間相對(duì)獨(dú)立。所以新的控制結(jié)構(gòu)里的is_clone,cloned兩個(gè)標(biāo)記都是0,而且新的存儲(chǔ)空間的引用計(jì)數(shù)是1。4.void kfree_skb(struct sk_buff *skb)釋放控制結(jié)構(gòu)skb和它所指的存儲(chǔ)空間。由于一個(gè)存儲(chǔ)空間可以有多個(gè)控制結(jié)構(gòu),所以只有在存儲(chǔ)空間的引用計(jì)數(shù)為1的情況下才釋放存儲(chǔ)空間,一般情況下,只釋放控制結(jié)構(gòu)skb。
Kfree_release_all:釋放與sk_buff相連的其他數(shù)據(jù)結(jié)構(gòu)的引用計(jì)數(shù)。
Kfree_release_data:釋放數(shù)據(jù)包的主緩沖區(qū)和數(shù)據(jù)片緩沖區(qū)。kfree_skbmem:將sk_buff數(shù)據(jù)結(jié)構(gòu)返回內(nèi)存對(duì)象池。
dst_release:釋放對(duì)路由表的引用。5.unsigned char *skb_put(struct sk_buff *skb, unsigned int len)將tail指針下移,并增加skb的len值。data和tail之間的空間就是可以存放網(wǎng)絡(luò)報(bào)文的空間。這個(gè)操作增加了可以存儲(chǔ)網(wǎng)絡(luò)報(bào)文的空間,但是增加不能使tail的值大于end的值,skb的len值大于truesize的值。unsigned char *skb_push(struct sk_buff *skb, unsigned int len)將data指針上移,并增加skb的len值。這個(gè)操作在存儲(chǔ)空間的頭部增加了一段可以存儲(chǔ)網(wǎng)絡(luò)報(bào)文的空間,上一個(gè)操作在存儲(chǔ)空間的尾部增加了一段可以存儲(chǔ)網(wǎng)絡(luò)報(bào)文的空間。但是增加不能使data的值小于head的值,skb的len值大于truesize的值。unsigned char * skb_pull(struct sk_buff *skb, unsigned int len)將data指針下移,并減小skb的len值。這個(gè)操作使data指針指向下一層網(wǎng)絡(luò)報(bào)文的頭部。void skb_reserve(struct sk_buff *skb, unsigned int len)將data指針和tail指針同時(shí)下移。這個(gè)操作在存儲(chǔ)空間的頭部預(yù)留len長(zhǎng)度的空隙。void skb_trim(struct sk_buff *skb, unsigned int len)將網(wǎng)絡(luò)報(bào)文的長(zhǎng)度縮減到len。這個(gè)操作丟棄了網(wǎng)絡(luò)報(bào)文尾部的填充值。6.int skb_cloned(struct sk_buff *skb)判斷skb是否是一個(gè)clone的控制結(jié)構(gòu)。如果是clone的,它的cloned標(biāo)記是1,而且它指向的存儲(chǔ)空間的引用計(jì)數(shù)大于1。7.sk_buff引用的計(jì)數(shù)加1:static inline strcut sk_buff *skb_get(struct sk_buff *skb)
分片數(shù)據(jù)引用計(jì)數(shù)加1:static void skb_clone_fraglist(struct sk_buff *skb)
8.數(shù)據(jù)分片和分段
Strcut skb_shared_info數(shù)據(jù)結(jié)構(gòu)。3、套接字緩存隊(duì)列(Socket-Buffer Queues)
3.1、sk_buff_head在網(wǎng)絡(luò)協(xié)議棧的實(shí)現(xiàn)中,有時(shí)需要把許多網(wǎng)絡(luò)報(bào)文放到一個(gè)隊(duì)列中做異步處理。LINUX 為此定義了相關(guān)的數(shù)據(jù)結(jié)構(gòu)sk_buff_head。這是一個(gè)雙向鏈表的頭,它把sk_buff鏈接成一個(gè)雙向鏈表。
//套接字緩存隊(duì)列頭structsk_buff_head {/* These twomembers must be first. */structsk_buff *next;structsk_buff *prev;
__u32 qlen;//隊(duì)列的長(zhǎng)度,即隊(duì)列中報(bào)文的數(shù)量spinlock_tlock;
};
3.2、與sk_buff_head相關(guān)的函數(shù)void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)將newsk加到鏈表list的頭部。void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)將newsk加到鏈表list的尾部。struct sk_buff *skb_dequeue(struct sk_buff_head *list)從鏈表list的頭部取下一個(gè)sk_buff。struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list)從鏈表list的尾部取下一個(gè)sk_buff。skb_insert(struct sk_buff *old, struct sk_buff *newsk)將newsk加到old所在的鏈表上,并且newsk在old的前面。void skb_append(struct sk_buff *old, struct sk_buff *newsk)將newsk加到old所在的鏈表上,并且newsk在old的后面。void skb_unlink(struct sk_buff *skb)將skb從它所在的鏈表上取下。以上的鏈表操作都是先關(guān)中斷的。這在中斷上下文中是不需要的,所以另外有一套與上面函數(shù)同名但是有前綴“__”的函數(shù)供運(yùn)行在中斷上下文中的函數(shù)調(diào)用。
功能專用字段
Linux是模塊化的,你編譯時(shí)可以帶上特定功能,比如netfilter等,相應(yīng)的字段才會(huì)生效。應(yīng)該是那些預(yù)定義控制的。
管理函數(shù)
下面這個(gè)圖是:(a*)skb_put; (b*) skb_push; (c*)skb_pull (d*) skb_reserve的使用,主要是對(duì)skb_buf所指向的數(shù)據(jù)區(qū)的指針移動(dòng)。(數(shù)據(jù)預(yù)留以及對(duì)齊)
下圖是用skb_reserve函數(shù),把一個(gè)14字節(jié)的ethernet幀拷貝到緩沖區(qū)。skb_reserve(skb, 2), 2表示16字節(jié)對(duì)齊。14+2=16
下圖是穿過協(xié)議棧從tcp層向下到鏈路層的過程
分配內(nèi)存:
alloc_skb 分配緩沖區(qū)和一個(gè)sk_buff結(jié)構(gòu)
dev_alloc_skb 設(shè)備驅(qū)動(dòng)程序使用的緩沖區(qū)分配函數(shù)
釋放內(nèi)存:
kfree_skb 只有skb->users計(jì)數(shù)器為1時(shí)才釋放
dev_kfree_skb
緩沖區(qū)克隆函數(shù) skb_clone
列表管理函數(shù):
skb_queue_head_init
隊(duì)列初始化
skb_queue_head , skb_queue_tail
把一個(gè)緩沖區(qū)添加到隊(duì)列頭或尾
skb_dequeue, skb_dequeue_tail
從頭或尾去掉
skb_queue_purge
把隊(duì)列變空
skb_queue_walk
循環(huán)隊(duì)列每個(gè)元素
內(nèi)核也新增了幾個(gè)函數(shù),來提供獲取這些偏移的接口:
#ifdefNET_SKBUFF_DATA_USES_OFFSET如果使用了offset來表示偏移的話,就是說是一個(gè)相對(duì)偏移的情況:staticinline unsignedchar*skb_transport_header(conststructsk_buff *skb)
{returnskb->head + skb->transport_header;
}staticinline voidskb_reset_transport_header(structsk_buff *skb)
{
skb->transport_header = skb->data - skb->head;
}staticinline voidskb_set_transport_header(structsk_buff *skb,constintoffset)
{
skb_reset_transport_header(skb);
skb->transport_header += offset;
}staticinline unsignedchar*skb_network_header(conststructsk_buff *skb)
{returnskb->head + skb->network_header;
}staticinline voidskb_reset_network_header(structsk_buff *skb)
{
skb->network_header = skb->data - skb->head;
}staticinline voidskb_set_network_header(structsk_buff *skb,constintoffset)
{
skb_reset_network_header(skb);
skb->network_header += offset;
}staticinline unsignedchar*skb_mac_header(conststructsk_buff *skb)
{returnskb->head + skb->mac_header;
}staticinline intskb_mac_header_was_set(conststructsk_buff *skb)
{returnskb->mac_header != ~0U;
}staticinline voidskb_reset_mac_header(structsk_buff *skb)
{
skb->mac_header = skb->data - skb->head;
}staticinline voidskb_set_mac_header(structsk_buff *skb,constintoffset)
{
skb_reset_mac_header(skb);
skb->mac_header += offset;
}#else/* NET_SKBUFF_DATA_USES_OFFSET */不使用相對(duì)偏移的情況staticinline unsignedchar*skb_transport_header(conststructsk_buff *skb)
{returnskb->transport_header;
}staticinline voidskb_reset_transport_header(structsk_buff *skb)
{
skb->transport_header = skb->data;
}staticinline voidskb_set_transport_header(structsk_buff *skb,constintoffset)
{
skb->transport_header = skb->data + offset;
}staticinline unsignedchar*skb_network_header(conststructsk_buff *skb)
{returnskb->network_header;
}staticinline voidskb_reset_network_header(structsk_buff *skb)
{
skb->network_header = skb->data;
}staticinline voidskb_set_network_header(structsk_buff *skb,constintoffset)
{
skb->network_header = skb->data + offset;
}staticinline unsignedchar*skb_mac_header(conststructsk_buff *skb)
{returnskb->mac_header;
}staticinline intskb_mac_header_was_set(conststructsk_buff *skb)
{returnskb->mac_header != NULL;
}staticinline voidskb_reset_mac_header(structsk_buff *skb)
{
skb->mac_header = skb->data;
}staticinline voidskb_set_mac_header(structsk_buff *skb,constintoffset)
{
skb->mac_header = skb->data + offset;
}#endif/* NET_SKBUFF_DATA_USES_OFFSET */1、TCP層獲取相關(guān)偏移的函數(shù)staticinline structtcphdr *tcp_hdr(conststructsk_buff *skb)
{return(structtcphdr *)skb_transport_header(skb);
}這個(gè)函數(shù)用來獲得sk_buff結(jié)構(gòu)中TCP頭的指針staticinline unsignedinttcp_hdrlen(conststructsk_buff *skb)
{returntcp_hdr(skb)->doff *4;
}這個(gè)函數(shù)用來獲得TCP頭的長(zhǎng)度staticinline unsignedinttcp_optlen(conststructsk_buff *skb)
{return(tcp_hdr(skb)->doff -5) *4;
}獲取tcp option的長(zhǎng)度2、IP相關(guān)的函數(shù)staticinline structiphdr *ip_hdr(conststructsk_buff *skb)
{return(structiphdr *)skb_network_header(skb);
}該函數(shù)獲得ip頭staticinline structiphdr *ipip_hdr(conststructsk_buff *skb)
{return(structiphdr *)skb_transport_header(skb);
}該函數(shù)獲得ipip頭,實(shí)際上偏移已經(jīng)跑到了傳輸層的開始3、MAC相關(guān)函數(shù)staticinline structebt_802_3_hdr *ebt_802_3_hdr(conststructsk_buff *skb)
{return(structebt_802_3_hdr *)skb_mac_header(skb);
}獲取802.3MAC頭指針。staticinline structethhdr *eth_hdr(conststructsk_buff *skb)
{return(structethhdr *)skb_mac_header(skb);
}獲取以太網(wǎng)MAC頭指針。以太網(wǎng)頭指針結(jié)構(gòu)體:structethhdr {
unsignedcharh_dest[ETH_ALEN];/* destination eth addr */unsignedcharh_source[ETH_ALEN];/* source ether addr */__be16 h_proto;/* packet type ID field */} __attribute__((packed));內(nèi)核中網(wǎng)絡(luò)地址轉(zhuǎn)化為字符串形式的IP地址的宏定義:#defineNIPQUAD(addr) \
((unsignedchar*)&addr)[0], \
((unsignedchar*)&addr)[1], \
((unsignedchar*)&addr)[2], \
((unsignedchar*)&addr)[3]#defineNIPQUAD_FMT "%u.%u.%u.%u"
2.2 sk_buff套接字緩存結(jié)構(gòu):
代碼:
//套接字緩存structsk_buff {/* These two members must be first. */structsk_buff *next;structsk_buff *prev;structsk_buff_head *list;structsock *sk;//指向創(chuàng)建報(bào)文的socketstructtimeval stamp;//此報(bào)文收到時(shí)的時(shí)間structnet_device *dev;//收到此報(bào)文的網(wǎng)絡(luò)設(shè)備structnet_device *input_dev;structnet_device *real_dev;//TCP報(bào)頭union {structtcphdr *th;//tcp頭structudphdr *uh;//udp頭structicmphdr *icmph;structigmphdr *igmph;structiphdr *ipiph;structipv6hdr *ipv6h;
unsignedchar*raw;
} h;//IP報(bào)頭union {structiphdr *iph;structipv6hdr *ipv6h;structarphdr *arph;
unsignedchar*raw;
} nh;//鏈路層幀頭union {
unsignedchar*raw;
} mac;structdst_entry *dst;//此報(bào)文的路由,路由確定后賦此值structsec_path *sp;/*
* This is the control buffer. It is free to use for every
* layer. Please put your private variables there. If you
* want to keep them across layers you have to do a skb_clone()
* first. This is owned by whoever has the skb queued ATM.
*/charcb[40];//此報(bào)文的長(zhǎng)度,這是指網(wǎng)絡(luò)報(bào)文在不同協(xié)議層中的長(zhǎng)度,包括頭部和數(shù)據(jù)。在協(xié)議棧的不同層,這個(gè)長(zhǎng)度是不同的。unsignedintlen,
data_len,
mac_len,
csum;
unsignedcharlocal_df,
cloned,
pkt_type,//網(wǎng)絡(luò)報(bào)文的類型,常見的有PACKET_HOST,代表發(fā)給本機(jī)的報(bào)文;還有PACKET_OUTGOING,代表本機(jī)發(fā)出的報(bào)文。ip_summed;
__u32 priority;
unsignedshortprotocol,//鏈路層協(xié)議security;void(*destructor)(structsk_buff *skb);
#ifdef CONFIG_NETFILTER
unsignedlongnfmark;
__u32 nfcache;
__u32 nfctinfo;structnf_conntrack *nfct;
#ifdef CONFIG_NETFILTER_DEBUG
unsignedintnf_debug;#endif#ifdef CONFIG_BRIDGE_NETFILTERstructnf_bridge_info *nf_bridge;#endif#endif/* CONFIG_NETFILTER */#ifdefined(CONFIG_HIPPI)
union {
__u32 ifield;
}private;#endif#ifdef CONFIG_NET_SCHED
__u32 tc_index;/* traffic control index */#ifdef CONFIG_NET_CLS_ACT
__u32 tc_verd;/* traffic control verdict */__u32 tc_classid;/* traffic control classid */#endif#endif/* These elements must be at the end, see alloc_skb() for details. *///此報(bào)文存儲(chǔ)區(qū)的長(zhǎng)度,這個(gè)長(zhǎng)度是16字節(jié)對(duì)齊的,一般要比報(bào)文的長(zhǎng)度大unsignedinttruesize;
atomic_t users;/*head和end指向報(bào)文數(shù)據(jù)的整個(gè)單元.head與data之間的空間稱為headroom,tail與end之間的空間稱為tailroom.
*/unsignedchar*head, *data, *tail, *end; };
總結(jié)
以上是生活随笔為你收集整理的linux接收网络数据并存存储,linux网络数据包数据结构 Socket Buffer的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: r语言工作路径linux,R语言实用基础
- 下一篇: linux 其他常用命令