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