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