亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 839 | 回復(fù): 0
打印 上一主題 下一主題

sk_buff [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2009-02-25 16:35 |只看該作者 |倒序瀏覽


struct sk_buff {
/* These two members must be first. */
struct sk_buff  *next;  //這兩個變量讓sk_buff   
struct sk_buff  *prev;  //構(gòu)成雙向的鏈表         
struct sk_buff_head *list;  //指向鏈表的頭
struct sock         *sk;    //指向創(chuàng)建這個sk_buff的socket
struct timeval      stamp;  //該packet到達(dá)的時間(以jiffies為單位)
struct net_device   *dev;   //incoming/outcoming 的設(shè)備
struct net_device   *input_dev; //到達(dá)的設(shè)備
struct net_device   *real_dev;  //真實使用的設(shè)備
union { //指向傳輸層
  struct tcphdr  *th;
  struct udphdr  *uh;
  struct icmphdr *icmph;
  struct igmphdr *igmph;
  struct iphdr   *ipiph;
  struct ipv6hdr *ipv6h;
  unsigned char  *raw;
} h;
union { //指向網(wǎng)絡(luò)層
  struct iphdr   *iph;
  struct ipv6hdr *ipv6h;
  struct arphdr  *arph;
  unsigned char  *raw;
} nh;
union { //指向鏈路層
    unsigned char  *raw;
} mac;
struct  dst_entry *dst; //目的表項(指向一個路由cache,在后面文章詳解)
struct sec_path   *sp;  //只被xfrm使用(這個討論基本上沒用到)
/*
  * 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[40];
unsigned int  len,      //真實數(shù)據(jù)區(qū)的長度
               data_len, //數(shù)據(jù)區(qū)的長度
               mac_len,  //mac長度
               csum;     //checknum
__u32   priority;
//以下都是作為flag用,只用了一個byte的5個bit
__u8    local_df:1, //允許local分片標(biāo)志
         cloned:1,   //允許clone操作標(biāo)志
         ip_summed:2,
         nohdr:1;
         /* 3 bits spare */
__u8     pkt_type; //packet的類型,在if_packet.h中定義取值
__be16   protocol;
void   (*destructor)(struct sk_buff *skb);
............
............
/* These elements must be at the end, see alloc_skb() for details.  */
unsigned int  truesize; //buffer的大小(后面詳解)
atomic_t      users;    //user計數(shù),atomic_t指明該變量只能"原子"操作
//在后面詳細(xì)介紹
unsigned char  *head,
                *data,
                *tail,
                *end;
};
struct sk_buff可能是linux網(wǎng)絡(luò)代碼中最重要的數(shù)據(jù)結(jié)構(gòu),它表示接收或發(fā)送數(shù)據(jù)包的包頭信息,并包含很多成員變量供網(wǎng)絡(luò)代碼中的各子系統(tǒng)使用。
    這個結(jié)構(gòu)被網(wǎng)絡(luò)的不同層(MAC或者其他二層鏈路協(xié)議,三層的IP,四層的TCP或UDP等)使用,并且其中的成員變量在結(jié)構(gòu)從一層向另一層傳遞時改變。 L4向L3傳遞前會添加一個L4的頭部,同樣,L3向L2傳遞前,會添加一個L3的頭部。添加頭部比在不同層之間拷貝數(shù)據(jù)的效率更高。由于在緩沖區(qū)的頭部 添加數(shù)據(jù)意味著要修改指向緩沖區(qū)的指針,這是個復(fù)雜的操作,所以內(nèi)核提供了一個函數(shù)skb_reserve來完成這個功能。協(xié)議棧中的每一層在往下一層傳 遞緩沖區(qū)前,第一件事就是調(diào)用skb_reserve在緩沖區(qū)的頭部給協(xié)議頭預(yù)留一定的空間。
    skb_reserve同樣被設(shè)備驅(qū)動使用來對齊接收到包的包頭。如果緩沖區(qū)向上層協(xié)議傳遞,舊的協(xié)議層的頭部信息就沒什么用了。例如,L2的頭部只有在 網(wǎng)絡(luò)驅(qū)動處理L2的協(xié)議時有用,L3是不會關(guān)心它的信息的。但是,內(nèi)核并沒有把L2的頭部從緩沖區(qū)中刪除,而是把有效荷載的指針指向L3的頭部,這樣做, 可以節(jié)省CPU時間。
    有些sk_buff成員變量的作用是方便查找或者是連接數(shù)據(jù)結(jié)構(gòu)本身。內(nèi)核可以把sk_buff組織成一個雙向鏈表。當(dāng)然,這個鏈表的結(jié)構(gòu)要比常見的雙向 鏈表的結(jié)構(gòu)復(fù)雜一點。就像任何一個雙向鏈表一樣,sk_buff中有兩個指針next和prev,其中,next指向下一個節(jié)點,而prev指向上一個節(jié) 點。在第一個節(jié)點前面會插入另一個結(jié)構(gòu)sk_buff_head,這是一個輔助節(jié)點(作為sk_buff雙向鏈表的頭),它的定義如下:
struct sk_buff_head {
    struct sk_buff -*next;
    struct sk_buff -
*prev;
    __u32           qlen;
    spinlock_t      lock;
};
    qlen代表鏈表元素的個數(shù)
    lock用于防止對鏈表的并發(fā)訪問
    sk_buff和sk_buff_head的前兩個元素是一樣的:next和prev指針。這使得它們可以放到同一個鏈表中,盡管sk_buff_head要比sk_buff小得多。另外,相同的函數(shù)可以同樣應(yīng)用于sk_buff和sk_buff_head。
sk_buff->sk
    這是一個指向擁有這個sk_buff的sock結(jié)構(gòu)的指針。這個指針在網(wǎng)絡(luò)包由本機(jī)發(fā)出或者由本機(jī)進(jìn)程接收時有效,因為插口相關(guān)的信息被L4(TCP或 UDP)或者用戶空間程序使用。如果sk_buff只在轉(zhuǎn)發(fā)中使用(這意味著,源地址和目的地址都不是本機(jī)地址),這個指針是NULL
sk_buff->len
   
表示當(dāng)前協(xié)議數(shù)據(jù)包的長度
。它包括主緩沖區(qū)中的數(shù)據(jù)長度(data指針指向它)和分片中的數(shù)據(jù)長度。
sk_buff->data_len
    和len不同,data_len只計算分片中數(shù)據(jù)的長度
sk_buff->mac_len
    這是mac頭的長度
sk_buff->users
    這是一個引用計數(shù),用于計算有多少實體引用了這個sk_buff緩沖區(qū)。它的主要用途是防止釋放sk_buff后,還有其他實體引用這個sk_buff。 因此,每個引用這個緩沖區(qū)的實體都必須在適當(dāng)?shù)臅r候增加或減小這個變量。這個計數(shù)器只保護(hù)sk_buff結(jié)構(gòu)本身,而緩沖區(qū)的數(shù)據(jù)部分由類似的計數(shù)器 (dataref)來保護(hù).有時可以用atomic_inc和atomic_dec函數(shù)來直接增加或減小users,但是,通常還是使用函數(shù) skb_get和kfree_skb來操作這個變量。
