不同情況下構造skb資料報的實現

2021-06-19 06:33:52 字數 4742 閱讀 2640

在我這個網路介面的程式中(can0),其實難點就是怎樣組包。怎樣在原來資料報的基礎加上自己的資料,怎樣構造ip頭,怎樣構造udp頭。

除錯了兩個星期,終於是調通了,在這個過程中,通過看核心源**和自己組包的嘗試,大概對組包的方法有了些了解,記錄在此,留做備忘,也希望能給需要這方面資訊的朋友一點幫助吧。

1,正常網絡卡收到資料報後的情況:

她的工作就是剝離mac頭,然後給一些字段賦值,最後呼叫netif_rx將剝離mac頭後的資料報(比如ip資料報)傳送到上層協議。由協議棧處理。在此以ldd3中的snull為例,雖然snull跟硬體不相關,但這個過程都是類似的。

struct sk_buff *skb;

struct snull_priv *priv = netdev_priv(dev)

;

skb = dev_alloc_skb(pkt-

>datalen + 2);if

(!skb)

skb_reserve(skb, 2)

;/* align ip on 16b boundary */

memcpy

(skb_put(skb, pkt-

>datalen)

, pkt-

>data, pkt-

>datalen)

;/* write metadata, and then pass to the receive level */

skb-

>dev = dev;

skb-

>protocol = eth_type_trans(skb, dev)

;skb-

>ip_summed = checksum_unnecessary;

/* don't check it */

priv-

>stats.rx_packets++;

priv-

>stats.rx_bytes +

= pkt-

>datalen;

netif_rx(skb)

;

注意:上面**中紅色放大的地方是重要的。

因為此刻收到的資料報的格式如下:mac+ip+udp/udp+data

這時候的處理就是剝離mac頭,然後需要更新的一些域值。這些都是在函式eth_type_trans函式裡做的。需要注意的是,skb->dev = dev;這條語句是很重要的,如果沒有此語句,將會導致系統錯誤而宕機(至少在我的板子上是這樣的)。

注意:eth_type_trans()函式主要賦值的是:

skb-

>mac.raw,

skb-

>protocol和

skb-

>pkt_type。見下面的**有無mac頭的情況。

2,完全從乙個字串開始構造乙個新的skb資料報。

以前只是看過如何修改資料報,自己構造資料報,這還是頭一次,剛開始確實給我難住了,來來經過看核心**和自己摸索,我自己寫的**如下:

/*假設:data是乙個指向字串的指標,data_len是data的長度*/

struct ipv6hdr *ipv6h;

struct udphdr *udph;

struct sk__buff * new_skb;

int length = data_len +

sizeof

(struct ipv6hdr)

+sizeof

(udphdr)

;new_skb = dev_alloc_skb(length);if

(!new_skb)

skb_reserve(new_skb,length)

;memcpy

(skb_push(new_skb,data_len)

,data,data_len)

;new_skb-

>h.uh = udph =

(struct udphdr *

)skb_push(new_skb,

sizeof

(struct udphdr));

memcpy

(udph,

&udph_tmp,

sizeof

(struct udphdr));

//注意,此刻我的udph_tmp是在另乙個過程中截獲的資料報的udp頭,如果完全是自己構造資料報,則需要自己填充udp資料頭中的字段。

udph-

>len =..

....

....

....

;//此處需要給udph->len賦值。注意udph->len是__u16的。儲存時是高低位互換的,所以你應該先將你要更新的數字編成16進製制的數,然後高低位互換,在賦值給udh->len。

udplen = new_skb-

>len;

new_skb-

>nh.ipv6h = ipv6h =

(struct ipv6hdr *

)skb_push(new_skb,

sizeof

(struct ipv6hdr));

memcpy

(ipv6h,

&ipv6h_tmp,

sizeof

(struct ipv6hdr));

//同udp頭注釋。

ipb6h-

>payload_len =..

....

