在2.6.24之后這個結構體有了較大的變化,此處先說一說2.6.16版本的sk_buff,以及解釋一些問題。
一、 先直觀的看一下這個結構體~~~~~~~~~~~~~~~~~~~~~~在下面解釋每個字段的意義~~~~~~~~~~~ [cpp]?view plain?copy?print?struct?sk_buff?{???????????/*?These?two?members?must?be?first.?*/???????????struct?sk_buff??????????*next;???????????struct?sk_buff??????????*prev;??????????????struct?sock?????????????*sk;???????????struct?skb_timeval??????tstamp;???????????struct?net_device???????*dev;???????????struct?net_device???????*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;??????????????struct??dst_entry???????*dst;???????????struct??sec_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.???????????*/???????????char????????????????????cb[48];??????????????unsigned?int????????????len,???????????????????????????????????data_len,???????????????????????????????????mac_len,???????????????????????????????????csum;???????????__u32???????????????????priority;???????????__u8????????????????????local_df:1,???????????????????????????????????cloned:1,???????????????????????????????????ip_summed:2,???????????????????????????????????nohdr:1,???????????????????????????????????nfctinfo:3;???????????__u8????????????????????pkt_type:3,???????????????????????????????????fclone:2,???????????????????????????????????ipvs_property:1;???????????__be16??????????????????protocol;??????????????void????????????????????(*destructor)(struct?sk_buff?*skb);??#ifdef?CONFIG_NETFILTER???????????__u32???????????????????nfmark;???????????struct?nf_conntrack?????*nfct;??#if?defined(CONFIG_NF_CONNTRACK)?||?defined(CONFIG_NF_CONNTRACK_MODULE)???????????struct?sk_buff??????????*nfct_reasm;??#endif??#ifdef?CONFIG_BRIDGE_NETFILTER???????????struct?nf_bridge_info???*nf_bridge;??#endif??#endif?/*?CONFIG_NETFILTER?*/??#ifdef?CONFIG_NET_SCHED???????????__u16???????????????????tc_index;???????/*?traffic?control?index?*/??#ifdef?CONFIG_NET_CLS_ACT???????????__u16???????????????????tc_verd;????????/*?traffic?control?verdict?*/??#endif??#endif?????????????????/*?These?elements?must?be?at?the?end,?see?alloc_skb()?for?details.??*/???????????unsigned?int????????????truesize;???????????atomic_t????????????????users;???????????unsigned?char???????????*head,???????????????????????????????????*data,???????????????????????????????????*tail,???????????????????????????????????*end;??};??
> : next和prev,這兩個域是用來連接相關的skb的(例如如果有分片,將這些分片連接在一起可以) > : sk,指向報文所屬的套接字指針 > : tstamp,記錄接收或者傳輸報文的時間戳 > : dev和input_dev,記錄接收或者發送的設備 >: union u,對于一個層次,例如tcp層,可能有很多不同的協議,他們的協議頭不一樣,那么這個聯合體就是記錄這些協議頭的。 ? ? ?此處u就是代表傳輸層 > : union nh,代表網絡層頭 > : union mac,代表鏈路層頭 > : dst,指向des_entry結構,記錄了到達目的地的路由信息,以及其他的一些網絡特征信息。 > : sp:安全路徑,用于xfrm > : cb[],保存與協議相關的控制信息,每個協議可能獨立使用這些信息。 > :?重要的字段 len 和 data_len: ? ? ? len代: 表整個數據區域的長度!這里要提前解釋幾個定義,skb的組成是有sk_buff控制 + 線性數據 + 非線性數據? ? ? ? (skb_shared_info)?組成! ? ? ?后面會具體解釋是什么意思!在sk_buff這個里面沒有實際的數據,這里僅僅是控制信息,數據是通過后面的data指針指向其他內 ? ? ?存塊的!那個內存塊中是線性數據和 ? ? ?非線性數據!那么len就是length(線性數據) + length(非線性數據)!!! ? ? ?data_len: 指的是length(非線性數據)!!!那么可以知道:length(線性數據) = ?skb->len - skb->data_len > : mac_len,指的是mac頭長度 > : csum,某時刻協議的校驗和 > : priority,報文排隊優先級,取決于ip中的tos域 > : local_df,允許在本地分配 > : cloned,保存當前的skb_buff是克隆的還是原始數據 > : ip_summed,是否計算ip校驗和 > : nohdr,僅僅引用數據區域 > :?pkt_type,報文類型,例如廣播,多播,回環,本機,傳出... > : fclone,skb_buff克隆狀態 > :?ipvs_property,skb_buff是否屬于ipvs > : protocal,協議信息 > :?nfmark,用于鉤子之間通信 > :?nfct_reasm,netfilter的跟蹤連接重新組裝指針 > :?nf_bridge,保存橋接信息 > : tc_index: Traffic control index,tc_verd: traffic control verdict > : truesize,該緩沖區分配的所有總的內存,包括:skb_buff + 所有數據大小 > : users,保存引用skb_buff的數量 > : 重要數據字段:head,data,tail,end!!! ? ? head:指向分配給的線性數據內存首地址( 建立起一個觀念:并不是分配這么多內存,就都能被使用作為數據存儲,可能沒這么多 ? ? 數據也有可能!但是也不要認為分配這么多 就足夠了,也不一定(非線性數據就是例子) ) ? ? data:指向保存數據內容的首地址!我們由head可以知道,head和data不一定就是指在同一個位置!!! ? ? tail:指向數據的結尾! ? ? end:指向分配的內存塊的結尾! ( 由上面我們知道數據結尾 != 分配的內存塊的結尾 ) ? ? 下面還會具體分析!!!!!!!!!!!
二、 我覺得需要先了解一些對于一個數據skb到底有什么,或者說由哪些元素組成!這就需要知道所謂的 “線性數據” 和 “非線性數據”。 基本的組成如下: > : sk_buff : 這是一個sk_buff的控制結構 > : 線性數據區域 > : 非線性數據區域( 由skb_shared_info結構體管理 )
那么下面通過一個圖來看看這個skb結構到底是怎么樣的!看(圖一)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????(圖一) 借助圖一,我們先來分析兩個重要字段:len和data_len! 之前說過len代表的是整個數據的長度,data_len代表的是非線性數據長度。我們由圖一可以看到線性數據長度為l1,再看看非線性數據,其實就是看frags[]和frag_list ok...那么我們可以知道非線性數據長度為( l2 + ... + ln ) + ( l(n+1) + ... + lm ) 即:len = l1 +?( l2 + ... + ln ) + ( l(n+1) + ... + lm ) ? ? ? ? data_len =?( l2 + ... + ln ) + ( l(n+1) + ... + lm )
ok...
現在從分配內存開始解釋這個圖的由來: 我們使用skb_alloc給skb分配空間,那么剛剛分配結束返回時候,是什么樣的情況呢?看下圖(圖二): ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? (圖二)
剛剛開始初始化的時候,預分配一個一塊線性數據區域,這個區域一般放入的是各個協議層次的不同的頭,還有一些實際數據,下面的非線性區域是為了彌補當數據真的很多的時候,作為數據區域的擴展!關于skb_shared_info具體意思下面會繼續說!注意在初始化的時候,head,data和tail都指向內存的開始位置,head在這個位置始終不變,它表示的是分配的內存的開始位置。end的位置也是不變的,表示的是分配的內存的結束位置。data和tail會隨著數據的加入和減少變化,總之表示的是放入數據的內存區域(由圖一)可知。
現在需要解釋一下skb_shared_info這個結構體,這個結構體真的是很很有特色!主要是其中的兩個字段frags和frag_list,下面繼續解釋: [cpp]?view plain?copy?print?struct?skb_shared_info?{???????????atomic_t????????dataref;????????//?對象被引用次數???????????unsigned?short??nr_frags;???????//?分頁段數目,即frags數組元素個數???????????unsigned?short??tso_size;??????????????????unsigned?short??tso_segs;???????????unsigned?short??ufo_size;???????????unsigned?int????ip6_frag_id;???????????struct?sk_buff??*frag_list;????//?一般用于分段(還沒有非常清楚的理解)???????????skb_frag_t??????frags[MAX_SKB_FRAGS];?//?保存分頁數據(skb->data_len=所有的數組數據長度之和)??};?? 關于frags和frag_list沒有必然的聯系!
> : 對于frags[]一般用在,當數據真的很多,而且在線性數據區域裝不下的時候,需要使用這個,skb_frag_t中是一頁一頁的數據,先看看結構體: [cpp]?view plain?copy?print?struct?skb_frag_struct?{???????????struct?page?*page;????//?代表一頁數據???????????__u16?page_offset;????//?代表相對開始位置的頁偏移量???????????__u16?size;???????????//?page中數據長度??};?? 需要注意的是:只有在DMA支持物理分散頁的Scatter/Gather(SG,分散/聚集)操作時候才可以使用frags[]來保存剩下的數據,否則,只能擴展線性數據區域進行保存!!!這些頁其實是其實就是虛擬頁映射到物理頁的結構,看下圖(圖三):
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? (圖三)
> : 對于frag_list來說,一般我們在分片的時候里面裝入每個片的信息,注意,每個片最終也都是被封裝成一個小的skb,這個必須 ? ? ?的! ? ? ?注意:具體怎么分片的看上一篇博文:數據分片?( ?看其中的ip_fragment函數??) ? ? ?那么看一下其基本結構如圖四: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? (圖四)
三、 最重要的是需要理解對于這個skb是怎么操作的,在操作的過程中,每一塊的內存分配是怎么變化的,這才更重要! 看下面的函數們:
> : alloc_skb()函數 [cpp]?view plain?copy?print?static?inline?struct?sk_buff?*alloc_skb(unsigned?int?size,???????????????????????????????????????????gfp_t?priority)??{???????????return?__alloc_skb(size,?priority,?0);??}??
其實看__alloc_skb函數:[cpp]?view plain?copy?print?struct?sk_buff?*__alloc_skb(unsigned?int?size,?gfp_t?gfp_mask,???????????????????????????????int?fclone)??{???????????kmem_cache_t?*cache;???????????struct?skb_shared_info?*shinfo;???????????struct?sk_buff?*skb;???????????u8?*data;??????????????cache?=?fclone???skbuff_fclone_cache?:?skbuff_head_cache;????//?根據克隆狀態來判斷在哪一個緩沖區進行分配cache??????????????/*?Get?the?HEAD?*/???????????skb?=?kmem_cache_alloc(cache,?gfp_mask?&?~__GFP_DMA);????????//?得到skb,注意這里沒有包含數據,僅僅是skb_buff這個結構體???????????if?(!skb)???????????????????goto?out;??????????????/*?Get?the?DATA.?Size?must?match?skb_add_mtu().?*/???????????size?=?SKB_DATA_ALIGN(size);?????????????????????????????????????//?獲得線性數據分片長度(注意對齊)???????????data?=?kmalloc(size?+?sizeof(struct?skb_shared_info),?gfp_mask);?//?注意分配的是什么,是size?+?skb_shared_info!!!!!???????????if?(!data)???????????????????goto?nodata;??????????????memset(skb,?0,?offsetof(struct?sk_buff,?truesize));??????????//?初始化???????????skb->truesize?=?size?+?sizeof(struct?sk_buff);???????????????//?實際大小等于sk_buff?+?size,剛剛開始還沒有非線性數據???????????atomic_set(&skb->users,?1);?????????????????????????????????????????????skb->head?=?data;????????????????????????????????????????????//?注意指針,這個結合上面的圖一清二楚???????????skb->data?=?data;???????????skb->tail?=?data;???????????skb->end??=?data?+?size;???????????/*?make?sure?we?initialize?shinfo?sequentially?*/???????????shinfo?=?skb_shinfo(skb);???????????atomic_set(&shinfo->dataref,?1);???????????shinfo->nr_frags??=?0;???????????shinfo->tso_size?=?0;???????????shinfo->tso_segs?=?0;???????????shinfo->ufo_size?=?0;???????????shinfo->ip6_frag_id?=?0;???????????shinfo->frag_list?=?NULL;??????????????if?(fclone)?{???????????????????struct?sk_buff?*child?=?skb?+?1;???????????????????atomic_t?*fclone_ref?=?(atomic_t?*)?(child?+?1);??????????????????????skb->fclone?=?SKB_FCLONE_ORIG;???????????????????atomic_set(fclone_ref,?1);??????????????????????child->fclone?=?SKB_FCLONE_UNAVAILABLE;???????????}??out:???????????return?skb;??nodata:???????????kmem_cache_free(cache,?skb);???????????skb?=?NULL;???????????goto?out;??}??
那么alloc之后的圖就是(圖五): ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?(圖五) 其實和圖二是一樣的!我們可以看到,現在僅僅是分配了線束數據區域,但是現在還沒有數據!一定要注意!所以前面三個指針指在一起!因為沒有數據,那么len和data_len的值就是0 !
> : skb_reserve函數 [cpp]?view plain?copy?print?static?inline?void?skb_reserve(struct?sk_buff?*skb,?int?len)??{???????????skb->data?+=?len;???????????skb->tail?+=?len;???}??
代碼其實很easy、就是移動兩個指針而已~
這個函數很重要,是為“協議頭”預留空間!而且是盡最大的空間預留,因為很多頭都會有可選項,那么我們不知道可選項是多大,所以只能是按照最大的分配,那么也說明了一點,預留的空間headroom也就是不一定都能使用完的!可能還有剩余的,由上面的圖也可以看出來!這也是為什么需要這么多指針的問題!那么這個函數直接導致head指針和tail、data指針分離,入下面圖六所示: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? (圖六)
注意headroom就是用來存儲各個協議頭的足夠大的空間,tailroom就可以認為是存儲其他線性數據的空間。( 這里不要曲解協議頭不是線性數據,其實協議頭也是!!!所以當增加頭的時候,data指針向上移動,當增加其他數據的時候,tail指針向下移動 )。現在data和tail指向一起,那么還是說明數據沒有!!!
> : skb_put函數 ----> 用于操作線性數據區域(tailroom區域)的用戶數據 [cpp]?view plain?copy?print?static?inline?unsigned?char?*skb_put(struct?sk_buff?*skb,?unsigned?int?len)??{???????????unsigned?char?*tmp?=?skb->tail;???????????SKB_LINEAR_ASSERT(skb);?????????????????????skb->tail?+=?len;?????????????????//?移動指針???????????skb->len??+=?len;?????????????????//?數據空間增大len???????????if?(unlikely(skb->tail>skb->end))?//?如果tail指針超過end指針了,那么處理錯誤~???????????????????skb_over_panic(skb,?len,?current_text_addr());???????????return?tmp;??}??
這函數其實就是從tailroom預留空間,相當于是移動tail指針,這樣如果從上圖(圖六)開始看,也就是tail開始向下移動,和data分離了。。。一般來說,這樣做都是為了用戶數據再次處理,或者說為TCP/IP的負載預留空間! 看圖七,當使用skb_put時候,由圖六---->圖七 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ?(圖七)
我們可以看到指針的移動data還是在headroom的下面,中間的是用戶數據預留的部分,由skb_put得到,tail表示數據結尾!再看一下sk_buff中的len,變成了數據長度ld!!
> : skb_push函數:----------> 用于操作headroom區域的協議頭 [cpp]?view plain?copy?print?static?inline?unsigned?char?*skb_push(struct?sk_buff?*skb,?unsigned?int?len)??{???????????skb->data?-=?len;??????//?向上移動指針???????????skb->len??+=?len;??????//?數據長度增加???????????if?(unlikely(skb->data<skb->head))??//?data指針超過head那么就是處理錯誤~???????????????????skb_under_panic(skb,?len,?current_text_addr());???????????return?skb->data;??}??
和skb_put對應,上面試操作用戶數據的,這里是操作協議頭的!其實就是data指針向上移動而已~注意len增大了哦~前面說了協議頭也是屬于數據! 如下面圖所示,由圖七---->圖八 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?(圖八)
我們可以知道,data向上移動了,同時注意len變成ld+lp了,其中lp是這個增加的協議頭的長度!
> : skb_pull函數:-----------> 其實這個函數才是與skb_push函數對應的函數!因為這是去頭函數,而skb_push是增頭函數!所以這個函數一般用在解包的時候! [cpp]?view plain?copy?print?static?inline?unsigned?char?*skb_pull(struct?sk_buff?*skb,?unsigned?int?len)??{???????????return?unlikely(len?>?skb->len)???NULL?:?__skb_pull(skb,?len);??}????????static?inline?unsigned?char?*__pskb_pull(struct?sk_buff?*skb,?unsigned?int?len)??{???????????if?(len?>?skb_headlen(skb)?&&???????????????!__pskb_pull_tail(skb,?len-skb_headlen(skb)))???????????????????return?NULL;???????????skb->len?-=?len;??????????????????????????????//?長度減小???????????return?skb->data?+=?len;??????????????????????//?移動指針??}??
其實就是data指針向下移動,當前一個協議頭被去掉,headroom剩余的空間增大了!看下圖: 由圖八---->圖九: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??(圖九)
虛線是data之前的指針位置,現在移動到下面實線!!需注意:len的長度減小,減小的大小是剝去的頭的大小!!
四、 最后我們從兩條線整體分析一下: 1:從應用層用戶數據開始,直到物理層發送出去 ? ? ? > 初始化的什么就不多說了,和前面的差不多,現在也加入用戶數據已經在了,如圖七所示一樣!那么到了TCP層,需要增加 ? ? ? ? ?TCP層的頭: ? ? ? ? ?如圖10所示: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ?(圖10)
? ? ? ??? ? 需要注意的是這里是傳輸層,那么傳輸層的結構u中的th代表的是tcp的頭,那么tcp指向tcp頭OK!同時注意 len長度+=l1 哦~~~ ? ? ? ? > 再看到了IP層:如圖11 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???(圖11)
??? ? ? ? ? ? ? 至于需要解釋什么就沒什么了,都是一樣的~ ? ? ? ? ? ? ?> 到鏈路層了:如圖12 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? (圖12)
??OK!
2:第二個過程其實是第一個逆過程,都差不多,所以不多說了~
五、 最后看一下操作skb的兩個函數pskb_copy和skb_copy 前者僅僅是將sk_buff的結構體和線性數據copy過來,對于非線性數據,是引用原始的skb的數據的!而后者是不僅將sk_buff和線性數據拷貝,同時將非線性數據也copy了一份,看下面就明白了!這就在效率上就差了很多!所以如果不想修改數據,那么還是使用pskb_copy更好!
對于pskb_copy:
對于skb_copy:
OK ?我覺得差不多了~~~~~結束~~~~~~~~~~~~~?
|