Chinaunix首页 | 论坛 | 博客
  • 博客访问: 85703
  • 博文数量: 15
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 210
  • 用 户 组: 普通用户
  • 注册时间: 2014-01-05 15:27
文章分类

全部博文(15)

文章存档

2014年(15)

我的朋友

分类: LINUX

2014-04-12 22:12:18


在IP头、UDP头、TCP头以及ICMP头中都有校验相关的字段,用于检测数据传输过程中是否出错了。
IP头校验只校验头本身,ICMP头校验包含ICMP头以及负载,而UDP、TCP头校验不仅包含相应的头和负载,同时包含了伪头部:
  1. /**
  2.  * csum_tcpup_magic - Compute an IPv4 pseudo header checksum.
  3.  * @saddr: source address
  4.  * @daddr: destination address
  5.  * @len: length of packet
  6.  * @proto: ip protocol of packet
  7.  * @sum: initial sum to be added in (32bit unfolded)
  8.  *
  9.  * Returns the 16bit pseudo header checksum the input data already
  10.  * complemented and ready to be filled in.
  11.  */
  12. static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
  13.                     unsigned short len,
  14.                     unsigned short proto, __wsum sum)
  15. {
  16.     return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
  17. }
先看接收部分
ip_rcv函数中涉及ip头的校验过程:
  1. static inline __sum16 ip_fast_csum(const void *iph, unsigned int ihl)
  2. {
  3.     unsigned int sum;

  4.     asm(" movl (%1), %0\n"
  5.         " subl $4, %2\n"
  6.         " jbe 2f\n"
  7.         " addl 4(%1), %0\n"
  8.         " adcl 8(%1), %0\n"
  9.         " adcl 12(%1), %0\n"
  10.         "1: adcl 16(%1), %0\n"
  11.         " lea 4(%1), %1\n"
  12.         " decl %2\n"
  13.         " jne 1b\n"
  14.         " adcl $0, %0\n"
  15.         " movl %0, %2\n"
  16.         " shrl $16, %0\n"
  17.         " addw %w2, %w0\n"
  18.         " adcl $0, %0\n"
  19.         " notl %0\n"
  20.         "2:"
  21.     /* Since the input registers which are loaded with iph and ihl
  22.        are modified, we must also specify them as outputs, or gcc
  23.        will assume they contain their original values. */
  24.         : "=r" (sum), "=r" (iph), "=r" (ihl)
  25.         : "1" (iph), "2" (ihl)
  26.         : "memory");
  27.     return (__force __sum16)sum;
  28. }
__udp4_lib_rcv函数中涉及udp的校验过程,如果硬件设置CHECKSUM_UNNECESSARY的话,则完全略过,否则先生成伪头部分的校验:
  1. static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh,
  2.                  int proto)
  3. {
  4.     const struct iphdr *iph;
  5.     int err;

  6.     UDP_SKB_CB(skb)->partial_cov = 0;
  7.     UDP_SKB_CB(skb)->cscov = skb->len;

  8.     if (proto == IPPROTO_UDPLITE) {
  9.         err = udplite_checksum_init(skb, uh);
  10.         if (err)
  11.             return err;
  12.     }

  13.     iph = ip_hdr(skb);
  14.     if (uh->check == 0) { //可以看到udp是支持不校验的
  15.         skb->ip_summed = CHECKSUM_UNNECESSARY;
  16.     } else if (skb->ip_summed == CHECKSUM_COMPLETE) { //CHECKSUM_COMPLETE表示硬件已经校验了L4的头和负载,赋值在csum中
  17.         if (!csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, //因此上层只需再加上伪头
  18.                       proto, skb->csum))
  19.             skb->ip_summed = CHECKSUM_UNNECESSARY;
  20.     }
  21.     if (!skb_csum_unnecessary(skb))  //走到这说明要么硬件没有校验功能,或者前面计算出错了,导致没有赋值CHECKSUM_UNNECESSARY
  22.         skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
  23.                            skb->len, proto, 0);
  24.     /* Probably, we should checksum udp header (it should be in cache
  25.      * in any case) and data in tiny packets (< rx copybreak).
  26.      */

  27.     return 0;
  28. }
后续流程:
  1. static inline int udp_lib_checksum_complete(struct sk_buff *skb)
  2. {
  3.     return !skb_csum_unnecessary(skb) &&  
  4.         __udp_lib_checksum_complete(skb);
  5. }
  1. __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len)  
  2. {
  3.     __sum16 sum;

  4.     sum = csum_fold(skb_checksum(skb, 0, len, skb->csum));  //sum不等于0的话说明数据出错了
  5.     if (likely(!sum)) {
  6.         if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE)) //正常走这条流程是由于硬件没有校验功能,如果是CHECKSUM_COMPLETE说明
  7.             netdev_rx_csum_fault(skb->dev);                 //硬件计算了但是出错了
  8.         skb->ip_summed = CHECKSUM_UNNECESSARY;
  9.     }
  10.     return sum;
  11. }
tcp_v4_rcv中涉及TCP头的校验,流程类似:
  1. static __sum16 tcp_v4_checksum_init(struct sk_buff *skb)
  2. {
  3.     const struct iphdr *iph = ip_hdr(skb);

  4.     if (skb->ip_summed == CHECKSUM_COMPLETE) {  //硬件已经计算了L4头和负载,软件只需增加伪头
  5.         if (!tcp_v4_check(skb->len, iph->saddr,
  6.                   iph->daddr, skb->csum)) {
  7.             skb->ip_summed = CHECKSUM_UNNECESSARY;
  8.             return 0;
  9.         }
  10.     }

  11.     skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, //硬件没有校验功能,先计算伪头
  12.                        skb->len, IPPROTO_TCP, 0);

  13.     if (skb->len <= 76) { //如果包比较小的话的,直接全部校验
  14.         return __skb_checksum_complete(skb);
  15.     }
  16.     return 0;
  17. }
  1. static inline __sum16 __tcp_checksum_complete(struct sk_buff *skb)
  2. {
  3.     return __skb_checksum_complete(skb);
  4. }

  5. static inline int tcp_checksum_complete(struct sk_buff *skb)
  6. {
  7.     return !skb_csum_unnecessary(skb) &&
  8.         __tcp_checksum_complete(skb);
  9. }
icmp_rcv中涉及ICMP头的校验,由于没有伪头的部分,因此比较简单直接:
  1. switch (skb->ip_summed) {
  2.     case CHECKSUM_COMPLETE:
  3.         if (!csum_fold(skb->csum))
  4.             break;
  5.         /* fall through */
  6.     case CHECKSUM_NONE:
  7.         skb->csum = 0;
  8.         if (__skb_checksum_complete(skb))
  9.             goto error;
  10.     }







阅读(2001) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~