....; //此處同udph->len.需要注意的是,此處所指的長度並不包括ipv6頭的長度,而是去掉ipv6頭後的長度。

udph-

>check = 0;

udph-

>check = csum_ipv6_magic(

&ipv6h-

>saddr,

&ipv6h-

>daddr, udplen,

ipproto_udp

,csum_partial(

(char

*)udph, udplen, 0));

///注意,如果是ipv4,則還需要計算ip校驗和,但此處是ipv6,不用計算ip檢驗和,所以此處沒有ipv6頭的校驗。//

new_skb-

>mac.raw = new_skb-

>data;

//因為無mac頭

new_skb-

>protocol =

htons

(eth_p_ipv6)

;//表明包是ipv6資料報

new_skb-

>pkt_type = packet_host;

//表明是發往本機的包

new_skb-

>dev =

&can_control;

//此處很重要,如果沒有這條語句,則核心跑死。至少在我板子上是這樣的。can_control是我的net_device結構體變數。

netif_rx(new_skb)

;

3,當需要改變原有skb的資料域的情況。

此時,有兩種辦法:

可以先判斷skb的tailroom,如果空間夠大,則我們可以把需要新增的資料放在skb的tailroom裡。如果tailroom不夠大,則需要呼叫skb_copy_expand函式來擴充tailroom或者headroom。

if

(skb_tailroom(skb)

< 16)

else

memcpy

(skb_put(skb,16)

,ipbuf,16)

;//ipbuf為要加到skb後面的字串

udplen = skb-

>len -

sizeof

(struct ipv6hdr)

;udph-

>len +

= 0x1000;

//換成十進位制為 + 16

ipv6h-

>payload_len +

= 0x1000;

udph-

>check = 0;

udph-

>check = csum_ipv6_magic(

&ipv6h-

>saddr,

&ipv6h-

>daddr, udplen,

ipproto_udp

,csum_partial(

(char

*)udph,udplen,0));

skb-

>mac.raw = new_skb-

>data;

//因為無mac頭

skb-

>protocol =

htons

(eth_p_ipv6)

;//表明包是ipv6資料報

skb-

>pkt_type = packet_host;

//表明是發往本機的包

skb-

>dev =

&can_control;

//此處很重要,如果沒有這條語句,則核心跑死。至少在我板子上是這樣的。can_control是我的net_device結構體變數。

netif_rx(skb);}

注意:當呼叫skb_copy_expand或者修改了skb的資料域後,一定要更新udph->len和ipv6h->payload_len。否則上層應用(比如udp套接字)收到的資料報還是原來的資料報而不是修改後的資料報,因為udph->len的原因。

這三種情況下構造資料報的方法弄明白了,估計以後在要運算元據包應該就不會被難住了。

不同情況下this的指向以及改變this指向的方法

1.call 有多個引數,第乙個引數是改變的this指向,剩餘的引數是實參 fn.call fn2,12,5,8 在fn中 this代表的是fn2 12,5,8 代表是fn的實參 有兩個引數,第乙個引數是改變的this的指向,第二個引數,形式是陣列的形式,放的是函式的實參 在fn中,this指的是f...

C 類模板在不同情況下的使用

4.類模板派生普通類 5.類模板派生類模板 6.類模板類內實現 7.類模板類外實現 8.模板類碰到友元函式 類模板和函式模板的定義和使用類似。有時,有兩個或多個類,其功能是相同的,僅僅是資料型別不同。類模板可以有預設引數,比如 template typename nametype,typename ...

了解不同情況下的static關鍵字

在全域性變數前加上關鍵字static,全域性變數就定義成乙個全域性靜態變數 靜態儲存區,在整個程式執行期間一直存在。初始化 未經初始化的全域性靜態變數會被自動初始化為0 自動物件的值是任意的,除非他被顯示初始化 作用域 全域性靜態變數在宣告他的檔案之外是不可見的,準確地說是從定義之處開始,到檔案結尾...