diff options
Diffstat (limited to 'src/checksums.c')
-rw-r--r-- | src/checksums.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/src/checksums.c b/src/checksums.c new file mode 100644 index 0000000..852f195 --- /dev/null +++ b/src/checksums.c @@ -0,0 +1,229 @@ +/*--------------------------------------------------------------- + * Copyright (c) 2018 + * Broadcom Corporation + * All Rights Reserved. + *--------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and + * the following disclaimers. + * + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimers in the documentation and/or other materials + * provided with the distribution. + * + * + * Neither the name of Broadcom Coporation, + * nor the names of its contributors may be used to endorse + * or promote products derived from this Software without + * specific prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * ________________________________________________________________ + * + * UDP checksums v4 and v6 + * + * The checksum calculation is defined in RFC 768 + * hints as to how to calculate it efficiently are in RFC 1071 + * + * by Robert J. McMahon (rjmcmahon@rjmcmahon.com, bob.mcmahon@broadcom.com) + * ------------------------------------------------------------------- + */ +#include "headers.h" + +/* + * + * Compute Internet Checksum for UDP packets (assumes 32 bit system) + * + * IPV4 Notes: + * + * User Datagram Header Format + * 0 7 8 15 16 23 24 31 + * +--------+--------+--------+--------+ + * | Source | Destination | + * | Port | Port | + * +--------+--------+--------+--------+ + * | | | + * | Length | Checksum | + * +--------+--------+--------+--------+ + * | + * | data octets ... + * +---------------- ... + * + * + * Checksum is the 16-bit one's complement of the one's complement sum of a + * pseudo header of information from the IP header, the UDP header, and the + * data, padded with zero octets at the end (if necessary) to make a + * multiple of two octets. + * + * The ipv4 pseudo header conceptually prefixed to the UDP header contains the + * source address, the destination address, the protocol, and the UDP + * length. This information gives protection against misrouted datagrams. + * This checksum procedure is the same as is used in TCP. + * + * 0 7 8 15 16 23 24 31 + * +--------+--------+--------+--------+ + * | source address | + * +--------+--------+--------+--------+ + * | destination address | + * +--------+--------+--------+--------+ + * | zero |protocol| UDP length | + * +--------+--------+--------+--------+ + * + * If the computed checksum is zero, it is transmitted as all ones (the + * equivalent in one's complement arithmetic). An all zero transmitted + * checksum value means that the transmitter generated no checksum (for + * debugging or for higher level protocols that don't care). + * + * + * IPV6 Notes: + * + * Any transport or other upper-layer protocol that includes the + * addresses from the IP header in its checksum computation must be + * modified for use over IPv6, to include the 128-bit IPv6 addresses + * instead of 32-bit IPv4 addresses. In particular, the following + * illustration shows the TCP and UDP "pseudo-header" for IPv6: + * + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * + + + * | | + * + Source Address + + * | | + * + + + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * + + + * | | + * + Destination Address + + * | | + * + + + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Upper-Layer Packet Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | zero | Next Header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * o If the IPv6 packet contains a Routing header, the Destination + * Address used in the pseudo-header is that of the final + * destination. At the originating node, that address will be in + * the last element of the Routing header; at the recipient(s), + * that address will be in the Destination Address field of the + * IPv6 header. + * + * o The Next Header value in the pseudo-header identifies the + * upper-layer protocol (e.g., 6 for TCP, or 17 for UDP). It will + * differ from the Next Header value in the IPv6 header if there + * are extension headers between the IPv6 header and the upper- + * layer header. + * + * o The Upper-Layer Packet Length in the pseudo-header is the + * length of the upper-layer header and data (e.g., TCP header + * plus TCP data). Some upper-layer protocols carry their own + * length information (e.g., the Length field in the UDP header); + * for such protocols, that is the length used in the pseudo- + * header. Other protocols (such as TCP) do not carry their own + * length information, in which case the length used in the + * pseudo-header is the Payload Length from the IPv6 header, minus + * the length of any extension headers present between the IPv6 + * header and the upper-layer header. + * + * o Unlike IPv4, when UDP packets are originated by an IPv6 node, + * the UDP checksum is not optional. That is, whenever + * originating a UDP packet, an IPv6 node must compute a UDP + * checksum over the packet and the pseudo-header, and, if that + * computation yields a result of zero, it must be changed to hex + * FFFF for placement in the UDP header. IPv6 receivers must + * discard UDP packets containing a zero checksum, and should log + * the error. + * + * + * Returns zero on checksum success, non zero otherwise + */ + +#define IPV4SRCOFFSET 12 // the ipv4 source address offset from the l3 pdu +#define IPV6SRCOFFSET 8 // the ipv6 source address offset +#define IPV6SIZE 8 // units is number of 16 bits, i.e. 128 bits is eight 16 bits +#define IPV4SIZE 2 // v4 is two 16 bits (32 bits) +#define UDPPROTO 17 // UDP protocol value for psuedo header +uint32_t udpchecksum(const void *l3pdu, const void *l4pdu, int udplen, int v6) { + register uint32_t sum = 0; + const uint16_t *data; + int i; + + const struct udphdr *udp_hdr = (const struct udphdr *)l4pdu; + if (!udp_hdr->check) { + if (v6) + // v6 requires checksums + return -1; + else + // v4 checksums are optional + return 0; + } + + /* + * Build pseudo headers, partially from the packet + * (which are in network byte order) and + * the protocol of UDP (value of 17). Also, the IP dst + * addr immediately follows the src so double the size + * per each loop to cover both addrs + */ + if (v6) { + // skip to the ip header v6 src field, offset 8 (see ipv6 header) + data = (const uint16_t *)((char *)l3pdu + IPV6SRCOFFSET); + for (i = 0; i < (2 * IPV6SIZE); i++) { + sum += *data++; + } + } else { + // skip to the ip header v4 src field, offset 12 (see ipv4 header) + data = (const uint16_t *)((char *)l3pdu + IPV4SRCOFFSET); + for (i = 0; i < (2 * IPV4SIZE); i++) { + sum += *data++; + } + } + // These should work for both v4 and v6 even though + // v6 psuedo header uses 32 bit values because the + // uppers in v6 will be zero + sum += htons(UDPPROTO); // proto of UDP is 17 + sum += htons(udplen); // For UDP, the pseudo hdr len equals udp len + + /* + * UDP hdr + payload + */ + data = (const uint16_t *) l4pdu; + while( udplen > 1 ) { + sum += *data++; + udplen -= 2; + } + /* Add left-over byte, if any */ + if( udplen > 0 ) + sum += * (uint8_t *) data; + + /* Fold 32-bit sum to 16 bits */ + while (sum>>16) + sum = (sum & 0xffff) + (sum >> 16); + + /* return ones complement */ + sum = (sum ^ 0xffff); + return sum; +} |