]> git.ipfire.org Git - thirdparty/dhcp.git/blame - common/packet.c
Merge changes between 3.0rc7 and 3.0rc8pl2.
[thirdparty/dhcp.git] / common / packet.c
CommitLineData
d29ce033 1/* packet.c
3f368088 2
d29ce033 3 Packet assembly code, originally contributed by Archie Cobbs. */
3f368088
TL
4
5/*
d758ad8c 6 * Copyright (c) 1996-2001 Internet Software Consortium.
49733f31 7 * All rights reserved.
3f368088 8 *
49733f31
TL
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
3f368088 12 *
49733f31
TL
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.
3f368088 21 *
49733f31
TL
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.
3f368088
TL
41 */
42
d29ce033
TL
43#ifndef lint
44static char copyright[] =
d758ad8c 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";
d29ce033
TL
46#endif /* not lint */
47
48#include "dhcpd.h"
714ef4f5 49
886f6a98 50#if defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING)
886f6a98
TL
51#include "includes/netinet/ip.h"
52#include "includes/netinet/udp.h"
53#include "includes/netinet/if_ether.h"
714ef4f5 54#endif /* PACKET_ASSEMBLY || PACKET_DECODING */
3f368088 55
886f6a98
TL
56/* Compute the easy part of the checksum on a range of bytes. */
57
fa805637 58u_int32_t checksum (buf, nbytes, sum)
886f6a98 59 unsigned char *buf;
b1b7b521 60 unsigned nbytes;
886f6a98
TL
61 u_int32_t sum;
62{
b1b7b521 63 unsigned i;
886f6a98 64
b081aeaf 65#ifdef DEBUG_CHECKSUM
8ae2d595 66 log_debug ("checksum (%x %d %x)", buf, nbytes, sum);
b081aeaf
TL
67#endif
68
886f6a98 69 /* Checksum all the pairs of bytes first... */
b1b7b521 70 for (i = 0; i < (nbytes & ~1U); i += 2) {
b081aeaf 71#ifdef DEBUG_CHECKSUM_VERBOSE
8ae2d595 72 log_debug ("sum = %x", sum);
b081aeaf 73#endif
ac16c3da 74 sum += (u_int16_t) ntohs(*((u_int16_t *)(buf + i)));
dda42ca1
TL
75 /* Add carry. */
76 if (sum > 0xFFFF)
77 sum -= 0xFFFF;
886f6a98 78 }
35a50d44 79
886f6a98
TL
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) {
b081aeaf 83#ifdef DEBUG_CHECKSUM_VERBOSE
8ae2d595 84 log_debug ("sum = %x", sum);
b081aeaf 85#endif
ac16c3da 86 sum += buf [i] << 8;
dda42ca1
TL
87 /* Add carry. */
88 if (sum > 0xFFFF)
89 sum -= 0xFFFF;
886f6a98
TL
90 }
91
92 return sum;
93}
94
dda42ca1 95/* Finish computing the checksum, and then put it into network byte order. */
886f6a98 96
fa805637 97u_int32_t wrapsum (sum)
886f6a98
TL
98 u_int32_t sum;
99{
b081aeaf 100#ifdef DEBUG_CHECKSUM
8ae2d595 101 log_debug ("wrapsum (%x)", sum);
b081aeaf 102#endif
35a50d44 103
dda42ca1 104 sum = ~sum & 0xFFFF;
b081aeaf 105#ifdef DEBUG_CHECKSUM_VERBOSE
8ae2d595 106 log_debug ("sum = %x", sum);
b081aeaf 107#endif
886f6a98 108
b081aeaf 109#ifdef DEBUG_CHECKSUM
8ae2d595 110 log_debug ("wrapsum returns %x", htons (sum));
b081aeaf 111#endif
35a50d44 112 return htons(sum);
886f6a98 113}
886f6a98
TL
114
115#ifdef PACKET_ASSEMBLY
d29ce033
TL
116void assemble_hw_header (interface, buf, bufix, to)
117 struct interface_info *interface;
118 unsigned char *buf;
b1b7b521 119 unsigned *bufix;
d29ce033
TL
120 struct hardware *to;
121{
7d29d66d 122#if defined (HAVE_TR_SUPPORT)
5dba009a 123 if (interface -> hw_address.hbuf [0] == HTYPE_IEEE802)
7d29d66d 124 assemble_tr_header (interface, buf, bufix, to);
d29ce033 125 else
7e2f5636
TL
126#endif
127#if defined (DEC_FDDI)
eb968146 128 if (interface -> hw_address.hbuf [0] == HTYPE_FDDI)
7e2f5636
TL
129 assemble_fddi_header (interface, buf, bufix, to);
130 else
d99452d1 131#endif
7d29d66d 132 assemble_ethernet_header (interface, buf, bufix, to);
d29ce033 133
d29ce033
TL
134}
135
136/* UDP header and IP header assembled together for convenience. */
3f368088 137
b400da0a
TL
138void assemble_udp_ip_header (interface, buf, bufix,
139 from, to, port, data, len)
d29ce033
TL
140 struct interface_info *interface;
141 unsigned char *buf;
b1b7b521 142 unsigned *bufix;
b400da0a
TL
143 u_int32_t from;
144 u_int32_t to;
15c1fd2c 145 u_int32_t port;
d29ce033 146 unsigned char *data;
b1b7b521 147 unsigned len;
d29ce033
TL
148{
149 struct ip ip;
150 struct udphdr udp;
151
152 /* Fill out the IP header */
cfdfb9f1
TL
153 IP_V_SET (&ip, 4);
154 IP_HL_SET (&ip, 20);
d29ce033
TL
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;
b400da0a
TL
162 ip.ip_src.s_addr = from;
163 ip.ip_dst.s_addr = to;
d29ce033
TL
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 */
a60076f9 173 udp.uh_sport = local_port; /* XXX */
d29ce033
TL
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,
2d1b06e0 186 2 * sizeof ip.ip_src,
d29ce033
TL
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}
886f6a98 195#endif /* PACKET_ASSEMBLY */
3f368088 196
886f6a98 197#ifdef PACKET_DECODING
d29ce033
TL
198/* Decode a hardware header... */
199/* XXX currently only supports ethernet; doesn't check for other types. */
3f368088 200
0cbc9e23 201ssize_t decode_hw_header (interface, buf, bufix, from)
d29ce033
TL
202 struct interface_info *interface;
203 unsigned char *buf;
b1b7b521 204 unsigned bufix;
d29ce033
TL
205 struct hardware *from;
206{
7d29d66d 207#if defined (HAVE_TR_SUPPORT)
5dba009a 208 if (interface -> hw_address.hbuf [0] == HTYPE_IEEE802)
7d29d66d
TL
209 return decode_tr_header (interface, buf, bufix, from);
210 else
7e2f5636
TL
211#endif
212#if defined (DEC_FDDI)
eb968146
TL
213 if (interface -> hw_address.hbuf [0] == HTYPE_FDDI)
214 return decode_fddi_header (interface, buf, bufix, from);
7e2f5636 215 else
7bb11ad5 216#endif
7d29d66d 217 return decode_ethernet_header (interface, buf, bufix, from);
3f368088
TL
218}
219
d29ce033 220/* UDP header and IP header decoded together for convenience. */
3f368088 221
bd5ef11d 222ssize_t decode_udp_ip_header (interface, buf, bufix, from, data, buflen)
d29ce033
TL
223 struct interface_info *interface;
224 unsigned char *buf;
b1b7b521 225 unsigned bufix;
d29ce033
TL
226 struct sockaddr_in *from;
227 unsigned char *data;
b1b7b521 228 unsigned buflen;
3f368088 229{
d29ce033
TL
230 struct ip *ip;
231 struct udphdr *udp;
232 u_int32_t ip_len = (buf [bufix] & 0xf) << 2;
233 u_int32_t sum, usum;
bd5ef11d
TL
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;
b1b7b521 240 unsigned len;
da9a43af 241 unsigned ulen;
d758ad8c 242 int ignore = 0;
7bb11ad5
TL
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? */
a60076f9 253 if (udp -> uh_dport != local_port)
7bb11ad5 254 return -1;
7bb11ad5 255#endif /* USERLAND_FILTER */
d29ce033 256
da9a43af 257 ulen = ntohs (udp -> uh_ulen);
e28dccc1
TL
258 if (ulen < sizeof *udp ||
259 ((unsigned char *)udp) + ulen > buf + bufix + buflen) {
da9a43af 260 log_info ("bogus UDP packet length: %d", ulen);
b0fff90a
TL
261 return -1;
262 }
263
d29ce033 264 /* Check the IP header checksum - it should be zero. */
dda42ca1 265 ++ip_packets_seen;
d29ce033 266 if (wrapsum (checksum (buf + bufix, ip_len, 0))) {
bd5ef11d
TL
267 ++ip_packets_bad_checksum;
268 if (ip_packets_seen > 4 &&
dda42ca1 269 (ip_packets_seen / ip_packets_bad_checksum) < 2) {
bd5ef11d
TL
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 }
7bb11ad5 274 return -1;
d29ce033 275 }
3f368088 276
bd5ef11d 277 /* Check the IP packet length. */
d758ad8c
TL
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 }
bd5ef11d 285
d29ce033 286 /* Copy out the IP source address... */
d29ce033 287 memcpy (&from -> sin_addr, &ip -> ip_src, 4);
3f368088 288
d29ce033
TL
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. */
3f368088 292
d29ce033
TL
293 if (!data) {
294 data = buf + bufix + ip_len + sizeof *udp;
da9a43af 295 len = ulen - sizeof *udp;
bd5ef11d 296 ++udp_packets_length_checked;
dda42ca1 297 if (len + data > buf + bufix + buflen) {
bd5ef11d
TL
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);
dda42ca1
TL
305 udp_packets_length_overflow =
306 udp_packets_length_checked = 0;
bd5ef11d
TL
307 }
308 return -1;
309 }
d758ad8c
TL
310 if (len + data < buf + bufix + buflen &&
311 len + data != buf + bufix + buflen && !ignore)
bd5ef11d 312 log_debug ("accepting packet with data after udp payload.");
1bd620d8 313 if (len + data > buf + bufix + buflen) {
c4661845
TL
314 log_debug ("dropping packet with bogus uh_ulen %ld",
315 (long)(len + sizeof *udp));
b29db8aa
TL
316 return -1;
317 }
d29ce033 318 }
3f368088 319
d29ce033
TL
320 usum = udp -> uh_sum;
321 udp -> uh_sum = 0;
7bb11ad5 322
d29ce033
TL
323 sum = wrapsum (checksum ((unsigned char *)udp, sizeof *udp,
324 checksum (data, len,
325 checksum ((unsigned char *)
326 &ip -> ip_src,
2d1b06e0 327 2 * sizeof ip -> ip_src,
d29ce033 328 IPPROTO_UDP +
da9a43af 329 (u_int32_t)ulen))));
d29ce033 330
609115bc 331 udp_packets_seen++;
d29ce033 332 if (usum && usum != sum) {
dda42ca1 333 udp_packets_bad_checksum++;
bd5ef11d
TL
334 if (udp_packets_seen > 4 &&
335 (udp_packets_seen / udp_packets_bad_checksum) < 2) {
dda42ca1 336 log_info ("%d bad udp checksums in %d packets",
bd5ef11d
TL
337 udp_packets_bad_checksum, udp_packets_seen);
338 udp_packets_seen = udp_packets_bad_checksum = 0;
339 }
d29ce033
TL
340 return -1;
341 }
3f368088 342
d29ce033
TL
343 /* Copy out the port... */
344 memcpy (&from -> sin_port, &udp -> uh_sport, sizeof udp -> uh_sport);
345
346 return ip_len + sizeof *udp;
3f368088 347}
886f6a98 348#endif /* PACKET_DECODING */