Checksum.h 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. /* vim: set expandtab ts=4 sw=4: */
  2. /*
  3. * You may redistribute this program and/or modify it under the terms of
  4. * the GNU General Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU General Public License
  13. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #ifndef Checksum_H
  16. #define Checksum_H
  17. #include "util/Endian.h"
  18. #include "util/Assert.h"
  19. #include <stdint.h>
  20. /**
  21. * buffer must be 2 byte aligned!
  22. */
  23. static uint32_t Checksum_step(const uint8_t* buffer,
  24. uint16_t length,
  25. uint32_t state)
  26. {
  27. // Checksum pairs.
  28. for (uint32_t i = 0; i < length / 2; i++) {
  29. state += ((uint16_t*) buffer)[i];
  30. }
  31. // Do the odd byte if there is one.
  32. if (length % 2) {
  33. state += Endian_isBigEndian() ? (buffer[length - 1] << 8) : (buffer[length - 1]);
  34. }
  35. return state;
  36. }
  37. static uint32_t Checksum_step32(uint32_t content, uint32_t state)
  38. {
  39. return state + (content >> 16) + (content & 0xFFFF);
  40. }
  41. static uint16_t Checksum_complete(uint32_t state)
  42. {
  43. while (state > 0xFFFF) {
  44. state = (state >> 16) + (state & 0xFFFF);
  45. }
  46. return ~state;
  47. }
  48. /**
  49. * Generate a checksum on a piece of data.
  50. * buffer must be 2 byte aligned.
  51. *
  52. * @param buffer the bytes to checksum.
  53. * @param length the number of bytes in the buffer.
  54. * @return the 1's complement checksum.
  55. */
  56. static inline uint16_t Checksum_engine(const uint8_t* buffer, uint16_t length)
  57. {
  58. Assert_true(!((uintptr_t)buffer % 2));
  59. return Checksum_complete(Checksum_step(buffer, length, 0));
  60. }
  61. /**
  62. * Generate a checksum for a generic content packet under an IPv6 header.
  63. * sourceAndDestAddrs and packetHeaderAndContent must be 2 byte aligned.
  64. *
  65. * @param sourceAndDestAddrs the 16 byte source address followed
  66. * by the 16 byte destination address.
  67. * @param packetHeaderAndContent the UDP/ICMP header and the content
  68. * with the checksum value set to 0.
  69. * @param length the length of the packet header header and content.
  70. * @param packetType_be the big endian representation of the packet type.
  71. * @return a 1's complement checksum
  72. */
  73. static inline uint16_t Checksum_Ip6(const uint8_t* restrict sourceAndDestAddrs,
  74. const uint8_t* restrict packetHeaderAndContent,
  75. uint16_t length,
  76. uint32_t packetType_be)
  77. {
  78. Assert_true(!((uintptr_t)sourceAndDestAddrs % 2));
  79. Assert_true(!((uintptr_t)packetHeaderAndContent % 2));
  80. // http://tools.ietf.org/html/rfc2460#page-27
  81. uint64_t sum = Checksum_step(sourceAndDestAddrs, 32, 0);
  82. const uint32_t length_be = Endian_hostToBigEndian32(length);
  83. sum = Checksum_step32(length_be, sum);
  84. sum = Checksum_step32(packetType_be, sum);
  85. sum = Checksum_step(packetHeaderAndContent, length, sum);
  86. return Checksum_complete(sum);
  87. }
  88. /**
  89. * Generate a checksum for a UDP/IPv6 packet.
  90. * sourceAndDestAddrs and udpHeaderAndContent must be 2 byte aligned.
  91. *
  92. * @param sourceAndDestAddrs the 16 byte source address followed
  93. * by the 16 byte destination address.
  94. * @param udpHeaderAndContent the UDP header and the content with the UDP checksum value set to 0.
  95. * @param length the length of the UDP header and content.
  96. * @return a 1's complement checksum
  97. */
  98. static inline uint16_t Checksum_udpIp6(const uint8_t* restrict sourceAndDestAddrs,
  99. const uint8_t* restrict udpHeaderAndContent,
  100. uint16_t length)
  101. {
  102. return Checksum_Ip6(sourceAndDestAddrs,
  103. udpHeaderAndContent,
  104. length,
  105. Endian_hostToBigEndian32(17));
  106. }
  107. /**
  108. * Generate a checksum for an ICMP6/IPv6 packet.
  109. * sourceAndDestAddrs and icmpHeaderAndContent must be 2 byte aligned.
  110. *
  111. * @param sourceAndDestAddrs the 16 byte source address followed
  112. * by the 16 byte destination address.
  113. * @param icmpHeaderAndContent the ICMP6 header and the content
  114. * with the ICMP6 checksum value set to 0.
  115. * @param length the length of the ICMP6 header and content.
  116. * @return a 1's complement checksum
  117. */
  118. static inline uint16_t Checksum_icmp6(const uint8_t* restrict sourceAndDestAddrs,
  119. const uint8_t* restrict icmpHeaderAndContent,
  120. uint16_t length)
  121. {
  122. return Checksum_Ip6(sourceAndDestAddrs,
  123. icmpHeaderAndContent,
  124. length,
  125. Endian_hostToBigEndian32(58));
  126. }
  127. #endif