·ip checksum a.
接收報文
struct iphdr *iph = ip_hdr(skb);
if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
goto checksum_error;
b.傳送報文
ip_send_check(iph);
·udp checksum a.
網絡卡裝置屬性
#define netif_f_ip_csum 2 /* 基於ipv4的l4層
checksum. */
#define netif_f_no_csum 4 /* 裝置可靠不需要l4層
checksum. loopack. */
#define netif_f_hw_csum 8 /* 基於所有協議的l4層
checksum*/
#define netif_f_ipv6_csum 16 /* 基於ipv6的l4層
checksum*/
通過ethtool可以檢視網絡卡是否支援硬體checksum
ethtool -k eth0
offload parameters for eth0:
cannot get device rx csum settings: operation not supported
cannot get device udp large send offload settings: operation not supported
rx-checksumming: off
tx-checksumming: on
scatter-gather: on
tcp segmentation offload: off
udp fragmentation offload: off
generic segmentation offload: off
tx-checksumming: on 表明支援傳送hardware checksum。 b.
linux udp checksum
資料結構
union ;
};1)
skb->csum和skb->ip_summed這兩個域也是與4層校驗相關的,這兩個域的含義依賴於skb表示的是乙個輸入包還是乙個輸出包。 2)
當網絡卡裝置能提供硬體checksum並且作為輸出包的時候,表示為skb-> csum_start和skb-> csum_offset
csum_start: offset from skb->head where checksumming should start
csum_offset: offset from csum_start where checksum should be stored
當資料報是乙個輸入包時
skb->ip_summed表示的是四層校驗的狀態,下面的幾個巨集定義表示了裝置驅動傳遞給4層的一些資訊。
#define checksum_none 0
#define checksum_unnecessary 1
#define checksum_complete 2
skb->csum:存放硬體或者軟體計算的payload的checksum不包括偽頭,但是是否有意義由skb->ip_summed的值決定。
checksum_none表示csum域中的校驗值是無意義的,需要l4層自己校驗payload和偽頭。有可能是硬體檢驗出錯或者硬體沒有校驗功能,協議棧軟體更改如pskb_trim_rcsum函式。
checksum_unnecessary表示網絡卡或者協議棧已經計算和驗證了l4層的頭和校驗值。也就是計算了tcp udp的偽頭。還有一種情況就是回環,因為在回環中錯誤發生的概率太低了,因此就不需要計算校驗來節省cpu事件。
checksum_complete表示網絡卡已經計算了l4層payload的校驗,並且csum已經被賦值,此時l4層的接收者只需要加偽頭並驗證校驗結果。 1)
在l4層發現如果udp->check位段被設為0,那麼skb->ip_summed直接設為checksum_unnecessary,放行該報文。
2) 如果skb->ip_summed為checksum_complete,則把skb->csum加上偽頭進行校驗,成功則將skb->ip_summed設為checksum_unnecessary,
放行該資料報。
3) 通過上述後skb->ip_summed還不是checksum_unnecessary,那麼重新計算偽頭賦給skb->csum。
4) 將還不是checksum_unnecessary的資料報文的payload加上skb->csum進行checksum計算,成功將設為checksum_unnecessary並放行,失敗則丟棄。
udp4_csum_init(skb, uh, proto)
else if (skb->ip_summed == checksum_complete)
if (!skb_csum_unnecessary(skb))
skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
skb->len, proto, 0); }
if (udp_lib_checksum_complete(skb))
goto csum_error;
static inline int udp_lib_checksum_complete(struct sk_buff *skb)
return sum; }
} 當資料報是輸出包時
skb->csum表示為csum_start和csum_offset,它表示硬體網絡卡存放將要計算的校驗值的位址,和最後填充的便宜。這個域在輸出包時使用,只在校驗值在硬體計算的情況下才對於網絡卡真正有意義。硬體checksum功能只能用於非分片報文。
而此時ip_summed可以被設定的值有下面兩種:
#define checksum_none 0
#define checksum_partial 3
checksum_none 表示協議棧計算好了校驗值,裝置不需要做任何事。checksum_partial表示協議棧算好了偽頭需要硬體計算payload checksum。
1)對於udp socket開啟了udp_csum_noxmit /* udp csum disabled */
uh->check = 0;
skb->ip_summed = checksum_none;
2)軟體udp checksum
struct iphdr *iph = ip_hdr(skb);
struct udphdr *uh = udp_hdr(skb);
uh->check = 0;
skb->csum = csum_partial(skb_transport_header (skb), skb->len, 0);//skb->data指向傳輸層頭
uh->check = csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, iph->protocol, skb->csum);
skb->ip_summed = checksum_none;
//todo: scatter and gather
3) 硬體checksum: 只能是ip報文長度小於mtu的資料報(沒有分片的報文)。
checksum_partial表示使用硬體checksum ,l4層的偽頭的校驗已經完畢,並且已經加入uh->check欄位中,此時只需要裝置計算整個頭4層頭的校驗值。
uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, ipproto_udp, 0);
skb->csum_start = skb_transport_header (skb) - skb->head;
skb->csum_offset = offsetof(struct udphdr, check);
skb->ip_summed = checksum_partial;
d最後在dev_queue_xmit傳送的時候發現裝置不支援硬體checksum就會進行軟體計算
int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq)
}
Ip 首部檢驗和字段
ip icmp igmp tcp udp等協議的校驗和演算法都是相同的,演算法如下 在傳送資料時,為了計算數ip據報的校驗和。應該按如下步驟 1 把ip資料報的首部都置為0,包括校驗和字段。2 把首部看成以16位為單位的數字組成,依次進行二進位制反碼求和。3 把得到的結果存入校驗和字段中。在接收資料...
IP首部檢驗和計算
計算ip首部檢驗和 如上圖,計算?是多少?計算 從08 00之後開始計算到後面的08 00即 兩個位元組的相加 45 00 00 20 d5 56 00 00 80 01 de b7 45 5d c0 a8 00 dc 等於 3800f 然後溢位的再次加到後面即 800f 3 等於 8012 最後用...
UDP報文段和其檢驗和小結
如圖所示 tcp首部行開銷為20位元組而udp則為8位元組,所以udp分組首部開銷更小。文件為 rfc 1071 從上圖的報文段結構我們知道每個首部欄位為16位元。現在假設源埠號 目的埠號和長度字段三個16位元字分別為 源埠號 0110 0110 0110 0000 十進位制 26208 目的埠號 ...