1 /* $NetBSD: packet.c,v 1.1.1.4 2014/07/12 11:57:46 spz Exp $ */
4 Packet assembly code, originally contributed by Archie Cobbs. */
7 * Copyright (c) 2009,2012 by Internet Systems Consortium, Inc. ("ISC")
8 * Copyright (c) 2004,2005,2007 by Internet Systems Consortium, Inc. ("ISC")
9 * Copyright (c) 1996-2003 by Internet Software Consortium
11 * Permission to use, copy, modify, and distribute this software for any
12 * purpose with or without fee is hereby granted, provided that the above
13 * copyright notice and this permission notice appear in all copies.
15 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Internet Systems Consortium, Inc.
25 * Redwood City, CA 94063
27 * https://www.isc.org/
29 * This code was originally contributed by Archie Cobbs, and is still
30 * very similar to that contribution, although the packet checksum code
31 * has been hacked significantly with the help of quite a few ISC DHCP
32 * users, without whose gracious and thorough help the checksum code would
36 #include <sys/cdefs.h>
37 __RCSID("$NetBSD: packet.c,v 1.1.1.4 2014/07/12 11:57:46 spz Exp $");
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 switch (interface
->hw_address
.hbuf
[0]) {
114 #if defined(HAVE_TR_SUPPORT)
116 assemble_tr_header(interface
, buf
, bufix
, to
);
119 #if defined (DEC_FDDI)
121 assemble_fddi_header(interface
, buf
, bufix
, to
);
124 case HTYPE_INFINIBAND
:
125 log_error("Attempt to assemble hw header for infiniband");
129 assemble_ethernet_header(interface
, buf
, bufix
, to
);
134 /* UDP header and IP header assembled together for convenience. */
136 void assemble_udp_ip_header (interface
, buf
, bufix
,
137 from
, to
, port
, data
, len
)
138 struct interface_info
*interface
;
150 memset (&ip
, 0, sizeof ip
);
152 /* Fill out the IP header */
155 ip
.ip_tos
= IPTOS_LOWDELAY
;
156 ip
.ip_len
= htons(sizeof(ip
) + sizeof(udp
) + len
);
160 ip
.ip_p
= IPPROTO_UDP
;
162 ip
.ip_src
.s_addr
= from
;
163 ip
.ip_dst
.s_addr
= to
;
165 /* Checksum the IP header... */
166 ip
.ip_sum
= wrapsum (checksum ((unsigned char *)&ip
, sizeof ip
, 0));
168 /* Copy the ip header into the buffer... */
169 memcpy (&buf
[*bufix
], &ip
, sizeof ip
);
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
);
178 /* Compute UDP checksums, including the ``pseudo-header'', the UDP
179 header and the data. */
182 wrapsum (checksum ((unsigned char *)&udp
, sizeof udp
,
184 checksum ((unsigned char *)
186 2 * sizeof ip
.ip_src
,
189 ntohs (udp
.uh_ulen
)))));
191 /* Copy the udp header into the buffer... */
192 memcpy (&buf
[*bufix
], &udp
, sizeof udp
);
193 *bufix
+= sizeof udp
;
195 #endif /* PACKET_ASSEMBLY */
197 #ifdef PACKET_DECODING
198 /* Decode a hardware header... */
199 /* Support for ethernet, TR and FDDI
200 * Doesn't support infiniband yet as the supported oses shouldn't get here
203 ssize_t
decode_hw_header (interface
, buf
, bufix
, from
)
204 struct interface_info
*interface
;
207 struct hardware
*from
;
209 switch(interface
->hw_address
.hbuf
[0]) {
210 #if defined (HAVE_TR_SUPPORT)
212 return (decode_tr_header(interface
, buf
, bufix
, from
));
214 #if defined (DEC_FDDI)
216 return (decode_fddi_header(interface
, buf
, bufix
, from
));
218 case HTYPE_INFINIBAND
:
219 log_error("Attempt to decode hw header for infiniband");
223 return (decode_ethernet_header(interface
, buf
, bufix
, from
));
227 /* UDP header and IP header decoded together for convenience. */
230 decode_udp_ip_header(struct interface_info
*interface
,
231 unsigned char *buf
, unsigned bufix
,
232 struct sockaddr_in
*from
, unsigned buflen
,
238 unsigned char *upp
, *endbuf
;
239 u_int32_t ip_len
, ulen
, pkt_len
;
241 static int ip_packets_seen
;
242 static int ip_packets_bad_checksum
;
243 static int udp_packets_seen
;
244 static int udp_packets_bad_checksum
;
245 static int udp_packets_length_checked
;
246 static int udp_packets_length_overflow
;
249 /* Designate the end of the input buffer for bounds checks. */
250 endbuf
= buf
+ bufix
+ buflen
;
252 /* Assure there is at least an IP header there. */
253 if ((buf
+ bufix
+ sizeof(ip
)) > endbuf
)
256 /* Copy the IP header into a stack aligned structure for inspection.
257 * There may be bits in the IP header that we're not decoding, so we
258 * copy out the bits we grok and skip ahead by ip.ip_hl * 4.
261 memcpy(&ip
, upp
, sizeof(ip
));
262 ip_len
= (*upp
& 0x0f) << 2;
265 /* Check the IP packet length. */
266 pkt_len
= ntohs(ip
.ip_len
);
267 if (pkt_len
> buflen
)
270 /* Assure after ip_len bytes that there is enough room for a UDP header. */
271 if ((upp
+ sizeof(udp
)) > endbuf
)
274 /* Copy the UDP header into a stack aligned structure for inspection. */
275 memcpy(&udp
, upp
, sizeof(udp
));
277 #ifdef USERLAND_FILTER
278 /* Is it a UDP packet? */
279 if (ip
.ip_p
!= IPPROTO_UDP
)
282 /* Is it to the port we're serving? */
283 if (udp
.uh_dport
!= local_port
)
285 #endif /* USERLAND_FILTER */
287 ulen
= ntohs(udp
.uh_ulen
);
288 if (ulen
< sizeof(udp
))
291 udp_packets_length_checked
++;
292 if ((upp
+ ulen
) > endbuf
) {
293 udp_packets_length_overflow
++;
294 if ((udp_packets_length_checked
> 4) &&
295 ((udp_packets_length_checked
/
296 udp_packets_length_overflow
) < 2)) {
297 log_info("%d udp packets in %d too long - dropped",
298 udp_packets_length_overflow
,
299 udp_packets_length_checked
);
300 udp_packets_length_overflow
= 0;
301 udp_packets_length_checked
= 0;
306 if ((ulen
< sizeof(udp
)) || ((upp
+ ulen
) > endbuf
))
309 /* Check the IP header checksum - it should be zero. */
311 if (wrapsum (checksum (buf
+ bufix
, ip_len
, 0))) {
312 ++ip_packets_bad_checksum
;
313 if (ip_packets_seen
> 4 &&
314 (ip_packets_seen
/ ip_packets_bad_checksum
) < 2) {
315 log_info ("%d bad IP checksums seen in %d packets",
316 ip_packets_bad_checksum
, ip_packets_seen
);
317 ip_packets_seen
= ip_packets_bad_checksum
= 0;
322 /* Copy out the IP source address... */
323 memcpy(&from
->sin_addr
, &ip
.ip_src
, 4);
325 /* Compute UDP checksums, including the ``pseudo-header'', the UDP
326 header and the data. If the UDP checksum field is zero, we're
327 not supposed to do a checksum. */
329 data
= upp
+ sizeof(udp
);
330 len
= ulen
- sizeof(udp
);
335 /* XXX: We have to pass &udp, because we have to zero the checksum
336 * field before calculating the sum...'upp' isn't zeroed.
338 sum
= wrapsum(checksum((unsigned char *)&udp
, sizeof(udp
),
340 checksum((unsigned char *)&ip
.ip_src
,
341 8, IPPROTO_UDP
+ ulen
))));
344 if (usum
&& usum
!= sum
) {
345 udp_packets_bad_checksum
++;
346 if (udp_packets_seen
> 4 &&
347 (udp_packets_seen
/ udp_packets_bad_checksum
) < 2) {
348 log_info ("%d bad udp checksums in %d packets",
349 udp_packets_bad_checksum
, udp_packets_seen
);
350 udp_packets_seen
= udp_packets_bad_checksum
= 0;
355 /* Copy out the port... */
356 memcpy (&from
-> sin_port
, &udp
.uh_sport
, sizeof udp
.uh_sport
);
358 /* Save the length of the UDP payload. */
362 /* Return the index to the UDP payload. */
363 return ip_len
+ sizeof udp
;
365 #endif /* PACKET_DECODING */