kernel 校验和实现

  1. Kernel checksum implementation
  2.  
  3. ) TCP包的错误检测使用16位累加和校验. 除了TCP包本身,
  4. TCP校验数据块还包括源IP地址,目的IP地址, TCP包长度, TCP协议号组成的12字节伪头标.
  5.  
  6. ) 校验和为16位字补码和, 数据块长度为奇数时, 数据块末尾添零处理.
  7. 校验和的计算与顺序无关, 可以从数据块开始计算, 也可以从未尾开始向前计算.
  8.  
  9. ) 为了提高计算效率, TCP包的校验和并不一次算出,
  10. 而是采用32位部分累加和(sk->csum)进行增量计算.
  11. csum_partial()用来计算数据块的32位部分累加和, 累加和可以用csum_fold()折叠为16位校验和.
  12. csum_partial_copy_nocheck()可在拷贝用户数据的同时计算出它的部分累加和.
  13.  
  14. ) 为了加快执行速度, csum_partial()将832位字分为一组用分立的指令进行32位累加,
  15. 这样可加长循环体中指令长度, 提高CPU指令流水线的效率.
  16.  
  17. ) 并不是所有的TCP包都必须校验, skb->ip_summed用来控制校验操作.
  18. 对于loopback设备的收发包, skb->ip_summed设为HECKSUM_UNNECESSARY, 忽略校验过程.
  19. 如果 skb->ip_summed == CHECKSUM_HW 说明TCP包本身的校验已经由硬件链路层完成,新kernel(2.6.)已经不使用这个选项。
  20.  
  21. 下面我们介绍具体实现,基于kernel-2.6., x86 architecture.
  22.  
  23. [函数实现]
  24. TCP包接收校验的初始化
  25. static __sum16 tcp_v4_checksum_init(struct sk_buff *skb)
  26. {
  27. const struct iphdr *iph = ip_hdr(skb);
  28. //如果TCP包本身的校验已经完成
  29. if (skb->ip_summed == CHECKSUM_COMPLETE) {
  30. if (!tcp_v4_check(skb->len, iph->saddr, iph->daddr, skb->csum)) { //附加伪头进行校验
  31. skb->ip_summed = CHECKSUM_UNNECESSARY;
  32. return ;
  33. }
  34. }
  35. //生成包含伪头的累加和
  36. skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, skb->len, IPPROTO_TCP, );
  37. if (skb->len <= ) {
  38. return __skb_checksum_complete(skb); //计算数据部分校验和
  39. }
  40. return ;
  41. }
  42. 附加伪头进行校验
  43. static inline __sum16 tcp_v4_check(int len, __be32 saddr, __be32 daddr, __wsum base)
  44. {
  45. return csum_tcpudp_magic(saddr, daddr, len, IPPROTO_TCP, base);
  46. }
  47. static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr, unsigned short len, unsigned short proto, __wsum sum)
  48. {
  49. return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
  50. }
  51. 生成包含伪头的累加和(源,目的,长度,协议号)
  52. static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len, unsigned short proto, __wsum sum)
  53. {
  54. __asm__(
  55. "addl %1, %0 ;\n" //addl 加法
  56. "adcl %2, %0 ;\n" //adcl 带进位的加法
  57. "adcl %3, %0 ;\n"
  58. "adcl $0, %0 ;\n" //如果有进位,进行累加
  59. : "=r" (sum)
  60. : "g" (daddr), "g"(saddr), "g"((len + proto) << ), ""(sum)
  61. );
  62.  
  63. return sum;
  64. }
  65. 32位累加和折叠成16位校验和
  66. static inline __sum16 csum_fold(__wsum sum)
  67. {
  68. __asm__(
  69. "addl %1, %0 ;\n"
  70. "adcl $0xffff, %0 ;\n"
  71. : "=r" (sum)
  72. : "r" ((__force u32)sum << ), "" ((__force u32)sum & 0xffff0000)
  73. );
  74. return (__force __sum16)(~(__force u32)sum >> );
  75. }
  76. 基于伪头累加和,完成全包校验
  77. static __inline__ int tcp_checksum_complete(struct sk_buff *skb)
  78. {
  79. return skb->ip_summed != CHECKSUM_UNNECESSARY && __tcp_checksum_complete(skb);
  80. }
  81. __sum16 __skb_checksum_complete(struct sk_buff *skb)
  82. {
  83. return __skb_checksum_complete_head(skb, skb->len);
  84. }
  85. __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len)
  86. {
  87. __sum16 sum;
  88.  
  89. sum = csum_fold(skb_checksum(skb, , len, skb->csum));
  90. if (likely(!sum)) {
  91. if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE))
  92. netdev_rx_csum_fault(skb->dev);
  93. skb->ip_summed = CHECKSUM_UNNECESSARY;
  94. }
  95. return sum;
  96. }
  97. __wsum skb_checksum(const struct sk_buff *skb, int offset, int len, __wsum csum)
  98. {
  99. int start = skb_headlen(skb);
  100. int i, copy = start - offset;
  101. int pos = ;
  102.  
  103. /* Checksum header. */
  104. if (copy > ) {
  105. if (copy > len)
  106. copy = len;
  107.  
  108. csum = csum_partial(skb->data + offset, copy, csum);
  109. if ((len -= copy) == )
  110. return csum;
  111.  
  112. offset += copy;
  113. pos = copy;
  114. }
  115. ......
  116. }
  117. 计算32位中间累加和
  118. unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum)
  119. {
  120. //arch/x86/lib/checksum_32.S 汇编文件
  121. }
  122. 基于TCP用户数据的中间累加和, 生成TCP包校验码
  123. void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb)
  124. {
  125. struct inet_sock *inet = inet_sk(sk);
  126. struct tcphdr *th = tcp_hdr(skb);
  127.  
  128. if (skb->ip_summed == CHECKSUM_PARTIAL) {
  129. th->check = ~tcp_v4_check(len, inet->saddr, inet->daddr, ); //附加伪头进行校验
  130. skb->csum_start = skb_transport_header(skb) - skb->head;
  131. skb->csum_offset = offsetof(struct tcphdr, check);
  132. } else {
  133. //完整的tcp校验和计算方法
  134. th->check = tcp_v4_check(len, inet->saddr, inet->daddr, csum_partial((char *)th, th->doff << , skb->csum));
  135. }
  136. }
  137. 在拷贝用户数据时同时计算累加和
  138. unsigned int csum_partial_copy_nocheck(const char *src, char *dst, int len, int sum)
  139. {
  140. return csum_partial_copy_generic(src, dst, len, sum, NULL, NULL); // arch/x86/lib/checksum_32.S
  141. }
  142. ip头校验和计算
  143. static inline __sum16 ip_fast_csum(const void *iph, unsigned int ihl)
  144. {
  145. unsigned int sum;
  146.  
  147. __asm__ __volatile__(
  148. "movl (%1), %0 ;\n"
  149. "subl $4, %2 ;\n"
  150. "jbe 2f ;\n"
  151. "addl 4(%1), %0 ;\n" //sum = sum + *(iph+4)
  152. "adcl 8(%1), %0 ;\n" //sum = sum + *(iph+8) + carry
  153. "adcl 12(%1), %0 ;\n" //sum = sum + *(iph+12) + carry
  154. "1: adcl 16(%1), %0 ;\n" //sum = sum + *(iph+16) + carry
  155. "lea 4(%1), %1 ;\n" //iph = iph + 4
  156. "decl %2 ;\n"
  157. "jne 1b ;\n"
  158. "adcl $0, %0 ;\n"
  159. "movl %0, %2 ;\n"
  160. "shrl $16, %0 ;\n"
  161. "addw %w2, %w0 ;\n"
  162. "adcl $0, %0 ;\n"
  163. "notl %0 ;\n"
  164. "2: ;\n"
  165. /* Since the input registers which are loaded with iph and ihl are modified, we must also specify them as outputs,
  166. or gcc will assume they contain their original values. */
  167. : "=r" (sum), "=r" (iph), "=r" (ihl)
  168. : "" (iph), "" (ihl)
  169. : "memory"
  170. );
  171. return (__force __sum16)sum;
  172. }
  173. 递减ip->ttl,更新校验和
  174. static inline int ip_decrease_ttl(struct iphdr *iph)
  175. {
  176. u32 check = (__force u32)iph->check;
  177. check += (__force u32)htons(0x0100);
  178. iph->check = (__force __sum16)(check + (check>=0xFFFF));
  179. return --iph->ttl;
  180. }
  181. static inline __wsum csum_add(__wsum csum, __wsum addend)
  182. {
  183. u32 res = (__force u32)csum;
  184. res += (__force u32)addend;
  185. return (__force __wsum)(res + (res < (__force u32)addend));
  186. }
  187.  
  188. static inline __wsum csum_sub(__wsum csum, __wsum addend)
  189. {
  190. return csum_add(csum, ~addend);
  191. }
  192.  
  193. static inline __wsum csum_block_add(__wsum csum, __wsum csum2, int offset)
  194. {
  195. u32 sum = (__force u32)csum2;
  196. if (offset & )
  197. sum = ((sum & 0xFF00FF)<<) + ((sum>>) & 0xFF00FF);
  198. return csum_add(csum, (__force __wsum)sum);
  199. }
  200. static inline __wsum csum_block_sub(__wsum csum, __wsum csum2, int offset)
  201. {
  202. u32 sum = (__force u32)csum2;
  203. if (offset & )
  204. sum = ((sum & 0xFF00FF)<<) + ((sum>>) & 0xFF00FF);
  205. return csum_sub(csum, (__force __wsum)sum);
  206. }
  207. [/函数实现]