sk_buff->truesize
    這是緩沖區(qū)的總長度,包括sk_buff結(jié)構(gòu)和數(shù)據(jù)部分。如果申請一個len字節(jié)的緩沖區(qū),alloc_skb函數(shù)會把它初始化成len+sizeof(sk_buff)。當(dāng)skb->len變化時,這個變量也會變化。
sk_buff->head
sk_buff->data
sk_buff->tail
sk_buff->end

    它們表示緩沖區(qū)和數(shù)據(jù)部分的邊界。在每一層申請緩沖區(qū)時,它會分配比協(xié)議頭或協(xié)議數(shù)據(jù)大的空間。head和end指向緩沖區(qū)的頭部和尾部,而data和 tail指向?qū)嶋H數(shù)據(jù)的頭部和尾部。每一層會在head和data之間填充協(xié)議頭,或者在tail和end之間添加新的協(xié)議數(shù)據(jù)。數(shù)據(jù)部分會在尾部包含一 個附加的頭部。
        struct sk_buff
         |----------|
         |          | -+---->|--------|
         |          |
-|     |        | head
         |          |
-|     |        |
         |----------|
-| -+->|--------|
         |   head   |--+
-| -|        |
         |----------|     |
-|        |
         |   data   |-----+
-|        | data
         |----------|        |        |
         |   tail   |-----+
