]>
Commit | Line | Data |
---|---|---|
1 | /** | |
2 | * @file | |
3 | * Incluse internet checksum functions. | |
4 | * | |
5 | */ | |
6 | ||
7 | /* | |
8 | * Copyright (c) 2001-2004 Swedish Institute of Computer Science. | |
9 | * All rights reserved. | |
10 | * | |
11 | * Redistribution and use in source and binary forms, with or without modification, | |
12 | * are permitted provided that the following conditions are met: | |
13 | * | |
14 | * 1. Redistributions of source code must retain the above copyright notice, | |
15 | * this list of conditions and the following disclaimer. | |
16 | * 2. Redistributions in binary form must reproduce the above copyright notice, | |
17 | * this list of conditions and the following disclaimer in the documentation | |
18 | * and/or other materials provided with the distribution. | |
19 | * 3. The name of the author may not be used to endorse or promote products | |
20 | * derived from this software without specific prior written permission. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
23 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
24 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT | |
25 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
26 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT | |
27 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | |
30 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY | |
31 | * OF SUCH DAMAGE. | |
32 | * | |
33 | * This file is part of the lwIP TCP/IP stack. | |
34 | * | |
35 | * Author: Adam Dunkels <adam@sics.se> | |
36 | * | |
37 | */ | |
38 | ||
39 | #include "lwip/opt.h" | |
40 | ||
41 | #include "lwip/inet_chksum.h" | |
42 | #include "lwip/inet.h" | |
43 | ||
44 | #include <string.h> | |
45 | ||
46 | /* These are some reference implementations of the checksum algorithm, with the | |
47 | * aim of being simple, correct and fully portable. Checksumming is the | |
48 | * first thing you would want to optimize for your platform. If you create | |
49 | * your own version, link it in and in your cc.h put: | |
50 | * | |
51 | * #define LWIP_CHKSUM <your_checksum_routine> | |
52 | * | |
53 | * Or you can select from the implementations below by defining | |
54 | * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3. | |
55 | */ | |
56 | ||
57 | #ifndef LWIP_CHKSUM | |
58 | # define LWIP_CHKSUM lwip_standard_chksum | |
59 | # ifndef LWIP_CHKSUM_ALGORITHM | |
60 | # define LWIP_CHKSUM_ALGORITHM 1 | |
61 | # endif | |
62 | #endif | |
63 | /* If none set: */ | |
64 | #ifndef LWIP_CHKSUM_ALGORITHM | |
65 | # define LWIP_CHKSUM_ALGORITHM 0 | |
66 | #endif | |
67 | ||
68 | #if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */ | |
69 | /** | |
70 | * lwip checksum | |
71 | * | |
72 | * @param dataptr points to start of data to be summed at any boundary | |
73 | * @param len length of data to be summed | |
74 | * @return host order (!) lwip checksum (non-inverted Internet sum) | |
75 | * | |
76 | * @note accumulator size limits summable length to 64k | |
77 | * @note host endianess is irrelevant (p3 RFC1071) | |
78 | */ | |
79 | static u16_t | |
80 | lwip_standard_chksum(void *dataptr, u16_t len) | |
81 | { | |
82 | u32_t acc; | |
83 | u16_t src; | |
84 | u8_t *octetptr; | |
85 | ||
86 | acc = 0; | |
87 | /* dataptr may be at odd or even addresses */ | |
88 | octetptr = (u8_t*)dataptr; | |
89 | while (len > 1) | |
90 | { | |
91 | /* declare first octet as most significant | |
92 | thus assume network order, ignoring host order */ | |
93 | src = (*octetptr) << 8; | |
94 | octetptr++; | |
95 | /* declare second octet as least significant */ | |
96 | src |= (*octetptr); | |
97 | octetptr++; | |
98 | acc += src; | |
99 | len -= 2; | |
100 | } | |
101 | if (len > 0) | |
102 | { | |
103 | /* accumulate remaining octet */ | |
104 | src = (*octetptr) << 8; | |
105 | acc += src; | |
106 | } | |
107 | /* add deferred carry bits */ | |
108 | acc = (acc >> 16) + (acc & 0x0000ffffUL); | |
109 | if ((acc & 0xffff0000) != 0) { | |
110 | acc = (acc >> 16) + (acc & 0x0000ffffUL); | |
111 | } | |
112 | /* This maybe a little confusing: reorder sum using htons() | |
113 | instead of ntohs() since it has a little less call overhead. | |
114 | The caller must invert bits for Internet sum ! */ | |
115 | return htons((u16_t)acc); | |
116 | } | |
117 | #endif | |
118 | ||
119 | #if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */ | |
120 | /* | |
121 | * Curt McDowell | |
122 | * Broadcom Corp. | |
123 | * csm@broadcom.com | |
124 | * | |
125 | * IP checksum two bytes at a time with support for | |
126 | * unaligned buffer. | |
127 | * Works for len up to and including 0x20000. | |
128 | * by Curt McDowell, Broadcom Corp. 12/08/2005 | |
129 | * | |
130 | * @param dataptr points to start of data to be summed at any boundary | |
131 | * @param len length of data to be summed | |
132 | * @return host order (!) lwip checksum (non-inverted Internet sum) | |
133 | */ | |
134 | ||
135 | static u16_t | |
136 | lwip_standard_chksum(void *dataptr, int len) | |
137 | { | |
138 | u8_t *pb = dataptr; | |
139 | u16_t *ps, t = 0; | |
140 | u32_t sum = 0; | |
141 | int odd = ((u32_t)pb & 1); | |
142 | ||
143 | /* Get aligned to u16_t */ | |
144 | if (odd && len > 0) { | |
145 | ((u8_t *)&t)[1] = *pb++; | |
146 | len--; | |
147 | } | |
148 | ||
149 | /* Add the bulk of the data */ | |
150 | ps = (u16_t *)pb; | |
151 | while (len > 1) { | |
152 | sum += *ps++; | |
153 | len -= 2; | |
154 | } | |
155 | ||
156 | /* Consume left-over byte, if any */ | |
157 | if (len > 0) | |
158 | ((u8_t *)&t)[0] = *(u8_t *)ps;; | |
159 | ||
160 | /* Add end bytes */ | |
161 | sum += t; | |
162 | ||
163 | /* Fold 32-bit sum to 16 bits */ | |
164 | while ((sum >> 16) != 0) | |
165 | sum = (sum & 0xffff) + (sum >> 16); | |
166 | ||
167 | /* Swap if alignment was odd */ | |
168 | if (odd) | |
169 | sum = ((sum & 0xff) << 8) | ((sum & 0xff00) >> 8); | |
170 | ||
171 | return sum; | |
172 | } | |
173 | #endif | |
174 | ||
175 | #if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */ | |
176 | /** | |
177 | * An optimized checksum routine. Basically, it uses loop-unrolling on | |
178 | * the checksum loop, treating the head and tail bytes specially, whereas | |
179 | * the inner loop acts on 8 bytes at a time. | |
180 | * | |
181 | * @arg start of buffer to be checksummed. May be an odd byte address. | |
182 | * @len number of bytes in the buffer to be checksummed. | |
183 | * @return host order (!) lwip checksum (non-inverted Internet sum) | |
184 | * | |
185 | * by Curt McDowell, Broadcom Corp. December 8th, 2005 | |
186 | */ | |
187 | ||
188 | static u16_t | |
189 | lwip_standard_chksum(void *dataptr, int len) | |
190 | { | |
191 | u8_t *pb = dataptr; | |
192 | u16_t *ps, t = 0; | |
193 | u32_t *pl; | |
194 | u32_t sum = 0, tmp; | |
195 | /* starts at odd byte address? */ | |
196 | int odd = ((u32_t)pb & 1); | |
197 | ||
198 | if (odd && len > 0) { | |
199 | ((u8_t *)&t)[1] = *pb++; | |
200 | len--; | |
201 | } | |
202 | ||
203 | ps = (u16_t *)pb; | |
204 | ||
205 | if (((u32_t)ps & 3) && len > 1) { | |
206 | sum += *ps++; | |
207 | len -= 2; | |
208 | } | |
209 | ||
210 | pl = (u32_t *)ps; | |
211 | ||
212 | while (len > 7) { | |
213 | tmp = sum + *pl++; /* ping */ | |
214 | if (tmp < sum) | |
215 | tmp++; /* add back carry */ | |
216 | ||
217 | sum = tmp + *pl++; /* pong */ | |
218 | if (sum < tmp) | |
219 | sum++; /* add back carry */ | |
220 | ||
221 | len -= 8; | |
222 | } | |
223 | ||
224 | /* make room in upper bits */ | |
225 | sum = (sum >> 16) + (sum & 0xffff); | |
226 | ||
227 | ps = (u16_t *)pl; | |
228 | ||
229 | /* 16-bit aligned word remaining? */ | |
230 | while (len > 1) { | |
231 | sum += *ps++; | |
232 | len -= 2; | |
233 | } | |
234 | ||
235 | /* dangling tail byte remaining? */ | |
236 | if (len > 0) /* include odd byte */ | |
237 | ((u8_t *)&t)[0] = *(u8_t *)ps; | |
238 | ||
239 | sum += t; /* add end bytes */ | |
240 | ||
241 | while ((sum >> 16) != 0) /* combine halves */ | |
242 | sum = (sum >> 16) + (sum & 0xffff); | |
243 | ||
244 | if (odd) | |
245 | sum = ((sum & 0xff) << 8) | ((sum & 0xff00) >> 8); | |
246 | ||
247 | return sum; | |
248 | } | |
249 | #endif | |
250 | ||
251 | /* inet_chksum_pseudo: | |
252 | * | |
253 | * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. | |
254 | * IP addresses are expected to be in network byte order. | |
255 | * | |
256 | * @param p chain of pbufs over that a checksum should be calculated (ip data part) | |
257 | * @param src source ip address (used for checksum of pseudo header) | |
258 | * @param dst destination ip address (used for checksum of pseudo header) | |
259 | * @param proto ip protocol (used for checksum of pseudo header) | |
260 | * @param proto_len length of the ip data part (used for checksum of pseudo header) | |
261 | * @return checksum (as u16_t) to be saved directly in the protocol header | |
262 | */ | |
263 | u16_t | |
264 | inet_chksum_pseudo(struct pbuf *p, | |
265 | struct ip_addr *src, struct ip_addr *dest, | |
266 | u8_t proto, u16_t proto_len) | |
267 | { | |
268 | u32_t acc; | |
269 | struct pbuf *q; | |
270 | u8_t swapped; | |
271 | ||
272 | acc = 0; | |
273 | swapped = 0; | |
274 | /* iterate through all pbuf in chain */ | |
275 | for(q = p; q != NULL; q = q->next) { | |
276 | LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", | |
277 | (void *)q, (void *)q->next)); | |
278 | acc += LWIP_CHKSUM(q->payload, q->len); | |
279 | /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ | |
280 | while ((acc >> 16) != 0) { | |
281 | acc = (acc & 0xffffUL) + (acc >> 16); | |
282 | } | |
283 | if (q->len % 2 != 0) { | |
284 | swapped = 1 - swapped; | |
285 | acc = ((acc & 0xff) << 8) | ((acc & 0xff00UL) >> 8); | |
286 | } | |
287 | /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ | |
288 | } | |
289 | ||
290 | if (swapped) { | |
291 | acc = ((acc & 0xff) << 8) | ((acc & 0xff00UL) >> 8); | |
292 | } | |
293 | acc += (src->addr & 0xffffUL); | |
294 | acc += ((src->addr >> 16) & 0xffffUL); | |
295 | acc += (dest->addr & 0xffffUL); | |
296 | acc += ((dest->addr >> 16) & 0xffffUL); | |
297 | acc += (u32_t)htons((u16_t)proto); | |
298 | acc += (u32_t)htons(proto_len); | |
299 | ||
300 | while ((acc >> 16) != 0) { | |
301 | acc = (acc & 0xffffUL) + (acc >> 16); | |
302 | } | |
303 | LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); | |
304 | return (u16_t)~(acc & 0xffffUL); | |
305 | } | |
306 | ||
307 | /* inet_chksum_pseudo: | |
308 | * | |
309 | * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. | |
310 | * IP addresses are expected to be in network byte order. | |
311 | * | |
312 | * @param p chain of pbufs over that a checksum should be calculated (ip data part) | |
313 | * @param src source ip address (used for checksum of pseudo header) | |
314 | * @param dst destination ip address (used for checksum of pseudo header) | |
315 | * @param proto ip protocol (used for checksum of pseudo header) | |
316 | * @param proto_len length of the ip data part (used for checksum of pseudo header) | |
317 | * @return checksum (as u16_t) to be saved directly in the protocol header | |
318 | */ | |
319 | u16_t | |
320 | inet_chksum_pseudo_partial(struct pbuf *p, | |
321 | struct ip_addr *src, struct ip_addr *dest, | |
322 | u8_t proto, u16_t proto_len, u16_t chksum_len) | |
323 | { | |
324 | u32_t acc; | |
325 | struct pbuf *q; | |
326 | u8_t swapped; | |
327 | u16_t chklen; | |
328 | ||
329 | acc = 0; | |
330 | swapped = 0; | |
331 | /* iterate through all pbuf in chain */ | |
332 | for(q = p; (q != NULL) && (chksum_len > 0); q = q->next) { | |
333 | LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", | |
334 | (void *)q, (void *)q->next)); | |
335 | chklen = q->len; | |
336 | if (chklen > chksum_len) { | |
337 | chklen = chksum_len; | |
338 | } | |
339 | acc += LWIP_CHKSUM(q->payload, chklen); | |
340 | chksum_len -= chklen; | |
341 | LWIP_ASSERT("delete me", chksum_len < 0x7fff); | |
342 | /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ | |
343 | while ((acc >> 16) != 0) { | |
344 | acc = (acc & 0xffffUL) + (acc >> 16); | |
345 | } | |
346 | if (q->len % 2 != 0) { | |
347 | swapped = 1 - swapped; | |
348 | acc = ((acc & 0xff) << 8) | ((acc & 0xff00UL) >> 8); | |
349 | } | |
350 | /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ | |
351 | } | |
352 | ||
353 | if (swapped) { | |
354 | acc = ((acc & 0xff) << 8) | ((acc & 0xff00UL) >> 8); | |
355 | } | |
356 | acc += (src->addr & 0xffffUL); | |
357 | acc += ((src->addr >> 16) & 0xffffUL); | |
358 | acc += (dest->addr & 0xffffUL); | |
359 | acc += ((dest->addr >> 16) & 0xffffUL); | |
360 | acc += (u32_t)htons((u16_t)proto); | |
361 | acc += (u32_t)htons(proto_len); | |
362 | ||
363 | while ((acc >> 16) != 0) { | |
364 | acc = (acc & 0xffffUL) + (acc >> 16); | |
365 | } | |
366 | LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); | |
367 | return (u16_t)~(acc & 0xffffUL); | |
368 | } | |
369 | ||
370 | /* inet_chksum: | |
371 | * | |
372 | * Calculates the Internet checksum over a portion of memory. Used primarily for IP | |
373 | * and ICMP. | |
374 | * | |
375 | * @param dataptr start of the buffer to calculate the checksum (no alignment needed) | |
376 | * @param len length of the buffer to calculate the checksum | |
377 | * @return checksum (as u16_t) to be saved directly in the protocol header | |
378 | */ | |
379 | ||
380 | u16_t | |
381 | inet_chksum(void *dataptr, u16_t len) | |
382 | { | |
383 | u32_t acc; | |
384 | ||
385 | acc = LWIP_CHKSUM(dataptr, len); | |
386 | while ((acc >> 16) != 0) { | |
387 | acc = (acc & 0xffff) + (acc >> 16); | |
388 | } | |
389 | return (u16_t)~(acc & 0xffff); | |
390 | } | |
391 | ||
392 | /** | |
393 | * Calculate a checksum over a chain of pbufs (without pseudo-header, much like | |
394 | * inet_chksum only pbufs are used). | |
395 | * | |
396 | * @param p pbuf chain over that the checksum should be calculated | |
397 | * @return checksum (as u16_t) to be saved directly in the protocol header | |
398 | */ | |
399 | u16_t | |
400 | inet_chksum_pbuf(struct pbuf *p) | |
401 | { | |
402 | u32_t acc; | |
403 | struct pbuf *q; | |
404 | u8_t swapped; | |
405 | ||
406 | acc = 0; | |
407 | swapped = 0; | |
408 | for(q = p; q != NULL; q = q->next) { | |
409 | acc += LWIP_CHKSUM(q->payload, q->len); | |
410 | while ((acc >> 16) != 0) { | |
411 | acc = (acc & 0xffffUL) + (acc >> 16); | |
412 | } | |
413 | if (q->len % 2 != 0) { | |
414 | swapped = 1 - swapped; | |
415 | acc = (acc & 0x00ffUL << 8) | (acc & 0xff00UL >> 8); | |
416 | } | |
417 | } | |
418 | ||
419 | if (swapped) { | |
420 | acc = ((acc & 0x00ffUL) << 8) | ((acc & 0xff00UL) >> 8); | |
421 | } | |
422 | return (u16_t)~(acc & 0xffffUL); | |
423 | } |