skb详细解析【转】
生活随笔
收集整理的這篇文章主要介紹了
skb详细解析【转】
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
skb詳細(xì)解析【轉(zhuǎn)】? 摘自:http://blog.chinaunix.net/uid-30035229-id-4883992.html 在自己的模塊發(fā)送函數(shù)中,需要對(duì)skb進(jìn)行重新構(gòu)造和別的一些操作。在網(wǎng)上看到一個(gè)寫的還可以的,粘過(guò)來(lái),就不自己寫了,估計(jì)這個(gè)哥們也是看<Understanding Linux Network Internals>翻譯或者總結(jié)的。 ------------------------------------------------
1.???定義
Packet:?????? 通過(guò)網(wǎng)卡收發(fā)的報(bào)文,包括鏈路層、網(wǎng)絡(luò)層、傳輸層的協(xié)議頭和攜帶的數(shù)據(jù) Data Buffer:用于存儲(chǔ) packet 的內(nèi)存空間 SKB:?????????? struct sk_buffer 的簡(jiǎn)寫2.???概述
Struct sk_buffer 是 linux TCP/IP stack 中,用于管理Data Buffer的結(jié)構(gòu)。Sk_buffer 在數(shù)據(jù)包的發(fā)送和接收中起著重要的作用。 為了提高網(wǎng)絡(luò)處理的性能,應(yīng)盡量避免數(shù)據(jù)包的拷貝。Linux 內(nèi)核開(kāi)發(fā)者們?cè)谠O(shè)計(jì) sk_buffer 結(jié)構(gòu)的時(shí)候,充分考慮到這一點(diǎn)。目前 Linux 協(xié)議棧在接收數(shù)據(jù)的時(shí)候,需要拷貝兩次:數(shù)據(jù)包進(jìn)入網(wǎng)卡驅(qū)動(dòng)后拷貝一次,從內(nèi)核空間遞交給用戶空間的應(yīng)用時(shí)再拷貝一次。 Sk_buffer結(jié)構(gòu)隨著內(nèi)核版本的升級(jí),也一直在改進(jìn)。 學(xué)習(xí)和理解 sk_buffer 結(jié)構(gòu),不僅有助于更好的理解內(nèi)核代碼,而且也可以從中學(xué)到一些設(shè)計(jì)技巧。3.?? Sk_buffer?定義
struct sk_buff { ??????????????? 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; ??????????????? char?????????????????????????????????????? cb[40]; ??????????????? 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; ??????????????? __be16????????????????????????????????? protocol; ??????????????? void??????????????????????????????????????? (*destructor)(struct sk_buff *skb); ??????????????? /* These elements must be at the end, see alloc_skb() for details.? */ ??????????????? unsigned int?????????????????????????truesize; ??????????????? atomic_t?????????????????????????????? users; ??????????????? unsigned char?????????????????????*head, ??????????????????????????????????????????????? *data, ??????????????????????????????????????????????? *tail, ??????????????????????????????????????????????? *end; };4.???成員變量
·??????????????struct skb_timeval??? tstamp; 此變量用于記錄 packet 的到達(dá)時(shí)間或發(fā)送時(shí)間。由于計(jì)算時(shí)間有一定開(kāi)銷,因此只在必要時(shí)才使用此變量。需要記錄時(shí)間時(shí),調(diào)用net_enable_timestamp(),不需要時(shí),調(diào)用net_disable_timestamp() 。 tstamp 主要用于包過(guò)濾,也用于實(shí)現(xiàn)一些特定的 socket 選項(xiàng),一些 netfilter 的模塊也要用到這個(gè)域。 ·??????????????struct net_device????? *dev; ·??????????????struct net_device????? *input_dev; 這幾個(gè)變量都用于跟蹤與 packet 相關(guān)的 device。由于 packet 在接收的過(guò)程中,可能會(huì)經(jīng)過(guò)多個(gè) virtual driver 處理,因此需要幾個(gè)變量。 接收數(shù)據(jù)包的時(shí)候, dev 和 input_dev 都指向最初的 interface,此后,如果需要被 virtual driver 處理,那么 dev 會(huì)發(fā)生變化,而 input_dev 始終不變。 (These three members help keep track of the devices assosciated with a packet. The reason we have three different device pointers is that the main 'skb->dev' member can change as we encapsulate and decapsulate via a virtual device. So if we are receiving a packet from a device which is part of a bonding device instance, initially 'skb->dev' will be set to point the real underlying bonding slave. When the packet enters the networking (via 'netif_receive_skb()') we save 'skb->dev' away in 'skb->real_dev' and update 'skb->dev' to point to the bonding device. Likewise, the physical device receiving a packet always records itself in 'skb->input_dev'. In this way, no matter how many layers of virtual devices end up being decapsulated, 'skb->input_dev' can always be used to find the top-level device that actually received this packet from the network. ) ·??????????????char?????????????????????????????? cb[40]; 此數(shù)組作為 SKB 的控制塊,具體的協(xié)議可用它來(lái)做一些私有用途,例如 TCP 用這個(gè)控制塊保存序列號(hào)和重傳狀態(tài)。 ·??????????????unsigned int?????????????? len, ·???????????????????????????????????????????????????data_len, ·???????????????????????????????????????????????????mac_len, ·???????????????????????????????????????????????????csum; ‘len’ 表示此 SKB 管理的 Data Buffer 中數(shù)據(jù)的總長(zhǎng)度; 通常,Data Buffer 只是一個(gè)簡(jiǎn)單的線性 buffer,這時(shí)候 len 就是線性 buffer 中的數(shù)據(jù)長(zhǎng)度; 但在有 ‘paged data’ 情況下, Data Buffer 不僅包括第一個(gè)線性 buffer ,還包括多個(gè) page buffer;這種情況下, ‘data_len’ 指的是 page buffer 中數(shù)據(jù)的長(zhǎng)度,’len’ 指的是線性 buffer 加上 page buffer 的長(zhǎng)度;len – data_len 就是線性 buffer 的長(zhǎng)度。 ‘mac_len’ 指 MAC 頭的長(zhǎng)度。目前,它只在 IPSec 解封裝的時(shí)候被使用。將來(lái)可能從 SKB 結(jié)構(gòu)中 去掉。 ‘csum’ 保存 packet 的校驗(yàn)和。 (Finally, 'csum' holds the checksum of the packet. When building send packets, we copy the data in from userspace and calculate the 16-bit two's complement sum in parallel for performance. This sum is accumulated in 'skb->csum'. This helps us compute the final checksum stored in the protocol packet header checksum field. This field can end up being ignored if, for example, the device will checksum the packet for us. On input, the 'csum' field can be used to store a checksum calculated by the device. If the device indicates 'CHECKSUM_HW' in the SKB 'ip_summed' field, this means that 'csum' is the two's complement checksum of the entire packet data area starting at 'skb->data'. This is generic enough such that both IPV4 and IPV6 checksum offloading can be supported. ) ·??????????????__u32??????????????????????????? priority; “priority”用于實(shí)現(xiàn) QoS,它的值可能取之于 IPv4 頭中的 TOS 域。Traffic Control 模塊需要根據(jù)這個(gè)域來(lái)對(duì) packet 進(jìn)行分類,以決定調(diào)度策略。 ·??????????????__u8????????????????????????????? local_df:1, ·???????????????????????????????????????????????????cloned:1, ·???????????????????????????????????????????????????ip_summed:2, ·???????????????????????????????????????????????????nohdr:1, ·???????????????????????????????????????????????????nfctinfo:3; 為了能迅速的引用一個(gè) SKB 的數(shù)據(jù), 當(dāng) clone 一個(gè)已存在的 SKB 時(shí),會(huì)產(chǎn)生一個(gè)新的 SKB,但是這個(gè) SKB 會(huì)共享已有 SKB 的數(shù)據(jù)區(qū)。 當(dāng)一個(gè) SKB 被 clone 后,原來(lái)的 SKB 和新的 SKB 結(jié)構(gòu)中,”cloned” 都要被設(shè)置為1。 (The 'local_df' field is used by the IPV4 protocol, and when set allows us to locally fragment frames which have already been fragmented. This situation can arise, for example, with IPSEC. The 'nohdr' field is used in the support of TCP Segmentation Offload ('TSO' for short). Most devices supporting this feature need to make some minor modifications to the TCP and IP headers of an outgoing packet to get it in the right form for the hardware to process. We do not want these modifications to be seen by packet sniffers and the like. So we use this 'nohdr' field and a special bit in the data area reference count to keep track of whether the device needs to replace the data area before making the packet header modifications. The type of the packet (basically, who is it for), is stored in the 'pkt_type' field. It takes on one of the 'PACKET_*' values defined in the 'linux/if_packet.h' header file. For example, when an incoming ethernet frame is to a destination MAC address matching the MAC address of the ethernet device it arrived on, this field will be set to 'PACKET_HOST'. When a broadcast frame is received, it will be set to 'PACKET_BROADCAST'. And likewise when a multicast packet is received it will be set to 'PACKET_MULTICAST'. The 'ip_summed' field describes what kind of checksumming assistence the card has provided for a receive packet. It takes on one of three values: 'CHECKSUM_NONE' if the card provided no checksum assistence, 'CHECKSUM_HW' if the two's complement checksum over the entire packet has been provides in 'skb->csum', and 'CHECKSUM_UNNECESSARY' if it is not necessary to verify the checksum of this packet. The latter usually occurs when the packet is received over the loopback device. 'CHECKSUM_UNNECESSARY' can also be used when the device only provides a 'checksum OK' indication for receive packet checksum offload. ) ·??????????????void?????????????????????????????? (*destructor)(struct sk_buff *skb); ·??????????????unsigned int?????????????? truesize; 一個(gè) SKB 所消耗的內(nèi)存包括 SKB 本身和 data buffer。 truesize 就是 data buffer 的空間加上 SKB 的大小。 struct sock 結(jié)構(gòu)中,有兩個(gè)域,用于統(tǒng)計(jì)用于發(fā)送的內(nèi)存空間和用于接收的內(nèi)存空間,它們是: rmem_alloc wmem_alloc 另外兩個(gè)域則統(tǒng)計(jì)接收到的數(shù)據(jù)包的總大小和發(fā)送的數(shù)據(jù)包的總大小。 rcvbuf sndbuf rmem_alloc 和 rcvbuf,wmem_alloc 和sndbuf 用于不同的目的。 當(dāng)我們收到一個(gè)數(shù)據(jù)包后,需要統(tǒng)計(jì)這個(gè) socket 總共消耗的內(nèi)存,這是通過(guò)skb_set_owner_r() 來(lái)做的。 static inline void skb_set_owner_r(struct sk_buff *skb, struct sock *sk) { ??????? skb->sk = sk; ??????? skb->destructor = sock_rfree; ??????? atomic_add(skb->truesize, &sk->sk_rmem_alloc); } ?最后,當(dāng)釋放一個(gè) SKB 后,需要調(diào)用 skb->destruction() 來(lái)減少rmem_alloc 的值。 ? 同樣,在發(fā)送一個(gè) SKB 的時(shí)候,需要調(diào)用skb_set_owner_w() , ? static inline void skb_set_owner_w(struct sk_buff *skb, struct sock *sk) { ??????? sock_hold(sk); ??????? skb->sk = sk; ??????? skb->destructor = sock_wfree; ??????? atomic_add(skb->truesize, &sk->sk_wmem_alloc); } ?在釋放這樣的一個(gè) SKB 的時(shí)候,需要 調(diào)用 sock_free() ? void sock_wfree(struct sk_buff *skb) { ??????? struct sock *sk = skb->sk; ? ??????? /* In case it might be waiting for more memory. */ ??????? atomic_sub(skb->truesize, &sk->sk_wmem_alloc); ??????? if (!sock_flag(sk, SOCK_USE_WRITE_QUEUE)) ?????????????? sk->sk_write_space(sk); ??????? sock_put(sk); } ? (Another subtle issue is worth pointing out here. For receive buffer accounting, we do not grab a reference to the socket (via 'sock_hold()'), because the socket handling code will always make sure to free up any packets in it's receive queue before allowing the socket to be destroyed. Whereas for send packets, we have to do proper accounting with 'sock_hold()' and 'sock_put()'. Send packets can be freed asynchronously at any point in time. For example, a packet could sit in a devices transmit queue for a long time under certain conditions. If, meanwhile, the socket is closed, we have to keep the socket reference around until SKBs referencing that socket are liberated. ) ·??????????????unsigned char??????????????????????? *head, ·???????????????????????????????????????????????????*data, ·???????????????????????????????????????????????????*tail, ·???????????????????????????????????????????????????*end; ?SKB 對(duì) Data Buffer 的巧妙管理,就是靠這四個(gè)指針實(shí)現(xiàn)的。 下圖展示了這四個(gè)指針是如何管理數(shù)據(jù) buffer 的: Head 指向 buffer 的開(kāi)始,end 指向 buffer 結(jié)束。 Data 指向?qū)嶋H數(shù)據(jù)的開(kāi)始,tail 指向?qū)嶋H數(shù)據(jù)的結(jié)束。這四個(gè)指針將整個(gè) buffer 分成三個(gè)區(qū): Packet data:這個(gè)空間保存的是真正的數(shù)據(jù) Head room:處于 packet data 之上的空間,是一個(gè)空閑區(qū)域 Tail room:處于 packet data 之下的空間,也是空閑區(qū)域。 由于 TCP/IP 協(xié)議族是一種分層的協(xié)議,傳輸層、網(wǎng)絡(luò)層、鏈路層,都有自己的協(xié)議頭,因此 TCP/IP 協(xié)議棧對(duì)于數(shù)據(jù)包的處理是比較復(fù)雜的。為了提高處理效率,避免數(shù)據(jù)移動(dòng)、拷貝,sk_buffer 在對(duì)數(shù)據(jù) buffer 管理的時(shí)候,在 packet data 之上和之下,都預(yù)留了空間。如果需要增加協(xié)議頭,只需要從 head room 中拿出一塊空間即可,而如果需要增加數(shù)據(jù),則可以從 tail room 中獲得空間。這樣,整個(gè)內(nèi)存只分配一次空間,此后 協(xié)議的處理,只需要挪動(dòng)指針。5.?? Sk_buffer?對(duì)內(nèi)存的管理
我們以構(gòu)造一個(gè)用于發(fā)送的數(shù)據(jù)包的過(guò)程,來(lái)理解 sk_buffer 是如何管理內(nèi)存的。5.1.????????????????????構(gòu)造Skb_buffer
alloc_skb() 用于構(gòu)造 skb_buffer,它需要一個(gè)參數(shù),指定了存放 packet 的空間的大小。 構(gòu)造時(shí),不僅需要?jiǎng)?chuàng)建 skb_buffer 結(jié)構(gòu)本身,還需要分配空間用于保存 packet。 skb = alloc_skb(len, GFP_KERNEL); 上圖是在調(diào)用完 alloc_skb() 后的情況: head, data, tail 指向 buffer 開(kāi)始,end 指向 buffer 結(jié)束,整個(gè) buffer 都被當(dāng)作 tail room。 Sk_buffer 當(dāng)前的數(shù)據(jù)長(zhǎng)度是0。5.2.????????????????????為?protocol header?留出空間
通常,當(dāng)構(gòu)造一個(gè)用于發(fā)送的數(shù)據(jù)包時(shí),需要留出足夠的空間給協(xié)議頭,包括 TCP/UDP header, IP header 和鏈路層頭。 對(duì) IPv4 數(shù)據(jù)包,可以從 sk->sk_prot->max_header 知道協(xié)議頭的最大長(zhǎng)度。 skb_reserve(skb, header_len); 上 圖是調(diào)用 skb_reserver() 后的情況5.3.????????????????????將用戶空間數(shù)據(jù)拷貝到?buffer?中
首先通過(guò) skb_put(skb, user_data_len) ,從 tail room 中留出用于保存數(shù)據(jù)的空間 然后通過(guò)csum_and_copy_from_user() 將數(shù)據(jù)從用戶空間拷貝到這個(gè)空間中。5.4.????????????????????構(gòu)造UDP協(xié)議頭
通過(guò) skb_push() ,向 head room 中要一塊空間 然后在此空間中構(gòu)造 UDP 頭。5.5.????????????????????構(gòu)造?IP?頭
通過(guò) skb_push() ,向 head room 中要一塊空間 然后在此空間中構(gòu)造 IP 頭。?
6.?? Sk_buffer?的秘密 當(dāng)調(diào)用 alloc_skb() 構(gòu)造 SKB 和 data buffer時(shí),需要的 buffer 大小是這樣計(jì)算的: data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask); 除了指定的 size 以外,還包括一個(gè) struct skb_shared_info 結(jié)構(gòu)的空間大小。也就是說(shuō),當(dāng)調(diào)用 alloc_skb(size) 要求分配 size 大小的 buffer 的時(shí)候,同時(shí)還創(chuàng)建了一個(gè) skb_shared_info 。 這個(gè)結(jié)構(gòu)定義如下: struct skb_shared_info { atomic_t??????????? dataref; unsigned int?????? nr_frags; unsigned short?? tso_size; unsigned short?? tso_segs; struct sk_buff???? *frag_list; skb_frag_t???????? frags[MAX_SKB_FRAGS]; }; 我們只要把 end 從 char* 轉(zhuǎn)換成skb_shared_info* ,就能訪問(wèn)到這個(gè)結(jié)構(gòu) Linux 提供一個(gè)宏來(lái)做這種轉(zhuǎn)換: #define skb_shinfo(SKB)???????????? ((struct skb_shared_info *)((SKB)->end)) 那么,這個(gè)隱藏的結(jié)構(gòu)用意何在? 它至少有兩個(gè)目的: 1、??用于管理 paged data 2、??用于管理分片 接下來(lái)分別研究 sk_buffer 對(duì)paged data 和分片的處理。7.???對(duì)?paged data?的處理
? 某些情況下,希望能將保存在文件中的數(shù)據(jù),通過(guò) socket 直接發(fā)送出去,這樣,避免了把數(shù)據(jù)先從文件拷貝到緩沖區(qū),從而提高了效率。 Linux 采用一種 “paged data” 的技術(shù),來(lái)提供這種支持。這種技術(shù)將文件中的數(shù)據(jù)直接被映射為多個(gè) page。 ? Linux 用 struct skb_frag_strut 來(lái)管理這種 page: ? typedef struct skb_frag_struct skb_frag_t; ? struct skb_frag_struct { ??????? struct page *page; ??????? __u16 page_offset; ??????? __u16 size; }; ? 并在shared info 中,用數(shù)組 frags[] 來(lái)管理這些結(jié)構(gòu)。 ? ? 如此一來(lái),sk_buffer 就不僅管理著一個(gè) buffer 空間的數(shù)據(jù)了,它還可能通過(guò) share info 結(jié)構(gòu)管理一組保存在 page 中的數(shù)據(jù)。 在采用 “paged data” 時(shí),data_len 成員派上了用場(chǎng),它表示有多少數(shù)據(jù)在 page 中。因此, 如果 data_len 非0,這個(gè) sk_buffer 管理的數(shù)據(jù)就是“非線性”的。 Skb->len – skb->data_len 就是非 paged 數(shù)據(jù)的長(zhǎng)度。 在有 “paged data” 情況下, skb_put()就無(wú)法使用了,必須使用 pskb_put() 。。。 ? ? ? ? ? ? ? ? ? ? ?8.???對(duì)分片的處理
9.?? SKB?的管理函數(shù)
9.1.??????????????????? Data Buffer?的基本管理函數(shù)
·??????????????unsigned char *skb_put(struct sk_buff *skb, unsigned int len) “推”入數(shù)據(jù) 在 buffer 的結(jié)束位置,增加數(shù)據(jù),len是要增加的長(zhǎng)度。 這個(gè)函數(shù)有兩個(gè)限制,需要調(diào)用者自己注意,否則后果由調(diào)用者負(fù)責(zé) 1)、不能用于 “paged data” 的情況 這要求調(diào)用者自己判斷是否為 “paged data” 情況 2)、增加新數(shù)據(jù)后,長(zhǎng)度不能超過(guò) buffer 的實(shí)際大小。 這要求調(diào)用者自己計(jì)算能增加的數(shù)據(jù)大小 ·??????????????unsigned char *skb_push(struct sk_buff *skb, unsigned int len) “壓”入數(shù)據(jù) 從 buffer 起始位置,增加數(shù)據(jù),len 是要增加的長(zhǎng)度。 實(shí)際就是將新的數(shù)據(jù)“壓”入到 head room 中 ·??????????????unsigned char *skb_pull(struct sk_buff *skb, unsigned int len) “拉”走數(shù)據(jù) 從 buffer 起始位置,去除數(shù)據(jù), len 是要去除的長(zhǎng)度。 如果 len 大于 skb->len,那么,什么也不做。 在處理接收到的 packet 過(guò)程中,通常要通過(guò) skb_pull() 將最外層的協(xié)議頭去掉;例如當(dāng)網(wǎng)絡(luò)層處理完畢后,就需要將網(wǎng)絡(luò)層的 header 去掉,進(jìn)一步交給傳輸層處理。 ·??????????????void skb_trim(struct sk_buff *skb, unsigned int len) 調(diào)整 buffer 的大小,len 是調(diào)整后的大小。 如果 len 比 buffer 小,則不做調(diào)整。 因此,實(shí)際是將 buffer 底部的數(shù)據(jù)去掉。 對(duì)于沒(méi)有 paged data 的情況,很好處理; 但是有 paged data 情況下,則需要調(diào)用 __pskb_trim() 來(lái)進(jìn)行處理。9.2.??????????????????? “Paged data”?和?分片的管理函數(shù)
·??????????????char *pskb_pull(struct sk_buff *skb, unsigned int len) “拉“走數(shù)據(jù) 如果 len 大于線性 buffer 中的數(shù)據(jù)長(zhǎng)度,則調(diào)用__pskb_pull_tail()? 進(jìn)行處理。 (Q:最后, return skb->data += len;? 是否會(huì)導(dǎo)致 skb->data 超出了鏈頭范圍?) ·??????????????int pskb_may_pull(struct sk_buff *skb, unsigned int len) 在調(diào)用 skb_pull() 去掉外層協(xié)議頭之前,通常先調(diào)用此函數(shù)判斷一下是否有足夠的數(shù)據(jù)用于“pull”。 如果線性 buffer足夠 pull,則返回1; 如果需要 pull 的數(shù)據(jù)超過(guò) skb->len,則返回0; 最后,調(diào)用__pskb_pull_tail() 來(lái)檢查 page buffer 有沒(méi)有足夠的數(shù)據(jù)用于 pull。 ·??????????????int pskb_trim(struct sk_buff *skb, unsigned int len) 將 Data Buffer 的數(shù)據(jù)長(zhǎng)度調(diào)整為 len 在沒(méi)有 page buffer 情況下,等同于 skb_trim(); 在有 page buffer 情況下,需要調(diào)用___pskb_trim() 進(jìn)一步處理。 ·??????????????int skb_linearize(struct sk_buff *skb, gfp_t gfp) ·??????????????struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask) ‘clone’ 一個(gè)新的 SKB。新的 SKB 和原有的 SKB 結(jié)構(gòu)基本一樣,區(qū)別在于: 1)、它們共享同一個(gè) Data Buffer 2)、它們的 cloned 標(biāo)志都設(shè)為1 3)、新的 SKB 的 sk 設(shè)置為空 (Q:在什么情況下用到克隆技術(shù)?) ·??????????????struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask) ·??????????????struct sk_buff *pskb_copy(struct sk_buff *skb, gfp_t gfp_mask) ·??????????????struct sk_buff *skb_pad(struct sk_buff *skb, int pad) ·??????????????void skb_clone_fraglist(struct sk_buff *skb) ·??????????????void skb_drop_fraglist(struct sk_buff *skb) ·??????????????void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) ·??????????????pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, gfp_t gfp_mask) ·??????????????int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len) ·??????????????int skb_store_bits(const struct sk_buff *skb, int offset, void *from, int len) ·??????????????struct sk_buff *skb_dequeue(struct sk_buff_head *list) ·??????????????struct sk_buff *skb_dequeue(struct sk_buff_head *list) ·??????????????void skb_queue_purge(struct sk_buff_head *list) ·??????????????void skb_queue_purge(struct sk_buff_head *list) ·??????????????void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk) ·??????????????void skb_unlink(struct sk_buff *skb, struct sk_buff_head *list) ·??????????????void skb_append(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head *list) ·??????????????void skb_insert(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head *list) ·??????????????int skb_add_data(struct sk_buff *skb, char __user *from, int copy) ·??????????????struct sk_buff *skb_padto(struct sk_buff *skb, unsigned int len) ·??????????????int skb_cow(struct sk_buff *skb, unsigned int headroom) 這個(gè)函數(shù)要對(duì) SKB 的 header room 調(diào)整,調(diào)整后的 header room 大小是 headroom. 如果 headroom 長(zhǎng)度超過(guò)當(dāng)前header room 的大小,或者 SKB 被 clone 過(guò),那么需要調(diào)整,方法是: 分配一塊新的 data buffer 空間,SKB 使用新的 data buffer 空間,而原有空間的引用計(jì)數(shù)減1。在沒(méi)有其它使用者的情況下,原有空間被釋放。 ·??????????????struct sk_buff *dev_alloc_skb(unsigned int length) ·??????????????void skb_orphan(struct sk_buff *skb) ·??????????????void skb_reserve(struct sk_buff *skb, unsigned int len) ·??????????????int skb_tailroom(const struct sk_buff *skb) ·??????????????int skb_headroom(const struct sk_buff *skb) ·??????????????int skb_pagelen(const struct sk_buff *skb) ·??????????????int skb_headlen(const struct sk_buff *skb) ·??????????????int skb_is_nonlinear(const struct sk_buff *skb) ·??????????????struct sk_buff *skb_share_check(struct sk_buff *skb, gfp_t pri) 如果skb 只有一個(gè)引用者,直接返回 skb 否則 clone 一個(gè) SKB,將原來(lái)的 skb->users 減1,返回新的 SKB 需要特別留意 pskb_pull() 和 pskb_may_pull() 是如何被使用的: 1)、在接收數(shù)據(jù)的時(shí)候,大量使用 pskb_may_pull(),其主要目的是判斷 SKB 中有沒(méi)有足夠的數(shù)據(jù),例如在 ip_rcv() 中: if (!pskb_may_pull(skb, sizeof(struct iphdr))) goto inhdr_error; iph = skb->nh.iph; 它的目的是拿到 IP header,但取之前,先通過(guò) pskb_may_pull() 判斷一下有沒(méi)有足夠一個(gè) IP header 的數(shù)據(jù)。 2)、當(dāng)我們構(gòu)造 IP 分組的時(shí)候,對(duì)于數(shù)據(jù)部分,通過(guò) put向下擴(kuò)展空間(如果一個(gè)sk_buffer 不夠用怎么分片?);對(duì)于 傳輸層、網(wǎng)絡(luò)層、鏈路層的頭,通過(guò) push 向上擴(kuò)展空間; 3)、當(dāng)我們解析 IP 分組的時(shí)候,通過(guò) pull(),從頭開(kāi)始,向下壓縮空間。 因此,put 和 push 主要用在發(fā)送數(shù)據(jù)包的時(shí)候;而pull 主要用在接收數(shù)據(jù)包的時(shí)候。10.?????????????????????各種?header
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; -------------------------------------------- 其中的第五部分正是我需要的,在此向上面朋友的辛苦表示感謝。轉(zhuǎn)載于:https://www.cnblogs.com/LiuYanYGZ/p/7566296.html
總結(jié)
以上是生活随笔為你收集整理的skb详细解析【转】的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: BZOJ 1685 [Usaco2005
- 下一篇: UWP入门(二) -- 基础笔记