linux内核udp校验和计算函数,Linux 内核IP和UDP检验和计算
·IP checksum
a.接收報(bào)文
struct iphdr *iph = ip_hdr(skb);
if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
goto checksum_error;
b.發(fā)送報(bào)文
ip_send_check(iph);
{
iph->check = 0;
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
}
·UDP checksum
a.網(wǎng)卡設(shè)備屬性
#define?NETIF_F_IP_CSUM?????2???/*?基于IPv4的L4層checksum.?*/
#define?NETIF_F_NO_CSUM?????4???/*?設(shè)備可靠不需要L4層checksum.?loopack.?*/
#define?NETIF_F_HW_CSUM?????8???/*?基于所有協(xié)議的L4層checksum*/
#define?NETIF_F_IPV6_CSUM???16??/*?基于IPv6的L4層checksum*/
通過ethtool可以查看網(wǎng)卡是否支持硬件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??表明支持發(fā)送hardware checksum。
b.linux UDP checksum數(shù)據(jù)結(jié)構(gòu)
union {
__wsum?????????????????????? csum;
struct {
__u16? csum_start;
__u16? csum_offset;
};
};
1)skb->csum和skb->ip_summed這兩個(gè)域也是與4層校驗(yàn)相關(guān)的,這兩個(gè)域的含義依賴于skb表示的是一個(gè)輸入包還是一個(gè)輸出包。
2)當(dāng)網(wǎng)卡設(shè)備能提供硬件checksum并且作為輸出包的時(shí)候,表示為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
當(dāng)數(shù)據(jù)包是一個(gè)輸入包時(shí)
skb->ip_summed表示的是四層校驗(yàn)的狀態(tài),下面的幾個(gè)宏定義表示了設(shè)備驅(qū)動(dòng)傳遞給4層的一些信息。
#define?CHECKSUM_NONE?0
#define?CHECKSUM_UNNECESSARY?1
#define?CHECKSUM_COMPLETE?2
skb->csum:存放硬件或者軟件計(jì)算的payload的checksum不包括偽頭,但是是否有意義由skb->ip_summed的值決定。
CHECKSUM_NONE表示csum域中的校驗(yàn)值是無意義的,需要L4層自己校驗(yàn)payload和偽頭。有可能是硬件檢驗(yàn)出錯(cuò)或者硬件沒有校驗(yàn)功能,協(xié)議棧軟件更改如pskb_trim_rcsum函數(shù)。
CHECKSUM_UNNECESSARY表示網(wǎng)卡或者協(xié)議棧已經(jīng)計(jì)算和驗(yàn)證了L4層的頭和校驗(yàn)值。也就是計(jì)算了tcp udp的偽頭。還有一種情況就是回環(huán),因?yàn)樵诨丨h(huán)中錯(cuò)誤發(fā)生的概率太低了,因此就不需要計(jì)算校驗(yàn)來節(jié)省cpu事件。
CHECKSUM_COMPLETE表示網(wǎng)卡已經(jīng)計(jì)算了L4層payload的校驗(yàn),并且csum已經(jīng)被賦值,此時(shí)L4層的接收者只需要加偽頭并驗(yàn)證校驗(yàn)結(jié)果。
1)在L4層發(fā)現(xiàn)如果udp->check位段被設(shè)為0,那么skb->ip_summed直接設(shè)為CHECKSUM_UNNECESSARY,放行該報(bào)文。
2)? ?如果skb->ip_summed為CHECKSUM_COMPLETE,則把skb->csum加上偽頭進(jìn)行校驗(yàn),成功則將skb->ip_summed設(shè)為CHECKSUM_UNNECESSARY,放行該數(shù)據(jù)包。
3)???通過上述后skb->ip_summed還不是CHECKSUM_UNNECESSARY,那么重新計(jì)算偽頭賦給skb->csum。
4) ???將還不是CHECKSUM_UNNECESSARY的數(shù)據(jù)報(bào)文的payload加上skb->csum進(jìn)行checksum計(jì)算,成功將設(shè)為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;
}
}
當(dāng)數(shù)據(jù)包是輸出包時(shí)
skb->csum表示為csum_start和csum_offset,它表示硬件網(wǎng)卡存放將要計(jì)算的校驗(yàn)值的地址,和最后填充的便宜。這個(gè)域在輸出包時(shí)使用,只在校驗(yàn)值在硬件計(jì)算的情況下才對于網(wǎng)卡真正有意義。硬件checksum功能只能用于非分片報(bào)文。
而此時(shí)ip_summed可以被設(shè)置的值有下面兩種:
#define?CHECKSUM_NONE?????? 0
#define CHECKSUM_PARTIAL 3
CHECKSUM_NONE?表示協(xié)議棧計(jì)算好了校驗(yàn)值,設(shè)備不需要做任何事。CHECKSUM_PARTIAL表示協(xié)議棧算好了偽頭需要硬件計(jì)算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報(bào)文長度小于mtu的數(shù)據(jù)報(bào)(沒有分片的報(bào)文)。
CHECKSUM_PARTIAL表示使用硬件checksum?,L4層的偽頭的校驗(yàn)已經(jīng)完畢,并且已經(jīng)加入uh->check字段中,此時(shí)只需要設(shè)備計(jì)算整個(gè)頭4層頭的校驗(yàn)值。
(對于支持scatter and gather的報(bào)文必須要傳輸層頭在線性空間才能使用硬件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發(fā)送的時(shí)候發(fā)現(xiàn)設(shè)備不支持硬件checksum就會(huì)進(jìn)行軟件計(jì)算
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;
}
}
總結(jié)
以上是生活随笔為你收集整理的linux内核udp校验和计算函数,Linux 内核IP和UDP检验和计算的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c语言宿舍管理设计报告,C语言课程设计—
- 下一篇: java中catch ()_有关java