-|        |
         |----------|     |
-|        |
         |   end    |--+
-| -|        |
         |----------|
-| -+->|--------|
                       |     |        | tail
                       |     |        |
                       +---->|--------|
void (*destructor)(struct sk_buff *skb)

    這個函數(shù)指針可以初始化成一個在緩沖區(qū)釋放時完成某些動作的函數(shù)。如果緩沖區(qū)不屬于一個socket,這個函數(shù)指針通常是不會被賦值的。如果緩沖區(qū)屬于一 個socket,這個函數(shù)指針會被賦值為sock_rfree或sock_wfree(分別由skb_set_owner_r或 skb_set_owner_w函數(shù)初始化)。這兩個sock_xxx函數(shù)用于更新socket的隊列中的內(nèi)存容量。
sk_buff->tstamp
    這個變量只對接收到的包有意義。它代表包接收時的時間戳,或者有時代表包準(zhǔn)備發(fā)出時的時間戳。它在netif_rx里面由函數(shù)net_timestamp設(shè)置,而netif_rx是設(shè)備驅(qū)動收到一個包后調(diào)用的函數(shù)。
sk_buff->dev
    這個變量的類型是net_device,net_device它代表一個網(wǎng)絡(luò)設(shè)備。dev的作用與這個包是準(zhǔn)備發(fā)出的包還是剛接收的包有關(guān)。當(dāng)收到一個包時,設(shè)備驅(qū)動會把sk_buff的dev指針指向收到這個包的網(wǎng)絡(luò)設(shè)備;當(dāng)一個包被發(fā)送時,這個變量代表將要發(fā)送這個包的設(shè)備。在發(fā)送網(wǎng)絡(luò)包時設(shè)置這個值的代碼要比接收網(wǎng)絡(luò)包時設(shè)置這個值的代碼復(fù)雜。有些網(wǎng)絡(luò)功能可以把多個網(wǎng) 絡(luò)設(shè)備組成一個虛擬的網(wǎng)絡(luò)設(shè)備(也就是說,這些設(shè)備沒有和物理設(shè)備直接關(guān)聯(lián)),并由一個虛擬網(wǎng)絡(luò)設(shè)備驅(qū)動管理。當(dāng)虛擬設(shè)備被使用時,dev指針指向虛擬設(shè) 備的net_device結(jié)構(gòu)。而虛擬設(shè)備驅(qū)動會在一組設(shè)備中選擇一個設(shè)備并把dev指針修改為這個設(shè)備的net_device結(jié)構(gòu)。因此,在某些情況 下,指向傳輸設(shè)備的指針會在包處理過程中被改變。
sk_buff->input_dev
    這是收到包的網(wǎng)絡(luò)設(shè)備的指針。如果包是本地生成的,這個值為NULL。對以太網(wǎng)設(shè)備來說,這個值由
eth_type_trans
初始化,它主要被流量控制代碼使用。
sk_buff->h
sk_buff->nh
sk_buff->mac

    這些是指向TCP/IP各層協(xié)議頭的指針:h指向L4(傳輸層),nh指向L3(網(wǎng)絡(luò)層),mac指向L2(數(shù)據(jù)鏈路層)。每個指針的類型都是一個聯(lián)合, 包含多個數(shù)據(jù)結(jié)構(gòu),每一個數(shù)據(jù)結(jié)構(gòu)都表示內(nèi)核在這一層可以解析的協(xié)議。例如,h是一個包含內(nèi)核所能解析的L4協(xié)議的數(shù)據(jù)結(jié)構(gòu)的聯(lián)合。每一個聯(lián)合都有一個 raw變量用于初始化,后續(xù)的訪問都是通過協(xié)議相關(guān)的變量進(jìn)行的。
    當(dāng)接收一個包時,處理n層協(xié)議頭的函數(shù)從其下層(n-1層)收到一個緩沖區(qū),它的skb->data指向n層協(xié)議的頭。處理n層協(xié)議的函數(shù)把本層的 指針(例如,L3對應(yīng)的是skb->nh指針)初始化為skb->data,因為這個指針(data指針)的值會在處理下一層協(xié)議時改變 (skb->data將被初始化成緩沖區(qū)里的其他地址)。在處理n層協(xié)議的函數(shù)結(jié)束時,在把包傳遞給n+1層的處理函數(shù)前,它會把skb-> data指針指向n層協(xié)議頭的末尾,這正好是n+1層協(xié)議的協(xié)議頭。
    當(dāng)網(wǎng)卡驅(qū)動程序收到一個UDP數(shù)據(jù)報后,它創(chuàng)建一個結(jié)構(gòu)體struct sk_buff,確保sk_buff->data成員指向的空間足夠存放收到的數(shù)據(jù)(對于數(shù)據(jù)報分片的情況,因為比較復(fù)雜,我們暫時忽略,我們假設(shè) 一次收到的是一個完整的UDP數(shù)據(jù)報)。把收到的數(shù)據(jù)全部拷貝到sk_buff->data指向的空間,然后,把skb->mac.raw指 向data,此時,數(shù)據(jù)報的開始位置是一個以太網(wǎng)頭,所以skb->mac.raw指向鏈路層的以太網(wǎng)頭。然后通過調(diào)用skb_pull剝掉以太網(wǎng) 頭,所謂剝掉以太網(wǎng)頭,只是把data加上sizeof(struct ethhdr),同時len減去這個值,這樣,在邏輯上,skb已經(jīng)不包含以太網(wǎng)頭了,但通過skb->mac.raw還能找到它。這就是我們通常 所說的,IP數(shù)據(jù)報被收到后,在鏈路層被剝?nèi)ヒ蕴W(wǎng)頭。
