]>
Commit | Line | Data |
---|---|---|
718e3744 | 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> | |
34204aac | 10 | #include "checksum.h" |
718e3744 | 11 | |
89087f23 DL |
12 | #define add_carry(dst, add) \ |
13 | do { \ | |
14 | typeof(dst) _add = (add); \ | |
15 | dst += _add; \ | |
16 | if (dst < _add) \ | |
17 | dst++; \ | |
18 | } while (0) | |
19 | ||
20 | uint16_t in_cksumv(const struct iovec *iov, size_t iov_len) | |
718e3744 | 21 | { |
89087f23 DL |
22 | const struct iovec *iov_end; |
23 | uint32_t sum = 0; | |
24 | ||
25 | union { | |
26 | uint8_t bytes[2]; | |
27 | uint16_t word; | |
28 | } wordbuf; | |
29 | bool have_oddbyte = false; | |
718e3744 | 30 | |
31 | /* | |
32 | * Our algorithm is simple, using a 32-bit accumulator (sum), | |
33 | * we add sequential 16-bit words to it, and at the end, fold back | |
34 | * all the carry bits from the top 16 bits into the lower 16 bits. | |
35 | */ | |
36 | ||
89087f23 DL |
37 | for (iov_end = iov + iov_len; iov < iov_end; iov++) { |
38 | const uint8_t *ptr, *end; | |
39 | ||
40 | ptr = (const uint8_t *)iov->iov_base; | |
41 | end = ptr + iov->iov_len; | |
42 | if (ptr == end) | |
43 | continue; | |
44 | ||
45 | if (have_oddbyte) { | |
46 | have_oddbyte = false; | |
47 | wordbuf.bytes[1] = *ptr++; | |
48 | ||
49 | add_carry(sum, wordbuf.word); | |
50 | } | |
51 | ||
52 | while (ptr + 8 <= end) { | |
53 | add_carry(sum, *(const uint32_t *)(ptr + 0)); | |
54 | add_carry(sum, *(const uint32_t *)(ptr + 4)); | |
55 | ptr += 8; | |
56 | } | |
57 | ||
58 | while (ptr + 2 <= end) { | |
59 | add_carry(sum, *(const uint16_t *)ptr); | |
60 | ptr += 2; | |
61 | } | |
62 | ||
63 | if (ptr + 1 <= end) { | |
64 | wordbuf.bytes[0] = *ptr++; | |
65 | have_oddbyte = true; | |
66 | } | |
718e3744 | 67 | } |
68 | ||
d62a17ae | 69 | /* mop up an odd byte, if necessary */ |
89087f23 DL |
70 | if (have_oddbyte) { |
71 | wordbuf.bytes[1] = 0; | |
72 | add_carry(sum, wordbuf.word); | |
718e3744 | 73 | } |
74 | ||
75 | /* | |
76 | * Add back carry outs from top 16 bits to low 16 bits. | |
77 | */ | |
78 | ||
d62a17ae | 79 | sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */ |
80 | sum += (sum >> 16); /* add carry */ | |
89087f23 | 81 | return ~sum; |
17b48d7d QY |
82 | } |
83 | ||
efda3bb8 | 84 | /* Fletcher Checksum -- Refer to RFC1008. */ |
6b143a68 | 85 | #define MODX 4102U /* 5802 should be fine */ |
efda3bb8 | 86 | |
d62a17ae | 87 | /* To be consistent, offset is 0-based index, rather than the 1-based |
efda3bb8 | 88 | index required in the specification ISO 8473, Annex C.1 */ |
d8a4e42b JR |
89 | /* calling with offset == FLETCHER_CHECKSUM_VALIDATE will validate the checksum |
90 | without modifying the buffer; a valid checksum returns 0 */ | |
d7c0a89a QY |
91 | uint16_t fletcher_checksum(uint8_t *buffer, const size_t len, |
92 | const uint16_t offset) | |
efda3bb8 | 93 | { |
d7c0a89a | 94 | uint8_t *p; |
d62a17ae | 95 | int x, y, c0, c1; |
d7c0a89a QY |
96 | uint16_t checksum = 0; |
97 | uint16_t *csum; | |
d62a17ae | 98 | size_t partial_len, i, left = len; |
99 | ||
100 | if (offset != FLETCHER_CHECKSUM_VALIDATE) | |
101 | /* Zero the csum in the packet. */ | |
efda3bb8 | 102 | { |
d62a17ae | 103 | assert(offset |
104 | < (len - 1)); /* account for two bytes of checksum */ | |
d7c0a89a | 105 | csum = (uint16_t *)(buffer + offset); |
d62a17ae | 106 | *(csum) = 0; |
efda3bb8 JD |
107 | } |
108 | ||
d62a17ae | 109 | p = buffer; |
110 | c0 = 0; | |
111 | c1 = 0; | |
112 | ||
113 | while (left != 0) { | |
114 | partial_len = MIN(left, MODX); | |
115 | ||
116 | for (i = 0; i < partial_len; i++) { | |
117 | c0 = c0 + *(p++); | |
118 | c1 += c0; | |
119 | } | |
120 | ||
121 | c0 = c0 % 255; | |
122 | c1 = c1 % 255; | |
123 | ||
124 | left -= partial_len; | |
125 | } | |
126 | ||
127 | /* The cast is important, to ensure the mod is taken as a signed value. | |
128 | */ | |
129 | x = (int)((len - offset - 1) * c0 - c1) % 255; | |
130 | ||
131 | if (x <= 0) | |
132 | x += 255; | |
133 | y = 510 - c0 - x; | |
134 | if (y > 255) | |
135 | y -= 255; | |
136 | ||
137 | if (offset == FLETCHER_CHECKSUM_VALIDATE) { | |
138 | checksum = (c1 << 8) + c0; | |
139 | } else { | |
140 | /* | |
141 | * Now we write this to the packet. | |
142 | * We could skip this step too, since the checksum returned | |
143 | * would | |
144 | * be stored into the checksum field by the caller. | |
145 | */ | |
146 | buffer[offset] = x; | |
147 | buffer[offset + 1] = y; | |
148 | ||
149 | /* Take care of the endian issue */ | |
150 | checksum = htons((x << 8) | (y & 0xFF)); | |
151 | } | |
152 | ||
153 | return checksum; | |
efda3bb8 | 154 | } |