在我這個網路介面的程式中(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 自動物件的值是任意的,除非他被顯示初始化 作用域 全域性靜態變數在宣告他的檔案之外是不可見的,準確地說是從定義之處開始,到檔案結尾...