]> git.ipfire.org Git - thirdparty/dhcp.git/blob - common/packet.c
[master] Checkum handling fixes
[thirdparty/dhcp.git] / common / packet.c
1 /* packet.c
2
3 Packet assembly code, originally contributed by Archie Cobbs. */
4
5 /*
6 * Copyright (c) 2009,2012,2014 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 2004,2005,2007 by Internet Systems Consortium, Inc. ("ISC")
8 * Copyright (c) 1996-2003 by Internet Software Consortium
9 *
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
20 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 *
22 * Internet Systems Consortium, Inc.
23 * 950 Charter Street
24 * Redwood City, CA 94063
25 * <info@isc.org>
26 * https://www.isc.org/
27 *
28 * This code was originally contributed by Archie Cobbs, and is still
29 * very similar to that contribution, although the packet checksum code
30 * has been hacked significantly with the help of quite a few ISC DHCP
31 * users, without whose gracious and thorough help the checksum code would
32 * still be disabled.
33 */
34
35 #include "dhcpd.h"
36
37 #if defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING)
38 #include "includes/netinet/ip.h"
39 #include "includes/netinet/udp.h"
40 #include "includes/netinet/if_ether.h"
41 #endif /* PACKET_ASSEMBLY || PACKET_DECODING */
42
43 /* Compute the easy part of the checksum on a range of bytes. */
44
45 u_int32_t checksum (buf, nbytes, sum)
46 unsigned char *buf;
47 unsigned nbytes;
48 u_int32_t sum;
49 {
50 unsigned i;
51
52 #ifdef DEBUG_CHECKSUM
53 log_debug ("checksum (%x %d %x)", buf, nbytes, sum);
54 #endif
55
56 /* Checksum all the pairs of bytes first... */
57 for (i = 0; i < (nbytes & ~1U); i += 2) {
58 #ifdef DEBUG_CHECKSUM_VERBOSE
59 log_debug ("sum = %x", sum);
60 #endif
61 sum += (u_int16_t) ntohs(*((u_int16_t *)(buf + i)));
62 /* Add carry. */
63 if (sum > 0xFFFF)
64 sum -= 0xFFFF;
65 }
66
67 /* If there's a single byte left over, checksum it, too. Network
68 byte order is big-endian, so the remaining byte is the high byte. */
69 if (i < nbytes) {
70 #ifdef DEBUG_CHECKSUM_VERBOSE
71 log_debug ("sum = %x", sum);
72 #endif
73 sum += buf [i] << 8;
74 /* Add carry. */
75 if (sum > 0xFFFF)
76 sum -= 0xFFFF;
77 }
78
79 return sum;
80 }
81
82 /* Finish computing the checksum, and then put it into network byte order. */
83
84 u_int32_t wrapsum (sum)
85 u_int32_t sum;
86 {
87 #ifdef DEBUG_CHECKSUM
88 log_debug ("wrapsum (%x)", sum);
89 #endif
90
91 sum = ~sum & 0xFFFF;
92 #ifdef DEBUG_CHECKSUM_VERBOSE
93 log_debug ("sum = %x", sum);
94 #endif
95
96 #ifdef DEBUG_CHECKSUM
97 log_debug ("wrapsum returns %x", htons (sum));
98 #endif
99 return htons(sum);
100 }
101
102 #ifdef PACKET_ASSEMBLY
103 void assemble_hw_header (interface, buf, bufix, to)
104 struct interface_info *interface;
105 unsigned char *buf;
106 unsigned *bufix;
107 struct hardware *to;
108 {
109 switch (interface->hw_address.hbuf[0]) {
110 #if defined(HAVE_TR_SUPPORT)
111 case HTYPE_IEEE802:
112 assemble_tr_header(interface, buf, bufix, to);
113 break;
114 #endif
115 #if defined (DEC_FDDI)
116 case HTYPE_FDDI:
117 assemble_fddi_header(interface, buf, bufix, to);
118 break;
119 #endif
120 case HTYPE_INFINIBAND:
121 log_error("Attempt to assemble hw header for infiniband");
122 break;
123 case HTYPE_ETHER:
124 default:
125 assemble_ethernet_header(interface, buf, bufix, to);
126 break;
127 }
128 }
129
130 /* UDP header and IP header assembled together for convenience. */
131
132 void assemble_udp_ip_header (interface, buf, bufix,
133 from, to, port, data, len)
134 struct interface_info *interface;
135 unsigned char *buf;
136 unsigned *bufix;
137 u_int32_t from;
138 u_int32_t to;
139 u_int32_t port;
140 unsigned char *data;
141 unsigned len;
142 {
143 struct ip ip;
144 struct udphdr udp;
145
146 memset (&ip, 0, sizeof ip);
147
148 /* Fill out the IP header */
149 IP_V_SET (&ip, 4);
150 IP_HL_SET (&ip, 20);
151 ip.ip_tos = IPTOS_LOWDELAY;
152 ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len);
153 ip.ip_id = 0;
154 ip.ip_off = 0;
155 ip.ip_ttl = 128;
156 ip.ip_p = IPPROTO_UDP;
157 ip.ip_sum = 0;
158 ip.ip_src.s_addr = from;
159 ip.ip_dst.s_addr = to;
160
161 /* Checksum the IP header... */
162 ip.ip_sum = wrapsum (checksum ((unsigned char *)&ip, sizeof ip, 0));
163
164 /* Copy the ip header into the buffer... */
165 memcpy (&buf [*bufix], &ip, sizeof ip);
166 *bufix += sizeof ip;
167
168 /* Fill out the UDP header */
169 udp.uh_sport = local_port; /* XXX */
170 udp.uh_dport = port; /* XXX */
171 udp.uh_ulen = htons(sizeof(udp) + len);
172 memset (&udp.uh_sum, 0, sizeof udp.uh_sum);
173
174 /* Compute UDP checksums, including the ``pseudo-header'', the UDP
175 header and the data. */
176
177 udp.uh_sum =
178 wrapsum (checksum ((unsigned char *)&udp, sizeof udp,
179 checksum (data, len,
180 checksum ((unsigned char *)
181 &ip.ip_src,
182 2 * sizeof ip.ip_src,
183 IPPROTO_UDP +
184 (u_int32_t)
185 ntohs (udp.uh_ulen)))));
186
187 /* Copy the udp header into the buffer... */
188 memcpy (&buf [*bufix], &udp, sizeof udp);
189 *bufix += sizeof udp;
190 }
191 #endif /* PACKET_ASSEMBLY */
192
193 #ifdef PACKET_DECODING
194 /* Decode a hardware header... */
195 /* Support for ethernet, TR and FDDI
196 * Doesn't support infiniband yet as the supported oses shouldn't get here
197 */
198
199 ssize_t decode_hw_header (interface, buf, bufix, from)
200 struct interface_info *interface;
201 unsigned char *buf;
202 unsigned bufix;
203 struct hardware *from;
204 {
205 switch(interface->hw_address.hbuf[0]) {
206 #if defined (HAVE_TR_SUPPORT)
207 case HTYPE_IEEE802:
208 return (decode_tr_header(interface, buf, bufix, from));
209 #endif
210 #if defined (DEC_FDDI)
211 case HTYPE_FDDI:
212 return (decode_fddi_header(interface, buf, bufix, from));
213 #endif
214 case HTYPE_INFINIBAND:
215 log_error("Attempt to decode hw header for infiniband");
216 return (0);
217 case HTYPE_ETHER:
218 default:
219 return (decode_ethernet_header(interface, buf, bufix, from));
220 }
221 }
222
223 /* UDP header and IP header decoded together for convenience. */
224
225 ssize_t
226 decode_udp_ip_header(struct interface_info *interface,
227 unsigned char *buf, unsigned bufix,
228 struct sockaddr_in *from, unsigned buflen,
229 unsigned *rbuflen, int csum_ready)
230 {
231 unsigned char *data;
232 struct ip ip;
233 struct udphdr udp;
234 unsigned char *upp, *endbuf;
235 u_int32_t ip_len, ulen, pkt_len;
236 static unsigned int ip_packets_seen = 0;
237 static unsigned int ip_packets_bad_checksum = 0;
238 static unsigned int udp_packets_seen = 0;
239 static unsigned int udp_packets_bad_checksum = 0;
240 static unsigned int udp_packets_length_checked = 0;
241 static unsigned int udp_packets_length_overflow = 0;
242 unsigned len;
243
244 /* Designate the end of the input buffer for bounds checks. */
245 endbuf = buf + bufix + buflen;
246
247 /* Assure there is at least an IP header there. */
248 if ((buf + bufix + sizeof(ip)) > endbuf)
249 return -1;
250
251 /* Copy the IP header into a stack aligned structure for inspection.
252 * There may be bits in the IP header that we're not decoding, so we
253 * copy out the bits we grok and skip ahead by ip.ip_hl * 4.
254 */
255 upp = buf + bufix;
256 memcpy(&ip, upp, sizeof(ip));
257 ip_len = (*upp & 0x0f) << 2;
258 upp += ip_len;
259
260 /* Check the IP packet length. */
261 pkt_len = ntohs(ip.ip_len);
262 if (pkt_len > buflen)
263 return -1;
264
265 /* Assure after ip_len bytes that there is enough room for a UDP header. */
266 if ((upp + sizeof(udp)) > endbuf)
267 return -1;
268
269 /* Copy the UDP header into a stack aligned structure for inspection. */
270 memcpy(&udp, upp, sizeof(udp));
271
272 #ifdef USERLAND_FILTER
273 /* Is it a UDP packet? */
274 if (ip.ip_p != IPPROTO_UDP)
275 return -1;
276
277 /* Is it to the port we're serving? */
278 if (udp.uh_dport != local_port)
279 return -1;
280 #endif /* USERLAND_FILTER */
281
282 ulen = ntohs(udp.uh_ulen);
283 if (ulen < sizeof(udp))
284 return -1;
285
286 udp_packets_length_checked++;
287 if ((upp + ulen) > endbuf) {
288 udp_packets_length_overflow++;
289 if (((udp_packets_length_checked > 4) &&
290 (udp_packets_length_overflow != 0)) &&
291 ((udp_packets_length_checked / udp_packets_length_overflow) < 2)) {
292 log_info("%u udp packets in %u too long - dropped",
293 udp_packets_length_overflow,
294 udp_packets_length_checked);
295 udp_packets_length_overflow = 0;
296 udp_packets_length_checked = 0;
297 }
298 return -1;
299 }
300
301 /* If at least 5 with less than 50% bad, start over */
302 if (udp_packets_length_checked > 4) {
303 udp_packets_length_overflow = 0;
304 udp_packets_length_checked = 0;
305 }
306
307 /* Check the IP header checksum - it should be zero. */
308 ip_packets_seen++;
309 if (wrapsum (checksum (buf + bufix, ip_len, 0))) {
310 ++ip_packets_bad_checksum;
311 if (((ip_packets_seen > 4) && (ip_packets_bad_checksum != 0)) &&
312 ((ip_packets_seen / ip_packets_bad_checksum) < 2)) {
313 log_info ("%u bad IP checksums seen in %u packets",
314 ip_packets_bad_checksum, ip_packets_seen);
315 ip_packets_seen = ip_packets_bad_checksum = 0;
316 }
317 return -1;
318 }
319
320 /* If at least 5 with less than 50% bad, start over */
321 if (ip_packets_seen > 4) {
322 ip_packets_bad_checksum = 0;
323 ip_packets_seen = 0;
324 }
325
326 /* Copy out the IP source address... */
327 memcpy(&from->sin_addr, &ip.ip_src, 4);
328
329 data = upp + sizeof(udp);
330 len = ulen - sizeof(udp);
331
332 /* UDP check sum may be optional (udp.uh_sum == 0) or not ready if checksum
333 * offloading is in use */
334 udp_packets_seen++;
335 if (udp.uh_sum && csum_ready) {
336 /* Check the UDP header checksum - since the received packet header
337 * contains the UDP checksum calculated by the transmitter, calculating
338 * it now should come out to zero. */
339 if (wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
340 checksum(data, len,
341 checksum((unsigned char *)&ip.ip_src,
342 8, IPPROTO_UDP + ulen))))) {
343 udp_packets_bad_checksum++;
344 if (((udp_packets_seen > 4) && (udp_packets_bad_checksum != 0))
345 && ((udp_packets_seen / udp_packets_bad_checksum) < 2)) {
346 log_info ("%u bad udp checksums in %u packets",
347 udp_packets_bad_checksum, udp_packets_seen);
348 udp_packets_seen = udp_packets_bad_checksum = 0;
349 }
350
351 return -1;
352 }
353 }
354
355 /* If at least 5 with less than 50% bad, start over */
356 if (udp_packets_seen > 4) {
357 udp_packets_bad_checksum = 0;
358 udp_packets_seen = 0;
359 }
360
361 /* Copy out the port... */
362 memcpy (&from -> sin_port, &udp.uh_sport, sizeof udp.uh_sport);
363
364 /* Save the length of the UDP payload. */
365 if (rbuflen != NULL)
366 *rbuflen = len;
367
368 /* Return the index to the UDP payload. */
369 return ip_len + sizeof udp;
370 }
371 #endif /* PACKET_DECODING */