Fix warning:
[dragonfly.git] / contrib / dhcp-3.0 / common / packet.c
1 /* packet.c
2
3    Packet assembly code, originally contributed by Archie Cobbs. */
4
5 /*
6  * Copyright (c) 2004 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
35 static char copyright[] =
36 "$Id: packet.c,v 1.40.2.4 2004/11/24 17:39:16 dhankins Exp $ Copyright (c) 2004 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
49 u_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
88 u_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
107 void 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)
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
129 void 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 = 16;
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
192 ssize_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)
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
213 ssize_t decode_udp_ip_header (interface, buf, bufix, from, buflen)
214         struct interface_info *interface;
215         unsigned char *buf;
216         unsigned bufix;
217         struct sockaddr_in *from;
218         unsigned buflen;
219 {
220   unsigned char *data;
221   struct ip *ip;
222   struct udphdr *udp;
223   u_int32_t ip_len = (buf [bufix] & 0xf) << 2;
224   u_int32_t sum, usum;
225   static int ip_packets_seen;
226   static int ip_packets_bad_checksum;
227   static int udp_packets_seen;
228   static int udp_packets_bad_checksum;
229   static int udp_packets_length_checked;
230   static int udp_packets_length_overflow;
231   unsigned len;
232   unsigned ulen;
233   int ignore = 0;
234
235   ip = (struct ip *)(buf + bufix);
236   udp = (struct udphdr *)(buf + bufix + ip_len);
237
238 #ifdef USERLAND_FILTER
239   /* Is it a UDP packet? */
240   if (ip -> ip_p != IPPROTO_UDP)
241           return -1;
242
243   /* Is it to the port we're serving? */
244   if (udp -> uh_dport != local_port)
245           return -1;
246 #endif /* USERLAND_FILTER */
247
248   ulen = ntohs (udp -> uh_ulen);
249   if (ulen < sizeof *udp ||
250       ((unsigned char *)udp) + ulen > buf + bufix + buflen) {
251           log_info ("bogus UDP packet length: %d", ulen);
252           return -1;
253   }
254
255   /* Check the IP header checksum - it should be zero. */
256   ++ip_packets_seen;
257   if (wrapsum (checksum (buf + bufix, ip_len, 0))) {
258           ++ip_packets_bad_checksum;
259           if (ip_packets_seen > 4 &&
260               (ip_packets_seen / ip_packets_bad_checksum) < 2) {
261                   log_info ("%d bad IP checksums seen in %d packets",
262                             ip_packets_bad_checksum, ip_packets_seen);
263                   ip_packets_seen = ip_packets_bad_checksum = 0;
264           }
265           return -1;
266   }
267
268   /* Check the IP packet length. */
269   if (ntohs (ip -> ip_len) != buflen) {
270           if ((ntohs (ip -> ip_len + 2) & ~1) == buflen)
271                   ignore = 1;
272           else
273                   log_debug ("ip length %d disagrees with bytes received %d.",
274                              ntohs (ip -> ip_len), buflen);
275   }
276
277   /* Copy out the IP source address... */
278   memcpy (&from -> sin_addr, &ip -> ip_src, 4);
279
280   /* Compute UDP checksums, including the ``pseudo-header'', the UDP
281      header and the data.   If the UDP checksum field is zero, we're
282      not supposed to do a checksum. */
283
284   data = buf + bufix + ip_len + sizeof *udp;
285   len = ulen - sizeof *udp;
286   ++udp_packets_length_checked;
287   if (len + data > buf + bufix + buflen) {
288           ++udp_packets_length_overflow;
289           if (udp_packets_length_checked > 4 &&
290               (udp_packets_length_checked /
291                udp_packets_length_overflow) < 2) {
292                   log_info ("%d udp packets in %d too long - dropped",
293                             udp_packets_length_overflow,
294                             udp_packets_length_checked);
295                   udp_packets_length_overflow =
296                           udp_packets_length_checked = 0;
297           }
298           return -1;
299   }
300   if (len + data < buf + bufix + buflen &&
301       len + data != buf + bufix + buflen && !ignore)
302           log_debug ("accepting packet with data after udp payload.");
303   if (len + data > buf + bufix + buflen) {
304           log_debug ("dropping packet with bogus uh_ulen %ld",
305                      (long)(len + sizeof *udp));
306           return -1;
307   }
308
309   usum = udp -> uh_sum;
310   udp -> uh_sum = 0;
311
312   sum = wrapsum (checksum ((unsigned char *)udp, sizeof *udp,
313                            checksum (data, len,
314                                      checksum ((unsigned char *)
315                                                &ip -> ip_src,
316                                                2 * sizeof ip -> ip_src,
317                                                IPPROTO_UDP +
318                                                (u_int32_t)ulen))));
319
320   udp_packets_seen++;
321   if (usum && usum != sum) {
322           udp_packets_bad_checksum++;
323           if (udp_packets_seen > 4 &&
324               (udp_packets_seen / udp_packets_bad_checksum) < 2) {
325                   log_info ("%d bad udp checksums in %d packets",
326                             udp_packets_bad_checksum, udp_packets_seen);
327                   udp_packets_seen = udp_packets_bad_checksum = 0;
328           }
329           return -1;
330   }
331
332   /* Copy out the port... */
333   memcpy (&from -> sin_port, &udp -> uh_sport, sizeof udp -> uh_sport);
334
335   return ip_len + sizeof *udp;
336 }
337 #endif /* PACKET_DECODING */