3 Packet assembly code, originally contributed by Archie Cobbs. */
6 * Copyright (c) 2004-2005 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1996-2003 by Internet Software Consortium
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.
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.
21 * Internet Systems Consortium, Inc.
23 * Redwood City, CA 94063
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
35 static char copyright
[] =
36 "$Id: packet.c,v 1.7 2005/08/11 17:13:21 drochner Exp $ Copyright (c) 2004-2005 Internet Systems Consortium. All rights reserved.\n";
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 */
47 /* Compute the easy part of the checksum on a range of bytes. */
49 u_int32_t
checksum (buf
, nbytes
, sum
)
57 log_debug ("checksum (%x %d %x)", buf
, nbytes
, sum
);
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
);
65 sum
+= (u_int16_t
) ntohs(*((u_int16_t
*)(buf
+ i
)));
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. */
74 #ifdef DEBUG_CHECKSUM_VERBOSE
75 log_debug ("sum = %x", sum
);
86 /* Finish computing the checksum, and then put it into network byte order. */
88 u_int32_t
wrapsum (sum
)
92 log_debug ("wrapsum (%x)", sum
);
96 #ifdef DEBUG_CHECKSUM_VERBOSE
97 log_debug ("sum = %x", sum
);
100 #ifdef DEBUG_CHECKSUM
101 log_debug ("wrapsum returns %x", htons (sum
));
106 #ifdef PACKET_ASSEMBLY
107 void assemble_hw_header (interface
, buf
, bufix
, to
)
108 struct interface_info
*interface
;
113 #if defined (HAVE_TR_SUPPORT)
114 if (interface
-> hw_address
.hbuf
[0] == HTYPE_IEEE802
)
115 assemble_tr_header (interface
, buf
, bufix
, to
);
118 #if defined (DEC_FDDI) || defined (NETBSD_FDDI)
119 if (interface
-> hw_address
.hbuf
[0] == HTYPE_FDDI
)
120 assemble_fddi_header (interface
, buf
, bufix
, to
);
123 assemble_ethernet_header (interface
, buf
, bufix
, to
);
127 /* UDP header and IP header assembled together for convenience. */
129 void assemble_udp_ip_header (interface
, buf
, bufix
,
130 from
, to
, port
, data
, len
)
131 struct interface_info
*interface
;
143 /* Fill out the IP header */
146 ip
.ip_tos
= IPTOS_LOWDELAY
;
147 ip
.ip_len
= htons(sizeof(ip
) + sizeof(udp
) + len
);
151 ip
.ip_p
= IPPROTO_UDP
;
153 ip
.ip_src
.s_addr
= from
;
154 ip
.ip_dst
.s_addr
= to
;
156 /* Checksum the IP header... */
157 ip
.ip_sum
= wrapsum (checksum ((unsigned char *)&ip
, sizeof ip
, 0));
159 /* Copy the ip header into the buffer... */
160 memcpy (&buf
[*bufix
], &ip
, sizeof ip
);
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
);
169 /* Compute UDP checksums, including the ``pseudo-header'', the UDP
170 header and the data. */
173 wrapsum (checksum ((unsigned char *)&udp
, sizeof udp
,
175 checksum ((unsigned char *)
177 2 * sizeof ip
.ip_src
,
180 ntohs (udp
.uh_ulen
)))));
182 /* Copy the udp header into the buffer... */
183 memcpy (&buf
[*bufix
], &udp
, sizeof udp
);
184 *bufix
+= sizeof udp
;
186 #endif /* PACKET_ASSEMBLY */
188 #ifdef PACKET_DECODING
189 /* Decode a hardware header... */
190 /* XXX currently only supports ethernet; doesn't check for other types. */
192 ssize_t
decode_hw_header (interface
, buf
, bufix
, from
)
193 struct interface_info
*interface
;
196 struct hardware
*from
;
198 #if defined (HAVE_TR_SUPPORT)
199 if (interface
-> hw_address
.hbuf
[0] == HTYPE_IEEE802
)
200 return decode_tr_header (interface
, buf
, bufix
, from
);
203 #if defined (DEC_FDDI) || defined (NETBSD_FDDI)
204 if (interface
-> hw_address
.hbuf
[0] == HTYPE_FDDI
)
205 return decode_fddi_header (interface
, buf
, bufix
, from
);
208 return decode_ethernet_header (interface
, buf
, bufix
, from
);
211 /* UDP header and IP header decoded together for convenience. */
213 ssize_t
decode_udp_ip_header (interface
, buf
, bufix
, from
, buflen
, rbuflen
)
214 struct interface_info
*interface
;
217 struct sockaddr_in
*from
;
224 u_int32_t ip_len
= (buf
[bufix
] & 0xf) << 2;
226 static int ip_packets_seen
;
227 static int ip_packets_bad_checksum
;
228 static int udp_packets_seen
;
229 static int udp_packets_bad_checksum
;
230 static int udp_packets_length_checked
;
231 static int udp_packets_length_overflow
;
236 memcpy(&ip
, buf
+ bufix
, sizeof (struct ip
));
237 udp
= (struct udphdr
*)(buf
+ bufix
+ ip_len
);
238 len
= 0; /* XXXGCC -Wuninitialized */
240 #ifdef USERLAND_FILTER
241 /* Is it a UDP packet? */
242 if (ip
.ip_p
!= IPPROTO_UDP
)
245 /* Is it to the port we're serving? */
246 if (udp
-> uh_dport
!= local_port
)
248 #endif /* USERLAND_FILTER */
250 ulen
= ntohs (udp
-> uh_ulen
);
251 if (ulen
< sizeof *udp
||
252 ((unsigned char *)udp
) + ulen
> buf
+ bufix
+ buflen
) {
253 log_info ("bogus UDP packet length: %d", ulen
);
257 /* Check the IP header checksum - it should be zero. */
259 if (wrapsum (checksum (buf
+ bufix
, ip_len
, 0))) {
260 ++ip_packets_bad_checksum
;
261 if (ip_packets_seen
> 4 &&
262 (ip_packets_seen
/ ip_packets_bad_checksum
) < 2) {
263 log_info ("%d bad IP checksums seen in %d packets",
264 ip_packets_bad_checksum
, ip_packets_seen
);
265 ip_packets_seen
= ip_packets_bad_checksum
= 0;
270 /* Check the IP packet length. */
271 if (ntohs (ip
.ip_len
) != buflen
) {
272 if ((ntohs (ip
.ip_len
+ 2) & ~1) == buflen
)
275 log_debug ("ip length %d disagrees with bytes received %d.",
276 ntohs (ip
.ip_len
), buflen
);
279 /* Copy out the IP source address... */
280 memcpy (&from
-> sin_addr
, &ip
.ip_src
, 4);
282 /* Compute UDP checksums, including the ``pseudo-header'', the UDP
283 header and the data. If the UDP checksum field is zero, we're
284 not supposed to do a checksum. */
286 data
= buf
+ bufix
+ ip_len
+ sizeof *udp
;
287 len
= ulen
- sizeof *udp
;
288 ++udp_packets_length_checked
;
289 if (len
+ data
> buf
+ bufix
+ buflen
) {
290 ++udp_packets_length_overflow
;
291 if (udp_packets_length_checked
> 4 &&
292 (udp_packets_length_checked
/
293 udp_packets_length_overflow
) < 2) {
294 log_info ("%d udp packets in %d too long - dropped",
295 udp_packets_length_overflow
,
296 udp_packets_length_checked
);
297 udp_packets_length_overflow
=
298 udp_packets_length_checked
= 0;
302 if (len
+ data
< buf
+ bufix
+ buflen
&&
303 len
+ data
!= buf
+ bufix
+ buflen
&& !ignore
)
304 log_debug ("accepting packet with data after udp payload.");
305 if (len
+ data
> buf
+ bufix
+ buflen
) {
306 log_debug ("dropping packet with bogus uh_ulen %ld",
307 (long)(len
+ sizeof *udp
));
311 usum
= udp
-> uh_sum
;
314 sum
= wrapsum (checksum ((unsigned char *)udp
, sizeof *udp
,
316 checksum ((unsigned char *)
318 2 * sizeof ip
.ip_src
,
323 if (usum
&& usum
!= sum
) {
324 udp_packets_bad_checksum
++;
325 if (udp_packets_seen
> 4 &&
326 (udp_packets_seen
/ udp_packets_bad_checksum
) < 2) {
327 log_info ("%d bad udp checksums in %d packets",
328 udp_packets_bad_checksum
, udp_packets_seen
);
329 udp_packets_seen
= udp_packets_bad_checksum
= 0;
334 /* Copy out the port... */
335 memcpy (&from
-> sin_port
, &udp
-> uh_sport
, sizeof udp
-> uh_sport
);
337 *rbuflen
= ntohs (ip
.ip_len
) - ip_len
- sizeof *udp
;
338 return ip_len
+ sizeof *udp
;
340 #endif /* PACKET_DECODING */