]>
Commit | Line | Data |
---|---|---|
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 |
44 | static 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 | 58 | u_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 | 97 | u_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 |
116 | void 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 |
138 | void 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 | 201 | ssize_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 | 222 | ssize_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 */ |