kernel 校验和实现的更多相关文章

  1. MIT JOS学习笔记02:kernel 01(2016.10.28)

    未经许可谢绝以任何形式对本文内容进行转载! 在文章开头不得不说的是,因为这部分的代码需要仔细理清的东西太多,所以导致这篇分析显得很啰嗦,还请谅解. 我们在上一篇文章已经分析了Boot Loader的功 ...

  2. TCP校验和的原理和实现

        http://blog.csdn.net/zhangskd/article/details/11770647 分类: Linux TCP/IP Linux Kernel 2013-09-24 ...

  3. TCP Socket Establish;UDP Send Package Process In Kernel Sourcecode Learning

    目录 . 引言 . TCP握手流程 . TCP connect() API原理 . TCP listen() API原理 . UDP交互过程 . UDP send() API原理 . UDP bind ...

  4. Nand ECC校验和纠错原理及2.6.27内核ECC代码分析

    ECC的全称是Error Checking and Correction,是一种用于Nand的差错检测和修正算法.如果操作时序和电路稳定性不存在问题的话,NAND Flash出错的时候一般不会造成整个 ...

  5. 一步步学习操作系统(2)——在STM32上实现一个可动态加载kernel的"my-boot"

    如果要做嵌入式Linux,我们首先要在板子上烧写的往往不是kernel,而是u-boot,这时需要烧写工具帮忙.当u-boot烧写成功后,我们就可以用u-boot附带的网络功能来烧写kernel了.每 ...

  6. 解决wireshark抓包校验和和分片显示异常

    问题描述: 在使用wireshark抓取报文时,发现从10.81.2.92发过来的报文绝大部分标记为异常报文(开启IPv4和TCP checksum) 分析如下报文,发现http报文(即tcp pay ...

  7. 深入linux kernel内核配置选项

    ============================================================================== 深入linux kernel内核配置选项 ...

  8. virtio_net设备的校验和问题

    我们来看一个virtio_net设备的校验和配置: [root@10 ~]# ethtool -K eth0 tx-checksumming on //caq:大写的K用来调整feature [roo ...

  9. Linux 内核概述 - Linux Kernel

    Linux 内核学习笔记整理. Unix unix 已有40历史,但计算机科学家仍认为其是现存操作系统中最大和最优秀的系统,它已成为一种传奇的存在,历经时间的考验却依然声名不坠. 1973 年,在用 ...

随机推荐

  1. PHPSession-完全PHP5之session篇

    http://blog.csdn.net/masterft/article/details/1640122 1.什么是session?       Session的中文译名叫做“会话”,其本来的含义是 ...

  2. IP-MAC绑定导致网络故障

    前段时间将一台服务器A的服务迁移至了另外一台服务器B,外网IP地址也顺带迁移过来了,结果网络出现了问题. 其中内网是畅通的,但是外网IP怎么都连不上另外一台路由C(B和C是在一个交换机下的,网段也相同 ...

  3. 《Think in Java》读书笔记一:对象

    一.抽象过程 Alan Kay曾经总结了第一个成功的面向对象语言.同时也是Java所基于的语言之一的SmallTalk的五个基本特性,这些特性表现了一种纯粹的面向对象程序设计方式: 1.万物皆为对象. ...

  4. xcode编译运行报错纪录(持续更新)

    ---恢复内容开始--- 1. Undefined symbols for architecture i386: "_deflate", referenced from: -[NS ...

  5. initialize 和init

    initialize 是类方法,创建实例时会调用该方法.但是只会调用一次.如一个类创建了10个对象,initialize方法只会调用一次,但是init会调用10次.init 是实例方法,每次创建一个实 ...

  6. html-----016---HTTP 状态消息

    HTTP 状态消息 当浏览器从 web 服务器请求服务时,可能会发生错误. 从而有可能会返回下面的一系列状态消息: 1xx: 信息 消息: 描述: 100 Continue 服务器仅接收到部分请求,但 ...

  7. FFT —— 快速傅里叶变换

    问题: 已知A[], B[], 求C[],使: 定义C是A,B的卷积,例如多项式乘法等. 朴素做法是按照定义枚举i和j,但这样时间复杂度是O(n2). 能不能使时间复杂度降下来呢? 点值表示法: 我们 ...

  8. leetcode345——Reverse Vowels of a String(C++)

    Write a function that takes a string as input and reverse only the vowels of a string. Example 1:Giv ...

  9. SGU 解题报告

    Volume 1 Volume 2

  10. IQueryable接口与IEnumberable区别

    IEnumerable<T> 泛型类在调用自己的SKip 和 Take 等扩展方法之前数据就已经加载在本地内存里了,而IQueryable<T> 是将Skip ,take 这些 ...