checksum.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /*
  2. * IP checksumming functions.
  3. * (c) 2008 Gerd Hoffmann <kraxel@redhat.com>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; under version 2 or later of the License.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include "qemu/osdep.h"
  18. #include "net/checksum.h"
  19. #include "net/eth.h"
  20. uint32_t net_checksum_add_cont(int len, uint8_t *buf, int seq)
  21. {
  22. uint32_t sum1 = 0, sum2 = 0;
  23. int i;
  24. for (i = 0; i < len - 1; i += 2) {
  25. sum1 += (uint32_t)buf[i];
  26. sum2 += (uint32_t)buf[i + 1];
  27. }
  28. if (i < len) {
  29. sum1 += (uint32_t)buf[i];
  30. }
  31. if (seq & 1) {
  32. return sum1 + (sum2 << 8);
  33. } else {
  34. return sum2 + (sum1 << 8);
  35. }
  36. }
  37. uint16_t net_checksum_finish(uint32_t sum)
  38. {
  39. while (sum>>16)
  40. sum = (sum & 0xFFFF)+(sum >> 16);
  41. return ~sum;
  42. }
  43. uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto,
  44. uint8_t *addrs, uint8_t *buf)
  45. {
  46. uint32_t sum = 0;
  47. sum += net_checksum_add(length, buf); // payload
  48. sum += net_checksum_add(8, addrs); // src + dst address
  49. sum += proto + length; // protocol & length
  50. return net_checksum_finish(sum);
  51. }
  52. void net_checksum_calculate(void *data, int length, int csum_flag)
  53. {
  54. int mac_hdr_len, ip_len;
  55. struct ip_header *ip;
  56. uint16_t csum;
  57. /*
  58. * Note: We cannot assume "data" is aligned, so the all code uses
  59. * some macros that take care of possible unaligned access for
  60. * struct members (just in case).
  61. */
  62. /* Ensure we have at least an Eth header */
  63. if (length < sizeof(struct eth_header)) {
  64. return;
  65. }
  66. /* Handle the optional VLAN headers */
  67. switch (lduw_be_p(&PKT_GET_ETH_HDR(data)->h_proto)) {
  68. case ETH_P_VLAN:
  69. mac_hdr_len = sizeof(struct eth_header) +
  70. sizeof(struct vlan_header);
  71. break;
  72. case ETH_P_DVLAN:
  73. if (lduw_be_p(&PKT_GET_VLAN_HDR(data)->h_proto) == ETH_P_VLAN) {
  74. mac_hdr_len = sizeof(struct eth_header) +
  75. 2 * sizeof(struct vlan_header);
  76. } else {
  77. mac_hdr_len = sizeof(struct eth_header) +
  78. sizeof(struct vlan_header);
  79. }
  80. break;
  81. default:
  82. mac_hdr_len = sizeof(struct eth_header);
  83. break;
  84. }
  85. length -= mac_hdr_len;
  86. /* Now check we have an IP header (with an optional VLAN header) */
  87. if (length < sizeof(struct ip_header)) {
  88. return;
  89. }
  90. ip = (struct ip_header *)((uint8_t *)data + mac_hdr_len);
  91. if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
  92. return; /* not IPv4 */
  93. }
  94. /* Calculate IP checksum */
  95. if (csum_flag & CSUM_IP) {
  96. stw_he_p(&ip->ip_sum, 0);
  97. csum = net_raw_checksum((uint8_t *)ip, IP_HDR_GET_LEN(ip));
  98. stw_be_p(&ip->ip_sum, csum);
  99. }
  100. if (IP4_IS_FRAGMENT(ip)) {
  101. return; /* a fragmented IP packet */
  102. }
  103. ip_len = lduw_be_p(&ip->ip_len);
  104. /* Last, check that we have enough data for the all IP frame */
  105. if (length < ip_len) {
  106. return;
  107. }
  108. ip_len -= IP_HDR_GET_LEN(ip);
  109. switch (ip->ip_p) {
  110. case IP_PROTO_TCP:
  111. {
  112. if (!(csum_flag & CSUM_TCP)) {
  113. return;
  114. }
  115. tcp_header *tcp = (tcp_header *)(ip + 1);
  116. if (ip_len < sizeof(tcp_header)) {
  117. return;
  118. }
  119. /* Set csum to 0 */
  120. stw_he_p(&tcp->th_sum, 0);
  121. csum = net_checksum_tcpudp(ip_len, ip->ip_p,
  122. (uint8_t *)&ip->ip_src,
  123. (uint8_t *)tcp);
  124. /* Store computed csum */
  125. stw_be_p(&tcp->th_sum, csum);
  126. break;
  127. }
  128. case IP_PROTO_UDP:
  129. {
  130. if (!(csum_flag & CSUM_UDP)) {
  131. return;
  132. }
  133. udp_header *udp = (udp_header *)(ip + 1);
  134. if (ip_len < sizeof(udp_header)) {
  135. return;
  136. }
  137. /* Set csum to 0 */
  138. stw_he_p(&udp->uh_sum, 0);
  139. csum = net_checksum_tcpudp(ip_len, ip->ip_p,
  140. (uint8_t *)&ip->ip_src,
  141. (uint8_t *)udp);
  142. /* Store computed csum */
  143. stw_be_p(&udp->uh_sum, csum);
  144. break;
  145. }
  146. default:
  147. /* Can't handle any other protocol */
  148. break;
  149. }
  150. }
  151. uint32_t
  152. net_checksum_add_iov(const struct iovec *iov, const unsigned int iov_cnt,
  153. uint32_t iov_off, uint32_t size, uint32_t csum_offset)
  154. {
  155. size_t iovec_off;
  156. unsigned int i;
  157. uint32_t res = 0;
  158. iovec_off = 0;
  159. for (i = 0; i < iov_cnt && size; i++) {
  160. if (iov_off < (iovec_off + iov[i].iov_len)) {
  161. size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off , size);
  162. void *chunk_buf = iov[i].iov_base + (iov_off - iovec_off);
  163. res += net_checksum_add_cont(len, chunk_buf, csum_offset);
  164. csum_offset += len;
  165. iov_off += len;
  166. size -= len;
  167. }
  168. iovec_off += iov[i].iov_len;
  169. }
  170. return res;
  171. }