]> git.proxmox.com Git - mirror_frr.git/blame - tests/lib/test_checksum.c
lib: use iovec for checksum code
[mirror_frr.git] / tests / lib / test_checksum.c
CommitLineData
d62a17ae 1/*
46f4a4d2
PJ
2 * Copyright (C) 2008 Sun Microsystems, Inc.
3 *
4 * This file is part of Quagga.
5 *
6 * Quagga is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * Quagga is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
896014f4
DL
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
46f4a4d2
PJ
19 */
20
5d4b8cf2
PJ
21#include <zebra.h>
22#include <stdlib.h>
23#include <time.h>
24
25#include "checksum.h"
5920b3eb 26#include "network.h"
5d4b8cf2
PJ
27
28struct thread_master *master;
29
30struct acc_vals {
d62a17ae 31 int c0;
32 int c1;
5d4b8cf2
PJ
33};
34
35struct csum_vals {
d62a17ae 36 struct acc_vals a;
37 int x;
38 int y;
5d4b8cf2
PJ
39};
40
41static struct csum_vals ospfd_vals, isisd_vals;
42
43typedef size_t testsz_t;
44typedef uint16_t testoff_t;
45
46/* Fletcher Checksum -- Refer to RFC1008. */
db7d7ba4 47#define MODX 4102U
6b0655a2 48
5d4b8cf2 49/* The final reduction phase.
d62a17ae 50 * This one should be the original ospfd version
5d4b8cf2 51 */
d7c0a89a
QY
52static uint16_t reduce_ospfd(struct csum_vals *vals, testsz_t len,
53 testoff_t off)
5d4b8cf2
PJ
54{
55#define x vals->x
56#define y vals->y
57#define c0 vals->a.c0
58#define c1 vals->a.c1
59
d62a17ae 60 x = ((len - off - 1) * c0 - c1) % 255;
5d4b8cf2 61
d62a17ae 62 if (x <= 0)
63 x += 255;
64 y = 510 - c0 - x;
65 if (y > 255)
66 y -= 255;
67
68 /* take care endian issue. */
69 return htons((x << 8) + y);
5d4b8cf2
PJ
70#undef x
71#undef y
72#undef c0
73#undef c1
74}
75
76/* slightly different concatenation */
d7c0a89a
QY
77static uint16_t reduce_ospfd1(struct csum_vals *vals, testsz_t len,
78 testoff_t off)
5d4b8cf2
PJ
79{
80#define x vals->x
81#define y vals->y
82#define c0 vals->a.c0
83#define c1 vals->a.c1
84
d62a17ae 85 x = ((len - off - 1) * c0 - c1) % 255;
86 if (x <= 0)
87 x += 255;
88 y = 510 - c0 - x;
89 if (y > 255)
90 y -= 255;
5d4b8cf2 91
d62a17ae 92 /* take care endian issue. */
93 return htons((x << 8) | (y & 0xff));
5d4b8cf2
PJ
94#undef x
95#undef y
96#undef c0
97#undef c1
98}
99
100/* original isisd version */
d7c0a89a
QY
101static uint16_t reduce_isisd(struct csum_vals *vals, testsz_t len,
102 testoff_t off)
5d4b8cf2
PJ
103{
104#define x vals->x
105#define y vals->y
106#define c0 vals->a.c0
107#define c1 vals->a.c1
d7c0a89a 108 uint32_t mul;
d62a17ae 109
110 mul = (len - off) * (c0);
111 x = mul - c0 - c1;
112 y = c1 - mul - 1;
5d4b8cf2 113
d62a17ae 114 if (y > 0)
115 y++;
116 if (x < 0)
117 x--;
5d4b8cf2 118
d62a17ae 119 x %= 255;
120 y %= 255;
5d4b8cf2 121
d62a17ae 122 if (x == 0)
123 x = 255;
124 if (y == 0)
125 y = 1;
5d4b8cf2 126
d62a17ae 127 return htons((x << 8) | (y & 0xff));
5d4b8cf2
PJ
128
129#undef x
130#undef y
131#undef c0
132#undef c1
133}
134
135/* Is the -1 in y wrong perhaps? */
d7c0a89a
QY
136static uint16_t reduce_isisd_yfix(struct csum_vals *vals, testsz_t len,
137 testoff_t off)
5d4b8cf2
PJ
138{
139#define x vals->x
140#define y vals->y
141#define c0 vals->a.c0
142#define c1 vals->a.c1
d7c0a89a 143 uint32_t mul;
d62a17ae 144
145 mul = (len - off) * (c0);
146 x = mul - c0 - c1;
147 y = c1 - mul;
5d4b8cf2 148
d62a17ae 149 if (y > 0)
150 y++;
151 if (x < 0)
152 x--;
5d4b8cf2 153
d62a17ae 154 x %= 255;
155 y %= 255;
5d4b8cf2 156
d62a17ae 157 if (x == 0)
158 x = 255;
159 if (y == 0)
160 y = 1;
5d4b8cf2 161
d62a17ae 162 return htons((x << 8) | (y & 0xff));
5d4b8cf2
PJ
163
164#undef x
165#undef y
166#undef c0
167#undef c1
168}
169
170/* Move the mods yp */
d7c0a89a
QY
171static uint16_t reduce_isisd_mod(struct csum_vals *vals, testsz_t len,
172 testoff_t off)
5d4b8cf2
PJ
173{
174#define x vals->x
175#define y vals->y
176#define c0 vals->a.c0
177#define c1 vals->a.c1
d7c0a89a 178 uint32_t mul;
5d4b8cf2 179
d62a17ae 180 mul = (len - off) * (c0);
181 x = mul - c1 - c0;
182 y = c1 - mul - 1;
5d4b8cf2 183
d62a17ae 184 x %= 255;
185 y %= 255;
5d4b8cf2 186
d62a17ae 187 if (y > 0)
188 y++;
189 if (x < 0)
190 x--;
5d4b8cf2 191
d62a17ae 192 if (x == 0)
193 x = 255;
194 if (y == 0)
195 y = 1;
196
197 return htons((x << 8) | (y & 0xff));
5d4b8cf2
PJ
198
199#undef x
200#undef y
201#undef c0
202#undef c1
203}
204
205/* Move the mods up + fix y */
d7c0a89a
QY
206static uint16_t reduce_isisd_mody(struct csum_vals *vals, testsz_t len,
207 testoff_t off)
5d4b8cf2
PJ
208{
209#define x vals->x
210#define y vals->y
211#define c0 vals->a.c0
212#define c1 vals->a.c1
d7c0a89a 213 uint32_t mul;
d62a17ae 214
215 mul = (len - off) * (c0);
216 x = mul - c0 - c1;
217 y = c1 - mul;
5d4b8cf2 218
d62a17ae 219 x %= 255;
220 y %= 255;
5d4b8cf2 221
d62a17ae 222 if (y > 0)
223 y++;
224 if (x < 0)
225 x--;
5d4b8cf2 226
d62a17ae 227 if (x == 0)
228 x = 255;
229 if (y == 0)
230 y = 1;
5d4b8cf2 231
d62a17ae 232 return htons((x << 8) | (y & 0xff));
5d4b8cf2
PJ
233
234#undef x
235#undef y
236#undef c0
237#undef c1
238}
239
240struct reductions_t {
d62a17ae 241 const char *name;
d7c0a89a 242 uint16_t (*f)(struct csum_vals *, testsz_t, testoff_t);
5d4b8cf2 243} reducts[] = {
d62a17ae 244 {.name = "ospfd", .f = reduce_ospfd},
245 {.name = "ospfd-1", .f = reduce_ospfd1},
246 {.name = "isisd", .f = reduce_isisd},
247 {.name = "isisd-yfix", .f = reduce_isisd_yfix},
248 {.name = "isisd-mod", .f = reduce_isisd_mod},
249 {.name = "isisd-mody", .f = reduce_isisd_mody},
250 {NULL, NULL},
5d4b8cf2 251};
6b0655a2 252
5d4b8cf2 253/* The original ospfd checksum */
d7c0a89a 254static uint16_t ospfd_checksum(uint8_t *buffer, testsz_t len, testoff_t off)
5d4b8cf2 255{
d7c0a89a 256 uint8_t *sp, *ep, *p, *q;
d62a17ae 257 int c0 = 0, c1 = 0;
258 int x, y;
d7c0a89a 259 uint16_t checksum, *csum;
d62a17ae 260
d7c0a89a 261 csum = (uint16_t *)(buffer + off);
d62a17ae 262 *(csum) = 0;
263
264 sp = buffer;
265
266 for (ep = sp + len; sp < ep; sp = q) {
267 q = sp + MODX;
268 if (q > ep)
269 q = ep;
270 for (p = sp; p < q; p++) {
271 c0 += *p;
272 c1 += c0;
273 }
274 c0 %= 255;
275 c1 %= 255;
276 }
277
278 ospfd_vals.a.c0 = c0;
279 ospfd_vals.a.c1 = c1;
280
281 // printf ("%s: len %u, off %u, c0 %d, c1 %d\n",
282 // __func__, len, off, c0, c1);
283
284 x = ((int)(len - off - 1) * (int)c0 - (int)c1) % 255;
285
286 if (x <= 0)
287 x += 255;
288 y = 510 - c0 - x;
289 if (y > 255)
290 y -= 255;
291
292 ospfd_vals.x = x;
293 ospfd_vals.y = y;
294
295 buffer[off] = x;
296 buffer[off + 1] = y;
297
298 /* take care endian issue. */
299 checksum = htons((x << 8) | (y & 0xff));
300
301 return (checksum);
5d4b8cf2
PJ
302}
303
304/* the original, broken isisd checksum */
d7c0a89a 305static uint16_t iso_csum_create(uint8_t *buffer, testsz_t len, testoff_t off)
5d4b8cf2
PJ
306{
307
d7c0a89a 308 uint8_t *p;
d62a17ae 309 int x;
310 int y;
d7c0a89a
QY
311 uint32_t mul;
312 uint32_t c0;
313 uint32_t c1;
314 uint16_t checksum, *csum;
d62a17ae 315 int i, init_len, partial_len;
316
317 checksum = 0;
318
d7c0a89a 319 csum = (uint16_t *)(buffer + off);
d62a17ae 320 *(csum) = checksum;
321
322 p = buffer;
323 c0 = 0;
324 c1 = 0;
325 init_len = len;
326
327 while (len != 0) {
328 partial_len = MIN(len, MODX);
329
330 for (i = 0; i < partial_len; i++) {
331 c0 = c0 + *(p++);
332 c1 += c0;
333 }
334
335 c0 = c0 % 255;
336 c1 = c1 % 255;
337
338 len -= partial_len;
5d4b8cf2
PJ
339 }
340
d62a17ae 341 isisd_vals.a.c0 = c0;
342 isisd_vals.a.c1 = c1;
343
344 mul = (init_len - off) * c0;
345
346 x = mul - c1 - c0;
347 y = c1 - mul - 1;
348
349 if (y > 0)
350 y++;
351 if (x < 0)
352 x--;
353
354 x %= 255;
355 y %= 255;
356
357 if (x == 0)
358 x = 255;
359 if (y == 0)
360 y = 1;
361
362 isisd_vals.x = x;
363 isisd_vals.y = y;
364
365 checksum = htons((x << 8) | (y & 0xFF));
366
367 *(csum) = checksum;
368
369 /* return the checksum for user usage */
370 return checksum;
5d4b8cf2
PJ
371}
372
d7c0a89a 373static int verify(uint8_t *buffer, testsz_t len)
5d4b8cf2 374{
d7c0a89a
QY
375 uint8_t *p;
376 uint32_t c0;
377 uint32_t c1;
d62a17ae 378 int i, partial_len;
379
380 p = buffer;
381
382 c0 = 0;
383 c1 = 0;
384
385 while (len) {
386 partial_len = MIN(len, 5803U);
387
388 for (i = 0; i < partial_len; i++) {
389 c0 = c0 + *(p++);
390 c1 += c0;
391 }
392 c0 = c0 % 255;
393 c1 = c1 % 255;
394
395 len -= partial_len;
396 }
397
398 if (c0 == 0 && c1 == 0)
399 return 0;
400
401 return 1;
5d4b8cf2
PJ
402}
403
d62a17ae 404static int /* return checksum in low-order 16 bits */
9d303b37 405 in_cksum_optimized(void *parg, int nbytes)
439c52f1 406{
d7c0a89a 407 unsigned short *ptr = parg;
d62a17ae 408 register long sum; /* assumes long == 32 bits */
d7c0a89a 409 register unsigned short answer; /* assumes unsigned short == 16 bits */
439c52f1
JT
410 register int count;
411 /*
412 * Our algorithm is simple, using a 32-bit accumulator (sum),
413 * we add sequential 16-bit words to it, and at the end, fold back
414 * all the carry bits from the top 16 bits into the lower 16 bits.
415 */
416
417 sum = 0;
418 count = nbytes >> 1; /* div by 2 */
d62a17ae 419 for (ptr--; count; --count)
420 sum += *++ptr;
439c52f1 421
d62a17ae 422 if (nbytes & 1) /* Odd */
d7c0a89a 423 sum += *(uint8_t *)(++ptr); /* one byte only */
439c52f1
JT
424
425 /*
426 * Add back carry outs from top 16 bits to low 16 bits.
427 */
428
d62a17ae 429 sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */
430 sum += (sum >> 16); /* add carry */
431 answer = ~sum; /* ones-complement, then truncate to 16 bits */
432 return (answer);
439c52f1
JT
433}
434
435
1dba254e 436static int /* return checksum in low-order 16 bits */
9d303b37 437 in_cksum_rfc(void *parg, int count)
439c52f1
JT
438/* from RFC 1071 */
439{
d7c0a89a 440 unsigned short *addr = parg;
439c52f1
JT
441 /* Compute Internet Checksum for "count" bytes
442 * beginning at location "addr".
443 */
d62a17ae 444 register long sum = 0;
439c52f1 445
d62a17ae 446 while (count > 1) {
447 /* This is the inner loop */
448 sum += *addr++;
449 count -= 2;
439c52f1
JT
450 }
451 /* Add left-over byte, if any */
452 if (count > 0) {
d7c0a89a 453 sum += *(uint8_t *)addr;
439c52f1
JT
454 }
455
456 /* Fold 32-bit sum to 16 bits */
d62a17ae 457 while (sum >> 16)
458 sum = (sum & 0xffff) + (sum >> 16);
439c52f1
JT
459 return ~sum;
460}
461
462
d62a17ae 463int main(int argc, char **argv)
5d4b8cf2
PJ
464{
465/* 60017 65629 702179 */
466#define MAXDATALEN 60017
d7c0a89a
QY
467#define BUFSIZE MAXDATALEN + sizeof(uint16_t)
468 uint8_t buffer[BUFSIZE];
d62a17ae 469 int exercise = 0;
5d4b8cf2 470#define EXERCISESTEP 257
d62a17ae 471 srandom(time(NULL));
472
473 while (1) {
d7c0a89a 474 uint16_t ospfd, isisd, lib, in_csum, in_csum_res, in_csum_rfc;
d62a17ae 475 int i, j;
476
477 exercise += EXERCISESTEP;
478 exercise %= MAXDATALEN;
479
89087f23
DL
480 printf("\rexercising length %d\033[K", exercise);
481
d62a17ae 482 for (i = 0; i < exercise; i += sizeof(long int)) {
5920b3eb 483 long int rand = frr_weak_random();
d62a17ae 484
485 for (j = sizeof(long int); j > 0; j--)
486 buffer[i + (sizeof(long int) - j)] =
487 (rand >> (j * 8)) & 0xff;
488 }
489
490 in_csum = in_cksum(buffer, exercise);
491 in_csum_res = in_cksum_optimized(buffer, exercise);
492 in_csum_rfc = in_cksum_rfc(buffer, exercise);
493 if (in_csum_res != in_csum || in_csum != in_csum_rfc)
89087f23 494 printf("\nverify: in_chksum failed in_csum:%x, in_csum_res:%x,in_csum_rfc %x, len:%d\n",
d62a17ae 495 in_csum, in_csum_res, in_csum_rfc, exercise);
496
89087f23
DL
497 struct iovec iov[3];
498 uint16_t in_csum_iov;
499
500 iov[0].iov_base = buffer;
501 iov[0].iov_len = exercise / 2;
502 iov[1].iov_base = buffer + iov[0].iov_len;
503 iov[1].iov_len = exercise - iov[0].iov_len;
504
505 in_csum_iov = in_cksumv(iov, 2);
506 if (in_csum_iov != in_csum)
507 printf("\nverify: in_cksumv failed, lens: %zu+%zu\n",
508 iov[0].iov_len, iov[1].iov_len);
509
510 if (exercise >= 6) {
511 /* force split with byte leftover */
512 iov[0].iov_base = buffer;
513 iov[0].iov_len = (exercise / 2) | 1;
514 iov[1].iov_base = buffer + iov[0].iov_len;
515 iov[1].iov_len = 2;
516 iov[2].iov_base = buffer + iov[0].iov_len + 2;
517 iov[2].iov_len = exercise - iov[0].iov_len - 2;
518
519 in_csum_iov = in_cksumv(iov, 3);
520 if (in_csum_iov != in_csum)
521 printf("\nverify: in_cksumv failed, lens: %zu+%zu+%zu, got %04x, expected %04x\n",
522 iov[0].iov_len, iov[1].iov_len,
523 iov[2].iov_len, in_csum_iov, in_csum);
524
525 /* force split without byte leftover */
526 iov[0].iov_base = buffer;
527 iov[0].iov_len = (exercise / 2) & ~1UL;
528 iov[1].iov_base = buffer + iov[0].iov_len;
529 iov[1].iov_len = 2;
530 iov[2].iov_base = buffer + iov[0].iov_len + 2;
531 iov[2].iov_len = exercise - iov[0].iov_len - 2;
532
533 in_csum_iov = in_cksumv(iov, 3);
534 if (in_csum_iov != in_csum)
535 printf("\nverify: in_cksumv failed, lens: %zu+%zu+%zu, got %04x, expected %04x\n",
536 iov[0].iov_len, iov[1].iov_len,
537 iov[2].iov_len, in_csum_iov, in_csum);
538 }
539
d7c0a89a 540 ospfd = ospfd_checksum(buffer, exercise + sizeof(uint16_t),
d62a17ae 541 exercise);
d7c0a89a 542 if (verify(buffer, exercise + sizeof(uint16_t)))
89087f23 543 printf("\nverify: ospfd failed\n");
d7c0a89a 544 isisd = iso_csum_create(buffer, exercise + sizeof(uint16_t),
d62a17ae 545 exercise);
d7c0a89a 546 if (verify(buffer, exercise + sizeof(uint16_t)))
89087f23 547 printf("\nverify: isisd failed\n");
d7c0a89a 548 lib = fletcher_checksum(buffer, exercise + sizeof(uint16_t),
d62a17ae 549 exercise);
d7c0a89a 550 if (verify(buffer, exercise + sizeof(uint16_t)))
89087f23 551 printf("\nverify: lib failed\n");
d62a17ae 552
553 if (ospfd != lib) {
89087f23 554 printf("\nMismatch in values at size %d\n"
d62a17ae 555 "ospfd: 0x%04x\tc0: %d\tc1: %d\tx: %d\ty: %d\n"
556 "isisd: 0x%04x\tc0: %d\tc1: %d\tx: %d\ty: %d\n"
557 "lib: 0x%04x\n",
558 exercise, ospfd, ospfd_vals.a.c0,
559 ospfd_vals.a.c1, ospfd_vals.x, ospfd_vals.y,
560 isisd, isisd_vals.a.c0, isisd_vals.a.c1,
561 isisd_vals.x, isisd_vals.y, lib);
562
563 /* Investigate reduction phase discrepencies */
564 if (ospfd_vals.a.c0 == isisd_vals.a.c0
565 && ospfd_vals.a.c1 == isisd_vals.a.c1) {
566 printf("\n");
567 for (i = 0; reducts[i].name != NULL; i++) {
568 ospfd = reducts[i].f(
569 &ospfd_vals,
d7c0a89a 570 exercise + sizeof(uint16_t),
d62a17ae 571 exercise);
572 printf("%20s: x: %02x, y %02x, checksum 0x%04x\n",
573 reducts[i].name,
574 ospfd_vals.x & 0xff,
575 ospfd_vals.y & 0xff, ospfd);
576 }
577 }
578
d7c0a89a 579 printf("\n uint8_t testdata [] = {\n ");
d62a17ae 580 for (i = 0; i < exercise; i++) {
581 printf("0x%02x,%s", buffer[i],
582 (i + 1) % 8 ? " " : "\n ");
583 }
584 printf("\n}\n");
585 exit(1);
586 }
587 }
5d4b8cf2 588}