]> git.ipfire.org Git - thirdparty/dhcp.git/blob - common/packet.c
Merge changes between 3.0rc7 and 3.0rc8pl2.
[thirdparty/dhcp.git] / common / packet.c
1 /* packet.c
2
3 Packet assembly code, originally contributed by Archie Cobbs. */
4
5 /*
6 * Copyright (c) 1996-2001 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.41 2001/06/27 00:29:55 mellon Exp $ Copyright (c) 1996-2001 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 */