sk_buff->dst
    這個變量在路由子系統(tǒng)中使用
sk_buff->sp
    這個變量被IPSec協(xié)議用于跟蹤傳輸?shù)男畔?br /> sk_buff->cb[48]
    這是一個“control buffer”,或者說是一個私有信息的存儲空間,由每一層自己維護(hù)并使用。它在分配sk_buff結(jié)構(gòu)時分配(它目前的大小是48字節(jié),已經(jīng)足夠為每一 層存儲必要的私有信息了)。在每一層中,訪問這個變量的代碼通常用宏實現(xiàn)以增強代碼的可讀性。例如,TCP用這個變量存儲tcp_skb_cb結(jié)構(gòu)。
    下面這個宏被TCP代碼用來訪問cb變量。在這個宏里面,有一個簡單的類型轉(zhuǎn)換:
    #define TCP_SKB_CB(__skb)     ((struct tcp_skb_cb *)&((__skb)->cb[0]))
    下面的例子是TCP子系統(tǒng)在收到一個分段時填充相關(guān)數(shù)據(jù)結(jié)構(gòu)的代碼:
int tcp_v4_rcv(struct sk_buff *skb)
{
    ...
    th = skb->h.th;
    TCP_SKB_CB(skb)->seq = ntohl(th->seq);
    TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
                 skb->len - th->doff * 4);
    TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
    TCP_SKB_CB(skb)->when = 0;
    TCP_SKB_CB(skb)->flags = skb->nh.iph->tos;
    TCP_SKB_CB(skb)->sacked = 0;
    ...
}
    如果想要了解cb中的參數(shù)是如何被取出的,可以查看net/ipv4/tcp_output.c中的tcp_transmit_skb函數(shù)。這個函數(shù)被TCP用于向IP層發(fā)送一個分段。
sk_buff->csum
sk_buff->ip_summed

    表示校驗和以及相關(guān)狀態(tài)標(biāo)記
sk_buff->cloned
    一個布爾標(biāo)記,當(dāng)被設(shè)置時,表示這個結(jié)構(gòu)是另一個sk_buff的克隆
sk_buff->pkt_type
    這個變量表示幀的類型,分類是由L2的目的地址來決定的。這個值在網(wǎng)卡驅(qū)動程序中由函數(shù)eth_type_trans通過判斷目的以太網(wǎng)地址來確定。如果 目的地址是FF:FF:FF:FF:FF:FF,則為廣播地址,pkt_type = PACKET_BROADCAST;如果最高位為1,則為組播地址,pkt_type = PACKET_MULTICAST;如果目的mac地址跟本機(jī)mac地址不相等,則不是發(fā)給本機(jī)的數(shù)據(jù)報,pkt_type = PACKET_OTHERHOST;否則就是缺省值PACKET_HOST。
