]>
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 | |
d62a17ae | 12 | int /* return checksum in low-order 16 bits */ |
9d303b37 | 13 | in_cksum(void *parg, int nbytes) |
718e3744 | 14 | { |
d7c0a89a | 15 | unsigned short *ptr = parg; |
d62a17ae | 16 | register long sum; /* assumes long == 32 bits */ |
d7c0a89a QY |
17 | unsigned short oddbyte; |
18 | register unsigned short answer; /* assumes unsigned short == 16 bits */ | |
718e3744 | 19 | |
20 | /* | |
21 | * Our algorithm is simple, using a 32-bit accumulator (sum), | |
22 | * we add sequential 16-bit words to it, and at the end, fold back | |
23 | * all the carry bits from the top 16 bits into the lower 16 bits. | |
24 | */ | |
25 | ||
26 | sum = 0; | |
d62a17ae | 27 | while (nbytes > 1) { |
718e3744 | 28 | sum += *ptr++; |
29 | nbytes -= 2; | |
30 | } | |
31 | ||
d62a17ae | 32 | /* mop up an odd byte, if necessary */ |
718e3744 | 33 | if (nbytes == 1) { |
d62a17ae | 34 | oddbyte = 0; /* make sure top half is zero */ |
d7c0a89a | 35 | *((uint8_t *)&oddbyte) = *(uint8_t *)ptr; /* one byte only */ |
718e3744 | 36 | sum += oddbyte; |
37 | } | |
38 | ||
39 | /* | |
40 | * Add back carry outs from top 16 bits to low 16 bits. | |
41 | */ | |
42 | ||
d62a17ae | 43 | sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */ |
44 | sum += (sum >> 16); /* add carry */ | |
45 | answer = ~sum; /* ones-complement, then truncate to 16 bits */ | |
46 | return (answer); | |
718e3744 | 47 | } |
efda3bb8 | 48 | |
17b48d7d QY |
49 | int in_cksum_with_ph4(struct ipv4_ph *ph, void *data, int nbytes) |
50 | { | |
51 | uint8_t dat[sizeof(struct ipv4_ph) + nbytes]; | |
52 | ||
53 | memcpy(dat, ph, sizeof(struct ipv4_ph)); | |
54 | memcpy(dat + sizeof(struct ipv4_ph), data, nbytes); | |
55 | return in_cksum(dat, sizeof(dat)); | |
56 | } | |
57 | ||
58 | int in_cksum_with_ph6(struct ipv6_ph *ph, void *data, int nbytes) | |
59 | { | |
60 | uint8_t dat[sizeof(struct ipv6_ph) + nbytes]; | |
61 | ||
62 | memcpy(dat, ph, sizeof(struct ipv6_ph)); | |
63 | memcpy(dat + sizeof(struct ipv6_ph), data, nbytes); | |
64 | return in_cksum(dat, sizeof(dat)); | |
65 | } | |
66 | ||
efda3bb8 | 67 | /* Fletcher Checksum -- Refer to RFC1008. */ |
6b143a68 | 68 | #define MODX 4102U /* 5802 should be fine */ |
efda3bb8 | 69 | |
d62a17ae | 70 | /* To be consistent, offset is 0-based index, rather than the 1-based |
efda3bb8 | 71 | index required in the specification ISO 8473, Annex C.1 */ |
d8a4e42b JR |
72 | /* calling with offset == FLETCHER_CHECKSUM_VALIDATE will validate the checksum |
73 | without modifying the buffer; a valid checksum returns 0 */ | |
d7c0a89a QY |
74 | uint16_t fletcher_checksum(uint8_t *buffer, const size_t len, |
75 | const uint16_t offset) | |
efda3bb8 | 76 | { |
d7c0a89a | 77 | uint8_t *p; |
d62a17ae | 78 | int x, y, c0, c1; |
d7c0a89a QY |
79 | uint16_t checksum = 0; |
80 | uint16_t *csum; | |
d62a17ae | 81 | size_t partial_len, i, left = len; |
82 | ||
83 | if (offset != FLETCHER_CHECKSUM_VALIDATE) | |
84 | /* Zero the csum in the packet. */ | |
efda3bb8 | 85 | { |
d62a17ae | 86 | assert(offset |
87 | < (len - 1)); /* account for two bytes of checksum */ | |
d7c0a89a | 88 | csum = (uint16_t *)(buffer + offset); |
d62a17ae | 89 | *(csum) = 0; |
efda3bb8 JD |
90 | } |
91 | ||
d62a17ae | 92 | p = buffer; |
93 | c0 = 0; | |
94 | c1 = 0; | |
95 | ||
96 | while (left != 0) { | |
97 | partial_len = MIN(left, MODX); | |
98 | ||
99 | for (i = 0; i < partial_len; i++) { | |
100 | c0 = c0 + *(p++); | |
101 | c1 += c0; | |
102 | } | |
103 | ||
104 | c0 = c0 % 255; | |
105 | c1 = c1 % 255; | |
106 | ||
107 | left -= partial_len; | |
108 | } | |
109 | ||
110 | /* The cast is important, to ensure the mod is taken as a signed value. | |
111 | */ | |
112 | x = (int)((len - offset - 1) * c0 - c1) % 255; | |
113 | ||
114 | if (x <= 0) | |
115 | x += 255; | |
116 | y = 510 - c0 - x; | |
117 | if (y > 255) | |
118 | y -= 255; | |
119 | ||
120 | if (offset == FLETCHER_CHECKSUM_VALIDATE) { | |
121 | checksum = (c1 << 8) + c0; | |
122 | } else { | |
123 | /* | |
124 | * Now we write this to the packet. | |
125 | * We could skip this step too, since the checksum returned | |
126 | * would | |
127 | * be stored into the checksum field by the caller. | |
128 | */ | |
129 | buffer[offset] = x; | |
130 | buffer[offset + 1] = y; | |
131 | ||
132 | /* Take care of the endian issue */ | |
133 | checksum = htons((x << 8) | (y & 0xFF)); | |
134 | } | |
135 | ||
136 | return checksum; | |
efda3bb8 | 137 | } |