Annotation of embedaddon/dhcp/common/packet.c, revision 1.1.1.1
1.1 misho 1: /* packet.c
2:
3: Packet assembly code, originally contributed by Archie Cobbs. */
4:
5: /*
1.1.1.1 ! misho 6: * Copyright (c) 2009,2012 by Internet Systems Consortium, Inc. ("ISC")
! 7: * Copyright (c) 2004,2005,2007 by Internet Systems Consortium, Inc. ("ISC")
1.1 misho 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: {
1.1.1.1 ! misho 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;
1.1 misho 114: #endif
115: #if defined (DEC_FDDI)
1.1.1.1 ! misho 116: case HTYPE_FDDI:
! 117: assemble_fddi_header(interface, buf, bufix, to);
! 118: break;
1.1 misho 119: #endif
1.1.1.1 ! misho 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: }
1.1 misho 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... */
1.1.1.1 ! misho 195: /* Support for ethernet, TR and FDDI
! 196: * Doesn't support infiniband yet as the supported oses shouldn't get here
! 197: */
1.1 misho 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: {
1.1.1.1 ! misho 205: switch(interface->hw_address.hbuf[0]) {
1.1 misho 206: #if defined (HAVE_TR_SUPPORT)
1.1.1.1 ! misho 207: case HTYPE_IEEE802:
! 208: return (decode_tr_header(interface, buf, bufix, from));
1.1 misho 209: #endif
210: #if defined (DEC_FDDI)
1.1.1.1 ! misho 211: case HTYPE_FDDI:
! 212: return (decode_fddi_header(interface, buf, bufix, from));
1.1 misho 213: #endif
1.1.1.1 ! misho 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: }
1.1 misho 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)
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: u_int32_t sum, usum;
237: static int ip_packets_seen;
238: static int ip_packets_bad_checksum;
239: static int udp_packets_seen;
240: static int udp_packets_bad_checksum;
241: static int udp_packets_length_checked;
242: static int udp_packets_length_overflow;
243: unsigned len;
244:
245: /* Designate the end of the input buffer for bounds checks. */
246: endbuf = buf + bufix + buflen;
247:
248: /* Assure there is at least an IP header there. */
249: if ((buf + bufix + sizeof(ip)) > endbuf)
250: return -1;
251:
252: /* Copy the IP header into a stack aligned structure for inspection.
253: * There may be bits in the IP header that we're not decoding, so we
254: * copy out the bits we grok and skip ahead by ip.ip_hl * 4.
255: */
256: upp = buf + bufix;
257: memcpy(&ip, upp, sizeof(ip));
258: ip_len = (*upp & 0x0f) << 2;
259: upp += ip_len;
260:
261: /* Check the IP packet length. */
262: pkt_len = ntohs(ip.ip_len);
263: if (pkt_len > buflen)
264: return -1;
265:
266: /* Assure after ip_len bytes that there is enough room for a UDP header. */
267: if ((upp + sizeof(udp)) > endbuf)
268: return -1;
269:
270: /* Copy the UDP header into a stack aligned structure for inspection. */
271: memcpy(&udp, upp, sizeof(udp));
272:
273: #ifdef USERLAND_FILTER
274: /* Is it a UDP packet? */
275: if (ip.ip_p != IPPROTO_UDP)
276: return -1;
277:
278: /* Is it to the port we're serving? */
279: if (udp.uh_dport != local_port)
280: return -1;
281: #endif /* USERLAND_FILTER */
282:
283: ulen = ntohs(udp.uh_ulen);
284: if (ulen < sizeof(udp))
285: return -1;
286:
287: udp_packets_length_checked++;
288: if ((upp + ulen) > endbuf) {
289: udp_packets_length_overflow++;
290: if ((udp_packets_length_checked > 4) &&
291: ((udp_packets_length_checked /
292: udp_packets_length_overflow) < 2)) {
293: log_info("%d udp packets in %d too long - dropped",
294: udp_packets_length_overflow,
295: udp_packets_length_checked);
296: udp_packets_length_overflow = 0;
297: udp_packets_length_checked = 0;
298: }
299: return -1;
300: }
301:
302: if ((ulen < sizeof(udp)) || ((upp + ulen) > endbuf))
303: return -1;
304:
305: /* Check the IP header checksum - it should be zero. */
306: ++ip_packets_seen;
307: if (wrapsum (checksum (buf + bufix, ip_len, 0))) {
308: ++ip_packets_bad_checksum;
309: if (ip_packets_seen > 4 &&
310: (ip_packets_seen / ip_packets_bad_checksum) < 2) {
311: log_info ("%d bad IP checksums seen in %d packets",
312: ip_packets_bad_checksum, ip_packets_seen);
313: ip_packets_seen = ip_packets_bad_checksum = 0;
314: }
315: return -1;
316: }
317:
318: /* Copy out the IP source address... */
319: memcpy(&from->sin_addr, &ip.ip_src, 4);
320:
321: /* Compute UDP checksums, including the ``pseudo-header'', the UDP
322: header and the data. If the UDP checksum field is zero, we're
323: not supposed to do a checksum. */
324:
325: data = upp + sizeof(udp);
326: len = ulen - sizeof(udp);
327:
328: usum = udp.uh_sum;
329: udp.uh_sum = 0;
330:
331: /* XXX: We have to pass &udp, because we have to zero the checksum
332: * field before calculating the sum...'upp' isn't zeroed.
333: */
334: sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
335: checksum(data, len,
336: checksum((unsigned char *)&ip.ip_src,
337: 8, IPPROTO_UDP + ulen))));
338:
339: udp_packets_seen++;
340: if (usum && usum != sum) {
341: udp_packets_bad_checksum++;
342: if (udp_packets_seen > 4 &&
343: (udp_packets_seen / udp_packets_bad_checksum) < 2) {
344: log_info ("%d bad udp checksums in %d packets",
345: udp_packets_bad_checksum, udp_packets_seen);
346: udp_packets_seen = udp_packets_bad_checksum = 0;
347: }
348: return -1;
349: }
350:
351: /* Copy out the port... */
352: memcpy (&from -> sin_port, &udp.uh_sport, sizeof udp.uh_sport);
353:
354: /* Save the length of the UDP payload. */
355: if (rbuflen != NULL)
356: *rbuflen = len;
357:
358: /* Return the index to the UDP payload. */
359: return ip_len + sizeof udp;
360: }
361: #endif /* PACKET_DECODING */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>