/* Packet types */
#define PACKET_HOST         0       /* To us        */
#define PACKET_BROADCAST    1       /* To all       */
#define PACKET_MULTICAST    2       /* To group     */
#define PACKET_OTHERHOST    3       /* To someone else      */
#define PACKET_OUTGOING     4       /* Outgoing of any type */
sk_buff->priority
    這個變量描述發(fā)送或轉(zhuǎn)發(fā)包的QoS類別。如果包是本地生成的,socket層會設(shè)置priority變量。如果包是將要被轉(zhuǎn)發(fā)的, rt_tos2priority函數(shù)會根據(jù)ip頭中的Tos域來計算賦給這個變量的值。這個變量的值與DSCP(DiffServ CodePoint)沒有任何關(guān)系。
sk_buff->protocol
    這個變量是高層協(xié)議從二層設(shè)備的角度所看到的協(xié)議。典型的協(xié)議包括IP,IPV6和ARP。完整的列表在 include/linux/if_ether.h中。由于每個協(xié)議都有自己的協(xié)議處理函數(shù)來處理接收到的包,因此,這個域被設(shè)備驅(qū)動用于通知上層調(diào)用哪 個協(xié)議處理函數(shù)。每個網(wǎng)絡(luò)驅(qū)動都調(diào)用netif_rx來通知上層網(wǎng)絡(luò)協(xié)議的協(xié)議處理函數(shù),因此protocol變量必須在這些協(xié)議處理函數(shù)調(diào)用之前初始 化。
----------------------------------------
    linux內(nèi)核是模塊化的,你可以選擇包含或者刪除某些功能。因此,sk_buff結(jié)構(gòu)里面的一些成員變量只有在內(nèi)核選擇支持某些功能時才有效,比如防火墻(netfilter)或者qos:
__u32             nfctinfo
...
#ifdef CONFIG_NETFILTER
    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
    __u32         nfmark;
#endif /* CONFIG_NETFILTER */
    這些變量被netfilter使用(防火墻代碼),內(nèi)核編譯選項是“Device Drivers->Networking support-> Networking options-> Network packet filtering”和兩個子選項“Network packet filtering debugging”和“Bridged IP/ARP packets filtering”
#ifdef CONFIG_NET_SCHED
    __u16          tc_index;
#ifdef CONFIG_NET_CLS_ACT
    __u16          tc_verd;
#endif
#endif
    這兩個變量被流量控制代碼使用。tc_index只有在編譯時定義了CONFIG_NET_SCHED符號才有效;tc_verd只有在編譯時定義了CONFIG_NET_CLS_ACT符號才有效。這兩個符號可以通過選擇特定的編譯選項來定義:
