summaryrefslogtreecommitdiffstats
path: root/src/checksums.c
blob: 852f195216f6ed7a20c5069e4a31c72b6fc33ecb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
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;
}