linux内核udp校验和计算函数,Linux 内核IP和UDP检验和计算
·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);
{
iph->check = 0;
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
}
·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 {
__wsum?????????????????????? csum;
struct {
__u16? csum_start;
__u16? csum_offset;
};
};
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)
{
const struct iphdr *iph = ip_hdr(skb);
if (uh->check == 0) {
skb->ip_summed = CHECKSUM_UNNECESSARY;
} else if (skb->ip_summed == CHECKSUM_COMPLETE) {
if (!csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len,
proto, skb->csum))
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
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 !skb_csum_unnecessary(skb) &&
{
sum = csum_fold(skb_checksum(skb, 0, len, skb->csum));
if (likely(!sum)) {
if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE))
netdev_rx_csum_fault(skb->dev);
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
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層頭的校驗值。
(對于支持scatter and gather的報文必須要傳輸層頭在線性空間才能使用硬件checksum功能)
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)
{
/* If packet is not checksummed and device does not
* support checksumming for this protocol, complete
* checksumming here.
*/
if (skb->ip_summed == CHECKSUM_PARTIAL) {
skb_set_transport_header(skb,
skb_checksum_start_offset(skb));
if (!(features & NETIF_F_ALL_CSUM) &&
skb_checksum_help(skb))
goto out_kfree_skb;
}
}
總結
以上是生活随笔為你收集整理的linux内核udp校验和计算函数,Linux 内核IP和UDP检验和计算的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c语言宿舍管理设计报告,C语言课程设计—
- 下一篇: linux shell sql赋值,Li