Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Checksum routine for Internet Protocol family headers (C Version). |
3 | | * |
4 | | * Refer to "Computing the Internet Checksum" by R. Braden, D. Borman and |
5 | | * C. Partridge, Computer Communication Review, Vol. 19, No. 2, April 1989, |
6 | | * pp. 86-101, for additional details on computing this checksum. |
7 | | */ |
8 | | |
9 | | #include <zebra.h> |
10 | | #include "checksum.h" |
11 | | |
12 | | #define add_carry(dst, add) \ |
13 | 7.25M | do { \ |
14 | 7.25M | typeof(dst) _add = (add); \ |
15 | 7.25M | dst += _add; \ |
16 | 7.25M | if (dst < _add) \ |
17 | 7.25M | dst++; \ |
18 | 7.25M | } while (0) |
19 | | |
20 | | uint16_t in_cksumv(const struct iovec *iov, size_t iov_len) |
21 | 7.44k | { |
22 | 7.44k | const struct iovec *iov_end; |
23 | 7.44k | uint32_t sum = 0; |
24 | 7.44k | register unsigned short answer; /* assumes unsigned short == 16 bits */ |
25 | | |
26 | 7.44k | union { |
27 | 7.44k | uint8_t bytes[2]; |
28 | 7.44k | uint16_t word; |
29 | 7.44k | } wordbuf; |
30 | 7.44k | bool have_oddbyte = false; |
31 | | |
32 | | /* |
33 | | * Our algorithm is simple, using a 32-bit accumulator (sum), |
34 | | * we add sequential 16-bit words to it, and at the end, fold back |
35 | | * all the carry bits from the top 16 bits into the lower 16 bits. |
36 | | */ |
37 | | |
38 | 14.8k | for (iov_end = iov + iov_len; iov < iov_end; iov++) { |
39 | 7.44k | const uint8_t *ptr, *end; |
40 | | |
41 | 7.44k | ptr = (const uint8_t *)iov->iov_base; |
42 | 7.44k | end = ptr + iov->iov_len; |
43 | 7.44k | if (ptr == end) |
44 | 0 | continue; |
45 | | |
46 | 7.44k | if (have_oddbyte) { |
47 | 0 | have_oddbyte = false; |
48 | 0 | wordbuf.bytes[1] = *ptr++; |
49 | |
|
50 | 0 | add_carry(sum, wordbuf.word); |
51 | 0 | } |
52 | | |
53 | 3.63M | while (ptr + 8 <= end) { |
54 | 3.62M | add_carry(sum, *(const uint32_t *)(ptr + 0)); |
55 | 3.62M | add_carry(sum, *(const uint32_t *)(ptr + 4)); |
56 | 3.62M | ptr += 8; |
57 | 3.62M | } |
58 | | |
59 | 16.2k | while (ptr + 2 <= end) { |
60 | 8.84k | add_carry(sum, *(const uint16_t *)ptr); |
61 | 8.84k | ptr += 2; |
62 | 8.84k | } |
63 | | |
64 | 7.44k | if (ptr + 1 <= end) { |
65 | 859 | wordbuf.bytes[0] = *ptr++; |
66 | 859 | have_oddbyte = true; |
67 | 859 | } |
68 | 7.44k | } |
69 | | |
70 | | /* mop up an odd byte, if necessary */ |
71 | 7.44k | if (have_oddbyte) { |
72 | 859 | wordbuf.bytes[1] = 0; |
73 | 859 | add_carry(sum, wordbuf.word); |
74 | 859 | } |
75 | | |
76 | | /* |
77 | | * Add back carry outs from top 16 bits to low 16 bits. |
78 | | */ |
79 | | |
80 | 7.44k | sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */ |
81 | 7.44k | sum += (sum >> 16); /* add carry */ |
82 | | /* ones-complement, then truncate to 16 bits */ |
83 | 7.44k | answer = (unsigned short)~sum; |
84 | 7.44k | return (answer); |
85 | 7.44k | } |
86 | | |
87 | | /* Fletcher Checksum -- Refer to RFC1008. */ |
88 | | #define MODX 4102U /* 5802 should be fine */ |
89 | | |
90 | | /* To be consistent, offset is 0-based index, rather than the 1-based |
91 | | index required in the specification ISO 8473, Annex C.1 */ |
92 | | /* calling with offset == FLETCHER_CHECKSUM_VALIDATE will validate the checksum |
93 | | without modifying the buffer; a valid checksum returns 0 */ |
94 | | uint16_t fletcher_checksum(uint8_t *buffer, const size_t len, |
95 | | const uint16_t offset) |
96 | | __attribute__((no_sanitize("unsigned-integer-overflow"))) |
97 | 47.4k | { |
98 | 47.4k | uint8_t *p; |
99 | 47.4k | int x, y, c0, c1; |
100 | 47.4k | uint16_t checksum = 0; |
101 | 47.4k | uint16_t *csum; |
102 | 47.4k | size_t partial_len, i, left = len; |
103 | | |
104 | 47.4k | if (offset != FLETCHER_CHECKSUM_VALIDATE) |
105 | | /* Zero the csum in the packet. */ |
106 | 367 | { |
107 | 367 | assert(offset |
108 | 367 | < (len - 1)); /* account for two bytes of checksum */ |
109 | 367 | csum = (uint16_t *)(buffer + offset); |
110 | 367 | *(csum) = 0; |
111 | 367 | } |
112 | | |
113 | 47.4k | p = buffer; |
114 | 47.4k | c0 = 0; |
115 | 47.4k | c1 = 0; |
116 | | |
117 | 94.8k | while (left != 0) { |
118 | 47.4k | partial_len = MIN(left, MODX); |
119 | | |
120 | 1.71M | for (i = 0; i < partial_len; i++) { |
121 | 1.66M | c0 = c0 + *(p++); |
122 | 1.66M | c1 += c0; |
123 | 1.66M | } |
124 | | |
125 | 47.4k | c0 = c0 % 255; |
126 | 47.4k | c1 = c1 % 255; |
127 | | |
128 | 47.4k | left -= partial_len; |
129 | 47.4k | } |
130 | | |
131 | | /* The cast is important, to ensure the mod is taken as a signed value. |
132 | | */ |
133 | 47.4k | x = (int)((len - offset - 1) * c0 - c1) % 255; |
134 | | |
135 | 47.4k | if (x <= 0) |
136 | 47.2k | x += 255; |
137 | 47.4k | y = 510 - c0 - x; |
138 | 47.4k | if (y > 255) |
139 | 14.7k | y -= 255; |
140 | | |
141 | 47.4k | if (offset == FLETCHER_CHECKSUM_VALIDATE) { |
142 | 47.0k | checksum = (c1 << 8) + c0; |
143 | 47.0k | } else { |
144 | | /* |
145 | | * Now we write this to the packet. |
146 | | * We could skip this step too, since the checksum returned |
147 | | * would |
148 | | * be stored into the checksum field by the caller. |
149 | | */ |
150 | 367 | buffer[offset] = x; |
151 | 367 | buffer[offset + 1] = y; |
152 | | |
153 | | /* Take care of the endian issue */ |
154 | 367 | checksum = htons((x << 8) | (y & 0xFF)); |
155 | 367 | } |
156 | | |
157 | 47.4k | return checksum; |
158 | 47.4k | } |