3 Packet assembly code, originally contributed by Archie Cobbs. */
6 * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1996-2003 by Internet Software Consortium
9 * This Source Code Form is subject to the terms of the Mozilla Public
10 * License, v. 2.0. If a copy of the MPL was not distributed with this
11 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
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 * Newmarket, NH 03857 USA
25 * https://www.isc.org/
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
36 #if defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING)
37 #include "includes/netinet/ip.h"
38 #include "includes/netinet/udp.h"
39 #include "includes/netinet/if_ether.h"
40 #endif /* PACKET_ASSEMBLY || PACKET_DECODING */
42 /* Compute the easy part of the checksum on a range of bytes. */
44 u_int32_t
checksum (buf
, nbytes
, sum
)
52 log_debug ("checksum (%x %d %x)", (unsigned)buf
, nbytes
, sum
);
55 /* Checksum all the pairs of bytes first... */
56 for (i
= 0; i
< (nbytes
& ~1U); i
+= 2) {
57 #ifdef DEBUG_CHECKSUM_VERBOSE
58 log_debug ("sum = %x", sum
);
60 sum
+= (u_int16_t
) ntohs(*((u_int16_t
*)(buf
+ i
)));
66 /* If there's a single byte left over, checksum it, too. Network
67 byte order is big-endian, so the remaining byte is the high byte. */
69 #ifdef DEBUG_CHECKSUM_VERBOSE
70 log_debug ("sum = %x", sum
);
81 /* Finish computing the checksum, and then put it into network byte order. */
83 u_int32_t
wrapsum (sum
)
87 log_debug ("wrapsum (%x)", sum
);
91 #ifdef DEBUG_CHECKSUM_VERBOSE
92 log_debug ("sum = %x", sum
);
96 log_debug ("wrapsum returns %x", htons (sum
));
101 #ifdef PACKET_ASSEMBLY
102 void assemble_hw_header (interface
, buf
, bufix
, to
)
103 struct interface_info
*interface
;
108 switch (interface
->hw_address
.hbuf
[0]) {
109 #if defined(HAVE_TR_SUPPORT)
111 assemble_tr_header(interface
, buf
, bufix
, to
);
114 #if defined (DEC_FDDI)
116 assemble_fddi_header(interface
, buf
, bufix
, to
);
119 case HTYPE_INFINIBAND
:
120 log_error("Attempt to assemble hw header for infiniband");
124 assemble_ethernet_header(interface
, buf
, bufix
, to
);
129 /* UDP header and IP header assembled together for convenience. */
131 void assemble_udp_ip_header (interface
, buf
, bufix
,
132 from
, to
, port
, data
, len
)
133 struct interface_info
*interface
;
145 memset (&ip
, 0, sizeof ip
);
147 /* Fill out the IP header */
150 ip
.ip_tos
= IPTOS_LOWDELAY
;
151 ip
.ip_len
= htons(sizeof(ip
) + sizeof(udp
) + len
);
155 ip
.ip_p
= IPPROTO_UDP
;
157 ip
.ip_src
.s_addr
= from
;
158 ip
.ip_dst
.s_addr
= to
;
160 /* Checksum the IP header... */
161 ip
.ip_sum
= wrapsum (checksum ((unsigned char *)&ip
, sizeof ip
, 0));
163 /* Copy the ip header into the buffer... */
164 memcpy (&buf
[*bufix
], &ip
, sizeof ip
);
167 /* Fill out the UDP header */
168 udp
.uh_sport
= local_port
; /* XXX */
169 udp
.uh_dport
= port
; /* XXX */
170 #if defined(RELAY_PORT)
171 /* Change to relay port defined if sending to server */
172 if (relay_port
&& (port
== htons(67))) {
173 udp
.uh_sport
= relay_port
;
176 udp
.uh_ulen
= htons(sizeof(udp
) + len
);
177 memset (&udp
.uh_sum
, 0, sizeof udp
.uh_sum
);
179 /* Compute UDP checksums, including the ``pseudo-header'', the UDP
180 header and the data. */
183 wrapsum (checksum ((unsigned char *)&udp
, sizeof udp
,
185 checksum ((unsigned char *)
187 2 * sizeof ip
.ip_src
,
190 ntohs (udp
.uh_ulen
)))));
192 /* Copy the udp header into the buffer... */
193 memcpy (&buf
[*bufix
], &udp
, sizeof udp
);
194 *bufix
+= sizeof udp
;
196 #endif /* PACKET_ASSEMBLY */
198 #ifdef PACKET_DECODING
199 /* Decode a hardware header... */
200 /* Support for ethernet, TR and FDDI
201 * Doesn't support infiniband yet as the supported oses shouldn't get here
204 ssize_t
decode_hw_header (interface
, buf
, bufix
, from
)
205 struct interface_info
*interface
;
208 struct hardware
*from
;
210 switch(interface
->hw_address
.hbuf
[0]) {
211 #if defined (HAVE_TR_SUPPORT)
213 return (decode_tr_header(interface
, buf
, bufix
, from
));
215 #if defined (DEC_FDDI)
217 return (decode_fddi_header(interface
, buf
, bufix
, from
));
219 case HTYPE_INFINIBAND
:
220 log_error("Attempt to decode hw header for infiniband");
224 return (decode_ethernet_header(interface
, buf
, bufix
, from
));
230 * \brief UDP header and IP header decoded together for convenience.
232 * Attempt to decode the UDP and IP headers and, if necessary, checksum
235 * \param inteface - the interface on which the packet was recevied
236 * \param buf - a pointer to the buffer for the received packet
237 * \param bufix - where to start processing the buffer, previous
238 * routines may have processed parts of the buffer already
239 * \param from - space to return the address of the packet sender
240 * \param buflen - remaining length of the buffer, this will have been
241 * decremented by bufix by the caller
242 * \param rbuflen - space to return the length of the payload from the udp
244 * \param csum_ready - indication if the checksum is valid for use
245 * non-zero indicates the checksum should be validated
247 * \return - the index to the first byte of the udp payload (that is the
248 * start of the DHCP packet
252 decode_udp_ip_header(struct interface_info
*interface
,
253 unsigned char *buf
, unsigned bufix
,
254 struct sockaddr_in
*from
, unsigned buflen
,
255 unsigned *rbuflen
, int csum_ready
)
261 u_int32_t ip_len
, ulen
, pkt_len
;
262 static unsigned int ip_packets_seen
= 0;
263 static unsigned int ip_packets_bad_checksum
= 0;
264 static unsigned int udp_packets_seen
= 0;
265 static unsigned int udp_packets_bad_checksum
= 0;
266 static unsigned int udp_packets_length_checked
= 0;
267 static unsigned int udp_packets_length_overflow
= 0;
270 /* Assure there is at least an IP header there. */
271 if (sizeof(ip
) > buflen
)
274 /* Copy the IP header into a stack aligned structure for inspection.
275 * There may be bits in the IP header that we're not decoding, so we
276 * copy out the bits we grok and skip ahead by ip.ip_hl * 4.
279 memcpy(&ip
, upp
, sizeof(ip
));
280 ip_len
= (*upp
& 0x0f) << 2;
283 /* Check packet lengths are within the buffer:
284 * first the ip header (ip_len)
285 * then the packet length from the ip header (pkt_len)
286 * then the udp header (ip_len + sizeof(udp)
287 * We are liberal in what we accept, the udp payload should fit within
288 * pkt_len, but we only check against the full buffer size.
290 pkt_len
= ntohs(ip
.ip_len
);
291 if ((ip_len
> buflen
) ||
292 (pkt_len
> buflen
) ||
293 ((ip_len
+ sizeof(udp
)) > buflen
))
296 /* Copy the UDP header into a stack aligned structure for inspection. */
297 memcpy(&udp
, upp
, sizeof(udp
));
299 #ifdef USERLAND_FILTER
300 /* Is it a UDP packet? */
301 if (ip
.ip_p
!= IPPROTO_UDP
)
304 /* Is it to the port we're serving? */
305 #if defined(RELAY_PORT)
306 if ((udp
.uh_dport
!= local_port
) &&
307 ((relay_port
== 0) || (udp
.uh_dport
!= relay_port
)))
309 if (udp
.uh_dport
!= local_port
)
312 #endif /* USERLAND_FILTER */
314 ulen
= ntohs(udp
.uh_ulen
);
315 if (ulen
< sizeof(udp
))
318 udp_packets_length_checked
++;
319 /* verify that the payload length from the udp packet fits in the buffer */
320 if ((ip_len
+ ulen
) > buflen
) {
321 udp_packets_length_overflow
++;
322 if (((udp_packets_length_checked
> 4) &&
323 (udp_packets_length_overflow
!= 0)) &&
324 ((udp_packets_length_checked
/ udp_packets_length_overflow
) < 2)) {
325 log_info("%u udp packets in %u too long - dropped",
326 udp_packets_length_overflow
,
327 udp_packets_length_checked
);
328 udp_packets_length_overflow
= 0;
329 udp_packets_length_checked
= 0;
334 /* If at least 5 with less than 50% bad, start over */
335 if (udp_packets_length_checked
> 4) {
336 udp_packets_length_overflow
= 0;
337 udp_packets_length_checked
= 0;
340 /* Check the IP header checksum - it should be zero. */
342 if (wrapsum (checksum (buf
+ bufix
, ip_len
, 0))) {
343 ++ip_packets_bad_checksum
;
344 if (((ip_packets_seen
> 4) && (ip_packets_bad_checksum
!= 0)) &&
345 ((ip_packets_seen
/ ip_packets_bad_checksum
) < 2)) {
346 log_info ("%u bad IP checksums seen in %u packets",
347 ip_packets_bad_checksum
, ip_packets_seen
);
348 ip_packets_seen
= ip_packets_bad_checksum
= 0;
353 /* If at least 5 with less than 50% bad, start over */
354 if (ip_packets_seen
> 4) {
355 ip_packets_bad_checksum
= 0;
359 /* Copy out the IP source address... */
360 memcpy(&from
->sin_addr
, &ip
.ip_src
, 4);
362 data
= upp
+ sizeof(udp
);
363 len
= ulen
- sizeof(udp
);
365 /* UDP check sum may be optional (udp.uh_sum == 0) or not ready if checksum
366 * offloading is in use */
368 if (udp
.uh_sum
&& csum_ready
) {
369 /* Check the UDP header checksum - since the received packet header
370 * contains the UDP checksum calculated by the transmitter, calculating
371 * it now should come out to zero. */
372 if (wrapsum(checksum((unsigned char *)&udp
, sizeof(udp
),
374 checksum((unsigned char *)&ip
.ip_src
,
375 8, IPPROTO_UDP
+ ulen
))))) {
376 udp_packets_bad_checksum
++;
377 if (((udp_packets_seen
> 4) && (udp_packets_bad_checksum
!= 0))
378 && ((udp_packets_seen
/ udp_packets_bad_checksum
) < 2)) {
379 log_debug ("%u bad udp checksums in %u packets",
380 udp_packets_bad_checksum
, udp_packets_seen
);
381 udp_packets_seen
= udp_packets_bad_checksum
= 0;
388 /* If at least 5 with less than 50% bad, start over */
389 if (udp_packets_seen
> 4) {
390 udp_packets_bad_checksum
= 0;
391 udp_packets_seen
= 0;
394 /* Copy out the port... */
395 memcpy (&from
-> sin_port
, &udp
.uh_sport
, sizeof udp
.uh_sport
);
397 /* Save the length of the UDP payload. */
401 /* Return the index to the UDP payload. */
402 return ip_len
+ sizeof udp
;
404 #endif /* PACKET_DECODING */