在IP头、UDP头、TCP头以及ICMP头中都有校验相关的字段,用于检测数据传输过程中是否出错了。
IP头校验只校验头本身,ICMP头校验包含ICMP头以及负载,而UDP、TCP头校验不仅包含相应的头和负载,同时包含了伪头部:
-
/**
-
* csum_tcpup_magic - Compute an IPv4 pseudo header checksum.
-
* @saddr: source address
-
* @daddr: destination address
-
* @len: length of packet
-
* @proto: ip protocol of packet
-
* @sum: initial sum to be added in (32bit unfolded)
-
*
-
* Returns the 16bit pseudo header checksum the input data already
-
* complemented and ready to be filled in.
-
*/
-
static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
-
unsigned short len,
-
unsigned short proto, __wsum sum)
-
{
-
return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
-
}
先看接收部分
ip_rcv函数中涉及ip头的校验过程:
-
static inline __sum16 ip_fast_csum(const void *iph, unsigned int ihl)
-
{
-
unsigned int sum;
-
-
asm(" movl (%1), %0\n"
-
" subl $4, %2\n"
-
" jbe 2f\n"
-
" addl 4(%1), %0\n"
-
" adcl 8(%1), %0\n"
-
" adcl 12(%1), %0\n"
-
"1: adcl 16(%1), %0\n"
-
" lea 4(%1), %1\n"
-
" decl %2\n"
-
" jne 1b\n"
-
" adcl $0, %0\n"
-
" movl %0, %2\n"
-
" shrl $16, %0\n"
-
" addw %w2, %w0\n"
-
" adcl $0, %0\n"
-
" notl %0\n"
-
"2:"
-
/* Since the input registers which are loaded with iph and ihl
-
are modified, we must also specify them as outputs, or gcc
-
will assume they contain their original values. */
-
: "=r" (sum), "=r" (iph), "=r" (ihl)
-
: "1" (iph), "2" (ihl)
-
: "memory");
-
return (__force __sum16)sum;
-
}
__udp4_lib_rcv函数中涉及udp的校验过程,如果硬件设置CHECKSUM_UNNECESSARY的话,则完全略过,否则先生成伪头部分的校验:
-
static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh,
-
int proto)
-
{
-
const struct iphdr *iph;
-
int err;
-
-
UDP_SKB_CB(skb)->partial_cov = 0;
-
UDP_SKB_CB(skb)->cscov = skb->len;
-
-
if (proto == IPPROTO_UDPLITE) {
-
err = udplite_checksum_init(skb, uh);
-
if (err)
-
return err;
-
}
-
-
iph = ip_hdr(skb);
-
if (uh->check == 0) { //可以看到udp是支持不校验的
-
skb->ip_summed = CHECKSUM_UNNECESSARY;
-
} else if (skb->ip_summed == CHECKSUM_COMPLETE) { //CHECKSUM_COMPLETE表示硬件已经校验了L4的头和负载,赋值在csum中
-
if (!csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, //因此上层只需再加上伪头
-
proto, skb->csum))
-
skb->ip_summed = CHECKSUM_UNNECESSARY;
-
}
-
if (!skb_csum_unnecessary(skb)) //走到这说明要么硬件没有校验功能,或者前面计算出错了,导致没有赋值CHECKSUM_UNNECESSARY
-
skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
-
skb->len, proto, 0);
-
/* Probably, we should checksum udp header (it should be in cache
-
* in any case) and data in tiny packets (< rx copybreak).
-
*/
-
-
return 0;
-
}
后续流程:
-
static inline int udp_lib_checksum_complete(struct sk_buff *skb)
-
{
-
return !skb_csum_unnecessary(skb) &&
-
__udp_lib_checksum_complete(skb);
-
}
-
__sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len)
-
{
-
__sum16 sum;
-
-
sum = csum_fold(skb_checksum(skb, 0, len, skb->csum)); //sum不等于0的话说明数据出错了
-
if (likely(!sum)) {
-
if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE)) //正常走这条流程是由于硬件没有校验功能,如果是CHECKSUM_COMPLETE说明
-
netdev_rx_csum_fault(skb->dev); //硬件计算了但是出错了
-
skb->ip_summed = CHECKSUM_UNNECESSARY;
-
}
-
return sum;
-
}
tcp_v4_rcv中涉及TCP头的校验,流程类似:
-
static __sum16 tcp_v4_checksum_init(struct sk_buff *skb)
-
{
-
const struct iphdr *iph = ip_hdr(skb);
-
-
if (skb->ip_summed == CHECKSUM_COMPLETE) { //硬件已经计算了L4头和负载,软件只需增加伪头
-
if (!tcp_v4_check(skb->len, iph->saddr,
-
iph->daddr, skb->csum)) {
-
skb->ip_summed = CHECKSUM_UNNECESSARY;
-
return 0;
-
}
-
}
-
-
skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, //硬件没有校验功能,先计算伪头
-
skb->len, IPPROTO_TCP, 0);
-
-
if (skb->len <= 76) { //如果包比较小的话的,直接全部校验
-
return __skb_checksum_complete(skb);
-
}
-
return 0;
-
}
-
static inline __sum16 __tcp_checksum_complete(struct sk_buff *skb)
-
{
-
return __skb_checksum_complete(skb);
-
}
-
-
static inline int tcp_checksum_complete(struct sk_buff *skb)
-
{
-
return !skb_csum_unnecessary(skb) &&
-
__tcp_checksum_complete(skb);
-
}
icmp_rcv中涉及ICMP头的校验,由于没有伪头的部分,因此比较简单直接:
-
switch (skb->ip_summed) {
-
case CHECKSUM_COMPLETE:
-
if (!csum_fold(skb->csum))
-
break;
-
/* fall through */
-
case CHECKSUM_NONE:
-
skb->csum = 0;
-
if (__skb_checksum_complete(skb))
-
goto error;
-
}
阅读(2001) | 评论(0) | 转发(0) |