/* vim: set expandtab ts=4 sw=4: */ /* * You may redistribute this program and/or modify it under the terms of * the GNU General Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef Checksum_H #define Checksum_H #include "util/Endian.h" #include "util/Assert.h" #include /** * buffer must be 2 byte aligned! */ static uint32_t Checksum_step(const uint8_t* buffer, uint16_t length, uint32_t state) { // Checksum pairs. for (uint32_t i = 0; i < length / 2; i++) { state += ((uint16_t*) buffer)[i]; } // Do the odd byte if there is one. if (length % 2) { state += Endian_isBigEndian() ? (buffer[length - 1] << 8) : (buffer[length - 1]); } return state; } static uint32_t Checksum_step32(uint32_t content, uint32_t state) { return state + (content >> 16) + (content & 0xFFFF); } static uint16_t Checksum_complete(uint32_t state) { while (state > 0xFFFF) { state = (state >> 16) + (state & 0xFFFF); } return ~state; } /** * Generate a checksum on a piece of data. * buffer must be 2 byte aligned. * * @param buffer the bytes to checksum. * @param length the number of bytes in the buffer. * @return the 1's complement checksum. */ static inline uint16_t Checksum_engine(const uint8_t* buffer, uint16_t length) { Assert_true(!((uintptr_t)buffer % 2)); return Checksum_complete(Checksum_step(buffer, length, 0)); } /** * Generate a checksum for a generic content packet under an IPv6 header. * sourceAndDestAddrs and packetHeaderAndContent must be 2 byte aligned. * * @param sourceAndDestAddrs the 16 byte source address followed * by the 16 byte destination address. * @param packetHeaderAndContent the UDP/ICMP header and the content * with the checksum value set to 0. * @param length the length of the packet header header and content. * @param packetType_be the big endian representation of the packet type. * @return a 1's complement checksum */ static inline uint16_t Checksum_Ip6(const uint8_t* restrict sourceAndDestAddrs, const uint8_t* restrict packetHeaderAndContent, uint16_t length, uint32_t packetType_be) { Assert_true(!((uintptr_t)sourceAndDestAddrs % 2)); Assert_true(!((uintptr_t)packetHeaderAndContent % 2)); // http://tools.ietf.org/html/rfc2460#page-27 uint64_t sum = Checksum_step(sourceAndDestAddrs, 32, 0); const uint32_t length_be = Endian_hostToBigEndian32(length); sum = Checksum_step32(length_be, sum); sum = Checksum_step32(packetType_be, sum); sum = Checksum_step(packetHeaderAndContent, length, sum); return Checksum_complete(sum); } /** * Generate a checksum for a UDP/IPv6 packet. * sourceAndDestAddrs and udpHeaderAndContent must be 2 byte aligned. * * @param sourceAndDestAddrs the 16 byte source address followed * by the 16 byte destination address. * @param udpHeaderAndContent the UDP header and the content with the UDP checksum value set to 0. * @param length the length of the UDP header and content. * @return a 1's complement checksum */ static inline uint16_t Checksum_udpIp6(const uint8_t* restrict sourceAndDestAddrs, const uint8_t* restrict udpHeaderAndContent, uint16_t length) { return Checksum_Ip6(sourceAndDestAddrs, udpHeaderAndContent, length, Endian_hostToBigEndian32(17)); } /** * Generate a checksum for an ICMP6/IPv6 packet. * sourceAndDestAddrs and icmpHeaderAndContent must be 2 byte aligned. * * @param sourceAndDestAddrs the 16 byte source address followed * by the 16 byte destination address. * @param icmpHeaderAndContent the ICMP6 header and the content * with the ICMP6 checksum value set to 0. * @param length the length of the ICMP6 header and content. * @return a 1's complement checksum */ static inline uint16_t Checksum_icmp6(const uint8_t* restrict sourceAndDestAddrs, const uint8_t* restrict icmpHeaderAndContent, uint16_t length) { return Checksum_Ip6(sourceAndDestAddrs, icmpHeaderAndContent, length, Endian_hostToBigEndian32(58)); } #endif