Checksum.h 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  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 <https://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. Assert_true(!(((uintptr_t)buffer) % 2));
  28. // Checksum pairs.
  29. for (uint32_t i = 0; i < length / 2; i++) {
  30. state += ((uint16_t*) buffer)[i];
  31. }
  32. // Do the odd byte if there is one.
  33. if (length % 2) {
  34. state += Endian_isBigEndian() ? (buffer[length - 1] << 8) : (buffer[length - 1]);
  35. }
  36. return state;
  37. }
  38. static uint32_t Checksum_step32(uint32_t content, uint32_t state)
  39. {
  40. return state + (content >> 16) + (content & 0xFFFF);
  41. }
  42. static uint16_t Checksum_complete_be(uint32_t state)
  43. {
  44. while (state > 0xFFFF) {
  45. state = (state >> 16) + (state & 0xFFFF);
  46. }
  47. return ~state;
  48. }
  49. /**
  50. * Generate a checksum on a piece of data.
  51. * buffer must be 2 byte aligned.
  52. *
  53. * @param buffer the bytes to checksum.
  54. * @param length the number of bytes in the buffer.
  55. * @return the 1's complement checksum.
  56. */
  57. static inline uint16_t Checksum_engine_be(const uint8_t* buffer, uint16_t length)
  58. {
  59. Assert_true(!((uintptr_t)buffer % 2));
  60. return Checksum_complete_be(Checksum_step(buffer, length, 0));
  61. }
  62. /**
  63. * Generate a checksum for a generic content packet under an IPv6 header.
  64. * sourceAndDestAddrs and packetHeaderAndContent must be 2 byte aligned.
  65. *
  66. * @param sourceAndDestAddrs the 16 byte source address followed
  67. * by the 16 byte destination address.
  68. * @param packetHeaderAndContent the UDP/ICMP header and the content
  69. * with the checksum value set to 0.
  70. * @param length the length of the packet header header and content.
  71. * @param packetType_be the big endian representation of the packet type.
  72. * @return a 1's complement checksum
  73. */
  74. static inline uint16_t Checksum_Ip6_be(const uint8_t* restrict sourceAndDestAddrs,
  75. const uint8_t* restrict packetHeaderAndContent,
  76. uint16_t length,
  77. uint32_t packetType_be)
  78. {
  79. Assert_true(!((uintptr_t)sourceAndDestAddrs % 2));
  80. Assert_true(!((uintptr_t)packetHeaderAndContent % 2));
  81. // http://tools.ietf.org/html/rfc2460#page-27
  82. uint64_t sum = Checksum_step(sourceAndDestAddrs, 32, 0);
  83. const uint32_t length_be = Endian_hostToBigEndian32(length);
  84. sum = Checksum_step32(length_be, sum);
  85. sum = Checksum_step32(packetType_be, sum);
  86. sum = Checksum_step(packetHeaderAndContent, length, sum);
  87. return Checksum_complete_be(sum);
  88. }
  89. /**
  90. * Generate a checksum for a UDP/IPv6 packet.
  91. * sourceAndDestAddrs and udpHeaderAndContent must be 2 byte aligned.
  92. *
  93. * @param sourceAndDestAddrs the 16 byte source address followed
  94. * by the 16 byte destination address.
  95. * @param udpHeaderAndContent the UDP header and the content with the UDP checksum value set to 0.
  96. * @param length the length of the UDP header and content.
  97. * @return a 1's complement checksum
  98. */
  99. static inline uint16_t Checksum_udpIp6_be(const uint8_t* restrict sourceAndDestAddrs,
  100. const uint8_t* restrict udpHeaderAndContent,
  101. uint16_t length)
  102. {
  103. return Checksum_Ip6_be(sourceAndDestAddrs,
  104. udpHeaderAndContent,
  105. length,
  106. Endian_hostToBigEndian32(17));
  107. }
  108. /**
  109. * Generate a checksum for an ICMP6/IPv6 packet.
  110. * sourceAndDestAddrs and icmpHeaderAndContent must be 2 byte aligned.
  111. *
  112. * @param sourceAndDestAddrs the 16 byte source address followed
  113. * by the 16 byte destination address.
  114. * @param icmpHeaderAndContent the ICMP6 header and the content
  115. * with the ICMP6 checksum value set to 0.
  116. * @param length the length of the ICMP6 header and content.
  117. * @return a 1's complement checksum
  118. */
  119. static inline uint16_t Checksum_icmp6_be(const uint8_t* restrict sourceAndDestAddrs,
  120. const uint8_t* restrict icmpHeaderAndContent,
  121. uint16_t length)
  122. {
  123. return Checksum_Ip6_be(sourceAndDestAddrs,
  124. icmpHeaderAndContent,
  125. length,
  126. Endian_hostToBigEndian32(58));
  127. }
  128. #endif