Networking --->
    Networking options --->
        QoS and/or fair queueing --->
      
  • QoS and/or fair queueing
          
  •    Actions
       
        QoS選項不能被編譯成內(nèi)核模塊。原因就是,內(nèi)核編譯之后,由某個選項所控制的數(shù)據(jù)結(jié)構(gòu)是不能動態(tài)變化的。一般來說,如果某個選項會修改內(nèi)核數(shù)據(jù)結(jié)構(gòu)(比如說,在sk_buff里面增加一個項tc_index),那么,包含這個選項的組件就不能被編譯成內(nèi)核模塊。
        你可能經(jīng)常需要查找是哪個make menuconfig編譯選項或者變種定義了某個#ifdef標(biāo)記,以便理解內(nèi)核中包含的某段代碼。在2.6內(nèi)核中,最快的,查找它們之間關(guān)聯(lián)關(guān)系的方 法,就是查找分布在內(nèi)核源代碼樹中的kconfig文件中是否定義了相應(yīng)的符號(每個目錄都有一個這樣的文件)。在2.4內(nèi)核中,你需要查看 Documentation/Configure.help文件。
        Quality of Service(QoS)服務(wù)質(zhì)量
    struct sk_buff {
        struct sk_buff    * next;               
             struct sk_buff    * prev;            
        struct sk_buff_head * list;        
        struct sock    *sk;            
        struct timeval    stamp;            
        struct net_device    *dev;        
        struct net_device    *real_dev;   
        union
        {
            struct tcphdr    *th;
            struct udphdr    *uh;
            struct icmphdr    *icmph;
            struct igmphdr    *igmph;
            struct iphdr    *ipiph;
            struct spxhdr    *spxh;
            unsigned char    *raw;
        } h;
       
        union
        {
            struct iphdr    *iph;
            struct ipv6hdr    *ipv6h;
            struct arphdr    *arph;
            struct ipxhdr    *ipxh;
            unsigned char    *raw;
        } nh;
    union
        {   
              struct ethhdr    *ethernet;
              unsigned char     *raw;
        } mac;
        struct  dst_entry *dst;
         
        char        cb[48];     
        unsigned int     len;        
        unsigned int     data_len;
        unsigned int    csum;               
             unsigned char     __unused,            
            
                cloned,                       pkt_type,        
                  ip_summed;            __u32        priority;            atomic_t    users;            
        unsigned short    protocol;        
        unsigned short    security;        
        unsigned int    truesize;                           
        unsigned char    *head;                           
        unsigned char    *data;                           
        unsigned char    *tail;            
        unsigned char     *end;            
        void         (*destructor)(struct sk_buff *);   
    #ifdef CONFIG_NETFILTER
       
            unsigned long    nfmark;
       
        __u32        nfcache;
       
        struct nf_ct_info *nfct;
    #ifdef CONFIG_NETFILTER_DEBUG
            unsigned int nf_debug;
    #endif
    #endif
    #if defined(CONFIG_HIPPI)
        union{
            __u32    ifield;
        } private;
    #endif
    #ifdef CONFIG_NET_SCHED
           __u32           tc_index;               
    #endif
    };

    該結(jié)構(gòu)維護(hù)一個收到的或者要發(fā)送的網(wǎng)絡(luò)包。但其本身并不包含存放網(wǎng)絡(luò)包的數(shù)據(jù)的存儲區(qū)。存儲區(qū)是另外單獨分配的內(nèi)存空間,但該結(jié)構(gòu)說明了如何訪問存儲區(qū)空間,如何維護(hù)多個存儲區(qū)空間以及存儲網(wǎng)絡(luò)包解析的成果。
    所有的sk_buff是通過一個雙向鏈表進(jìn)行維護(hù)的。需要說明的是該雙向鏈表中的一個元素是struct sk_buff_head類型。它相當(dāng)于該雙向鏈表的表頭,其中有鎖,鏈表元素個數(shù)等維護(hù)鏈表的相關(guān)信息。鏈表中其它的元素都是sk_buff類型。
    鏈表結(jié)構(gòu)圖如下:
    而對于每個sk_buff而言,如前所述,其本身并不維護(hù)網(wǎng)絡(luò)包的存儲區(qū)。該結(jié)構(gòu)跟存儲區(qū)的關(guān)系如下圖所示:
    圖的左邊表示sk_buff結(jié)構(gòu),右側(cè)表示網(wǎng)絡(luò)包的存儲區(qū)。可以看到,sk_buff中有四個指針指向存儲區(qū)。其中head一定指向存儲區(qū)的開頭,end一定指向存儲區(qū)的結(jié)尾。data指向?qū)嶋H內(nèi)容的開頭,tail指向?qū)嶋H內(nèi)容的結(jié)尾。這樣做有兩個原因,一是在分配空間的時候我們尚不知道具體需要多大的空間,只能按照最大可能空間來分配;二是為了滿足字節(jié)對齊的需要。
    圖中Data部分的內(nèi)容包括網(wǎng)絡(luò)包的所有內(nèi)容。對于輸入包而言,其就是從當(dāng)前層向上的所有層的頭和最后的負(fù)載,每解析掉一層的頭,該協(xié)議的協(xié)議頭對應(yīng)的數(shù)據(jù)就不再繼續(xù)處理,所以data指針在每層會逐漸增大。對于輸出包而言,其每向下傳輸一層,都會添加一層的頭,所以sk_buff的data指針也會不斷減小。
    對于輸入包而言,存儲區(qū)的內(nèi)容是不變的。但我們在分析包時,在每層都會剝除該層的頭。為了保留我們的剝除結(jié)果,結(jié)構(gòu)中會有三個指針分別指向以太,網(wǎng)絡(luò)和傳輸三層的協(xié)議頭。這三個指針依次分別是union {…} mac,union {…} nh和union{…} h。三個成員都是聯(lián)合,其中的內(nèi)容是該層可能的協(xié)議類型的指針。
    還有一組成員表示存儲區(qū)中數(shù)據(jù)的長度。len表示存儲區(qū)的數(shù)據(jù)長度和分片長度之和。data_len表示分片長度。mac_len表示mac頭的長度。truesize表示存儲區(qū)總長度(即end-head)和sk_buff本身長度之和。
    下面的內(nèi)容是關(guān)于如何維護(hù)存儲區(qū)。
    alloc_skb和dev_alloc_skb用來分配sk_buff和存儲區(qū),kfree_skb和dev_kfree_skb則釋放sk_buff和其對應(yīng)的存儲區(qū)。剛初始化完的存儲區(qū),head,data,tail都指向存儲區(qū)的開頭,end指向結(jié)尾。下面這些函數(shù)是修改data和tail指針的指向。
    skb_reserve是在存儲區(qū)的開頭保留一段空間。其有兩個作用,對于接收到的報文而言,可以保持字節(jié)對齊;對于發(fā)送的報文而言,可以保留空間用于存放各層的協(xié)議頭。畢竟我們在高層協(xié)議就會分配該存儲區(qū),所以需要預(yù)留下層的協(xié)議頭的空間。該函數(shù)的作用會導(dǎo)致data和tail兩個指針都下移,在head和它們之間留出空間。
    skb_put則是將tail下移,即增加了真正空間的長度。
    skb_push是將data上移,也增加了真正空間的長度。
    skb_pull將data下移,減少了真正空間的長度。
    下面是sk_buff的其它成員:
    struct sock *sk:表示該網(wǎng)絡(luò)包所屬的socket。對于該主機(jī)發(fā)送的和接收的網(wǎng)絡(luò)包,自然對應(yīng)著一個socket套接字。對于轉(zhuǎn)發(fā)的網(wǎng)絡(luò)包,則該成員為NULL。
    atomic_t users:表示該sk_buff結(jié)構(gòu)實例被引用的計數(shù)。這個是結(jié)構(gòu)本身的引用計數(shù),而不是其對應(yīng)的存儲區(qū)的引用計數(shù)。
    void (*destructor)():析構(gòu)函數(shù)。
    struct timeval stamp:對于接受包,表示該包接收到的時間。
    struct net_device *dev:接收或者發(fā)送包的網(wǎng)卡接口。
    struct net_device *input_dev:接收到包的網(wǎng)卡接口。
    struct net_device *real_dev:在某些機(jī)器中,多個物理網(wǎng)卡接口可以被看作一個虛擬設(shè)備。這中情況下real_dev表示物理結(jié)構(gòu)。
    char cb[40]:一個用于對該網(wǎng)絡(luò)包進(jìn)行處理的通用緩沖區(qū)?梢源娣乓恍┰诓煌瑢又g傳輸?shù)臄?shù)據(jù)。
    unsigned int csum,ipsum:校驗和。
    unsigned char cloned:
    unsigned char pkt_type:包類型,可以是單發(fā)給該主機(jī)的包,廣播包,多播包,轉(zhuǎn)發(fā)包等。
    unsigned short protocol:表示以太層的上層協(xié)議,可以是IP,IPv6和ARP。
    下面是關(guān)于skb_shared_info結(jié)構(gòu)的。當(dāng)我們使用alloc_skb分配空間時,會在存儲區(qū)的后面緊接著一個該結(jié)構(gòu)的實例。如下圖所示:
    其中存儲了關(guān)于網(wǎng)絡(luò)塊的附加信息。不知為何不也放在sk_buff中。其中維護(hù)了存儲塊的引用計數(shù),IP分片數(shù)據(jù)的信息。
    對于sk_buff和存儲塊,有時是需要拷貝的。分為三種情況,一是只拷貝sk_buff,存儲塊復(fù)用,此時使用skb_clone函數(shù);二是拷貝sk_buff和存儲塊的主體,但存儲塊后的skb_shared_info中frags數(shù)組指向的分片數(shù)據(jù)不拷貝,這使用pskb_copy函數(shù);三是上述的全都拷貝,使用skb_copy函數(shù)。在不同情況下不同使用。


    本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u/6038/showart_1843120.html
  • 您需要登錄后才可以回帖 登錄 | 注冊

    本版積分規(guī)則 發(fā)表回復(fù)

      

    北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
    未成年舉報專區(qū)
    中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
    感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

    清除 Cookies - ChinaUnix - Archiver - WAP - TOP