Home | History | Annotate | Line # | Download | only in common
packet.c revision 1.2.6.1
      1  1.2.6.1    martin /*	$NetBSD: packet.c,v 1.2.6.1 2024/02/29 11:39:17 martin Exp $	*/
      2      1.1  christos 
      3      1.1  christos /* packet.c
      4      1.1  christos 
      5      1.1  christos    Packet assembly code, originally contributed by Archie Cobbs. */
      6      1.1  christos 
      7      1.1  christos /*
      8  1.2.6.1    martin  * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC")
      9      1.1  christos  * Copyright (c) 1996-2003 by Internet Software Consortium
     10      1.1  christos  *
     11      1.1  christos  * This Source Code Form is subject to the terms of the Mozilla Public
     12      1.1  christos  * License, v. 2.0. If a copy of the MPL was not distributed with this
     13      1.1  christos  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     14      1.1  christos  *
     15      1.1  christos  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
     16      1.1  christos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     17      1.1  christos  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
     18      1.1  christos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     19      1.1  christos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     20      1.1  christos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
     21      1.1  christos  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     22      1.1  christos  *
     23      1.1  christos  *   Internet Systems Consortium, Inc.
     24  1.2.6.1    martin  *   PO Box 360
     25  1.2.6.1    martin  *   Newmarket, NH 03857 USA
     26      1.1  christos  *   <info (at) isc.org>
     27      1.1  christos  *   https://www.isc.org/
     28      1.1  christos  *
     29      1.1  christos  * This code was originally contributed by Archie Cobbs, and is still
     30      1.1  christos  * very similar to that contribution, although the packet checksum code
     31      1.1  christos  * has been hacked significantly with the help of quite a few ISC DHCP
     32      1.1  christos  * users, without whose gracious and thorough help the checksum code would
     33      1.1  christos  * still be disabled.
     34      1.1  christos  */
     35      1.1  christos 
     36      1.1  christos #include <sys/cdefs.h>
     37  1.2.6.1    martin __RCSID("$NetBSD: packet.c,v 1.2.6.1 2024/02/29 11:39:17 martin Exp $");
     38      1.1  christos 
     39      1.1  christos #include "dhcpd.h"
     40      1.1  christos 
     41      1.1  christos #if defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING)
     42      1.1  christos #include "includes/netinet/ip.h"
     43      1.1  christos #include "includes/netinet/udp.h"
     44      1.1  christos #include "includes/netinet/if_ether.h"
     45      1.1  christos #endif /* PACKET_ASSEMBLY || PACKET_DECODING */
     46      1.1  christos 
     47      1.1  christos /* Compute the easy part of the checksum on a range of bytes. */
     48      1.1  christos 
     49      1.1  christos u_int32_t checksum (buf, nbytes, sum)
     50      1.1  christos 	unsigned char *buf;
     51      1.1  christos 	unsigned nbytes;
     52      1.1  christos 	u_int32_t sum;
     53      1.1  christos {
     54      1.1  christos 	unsigned i;
     55      1.1  christos 
     56      1.1  christos #ifdef DEBUG_CHECKSUM
     57      1.1  christos 	log_debug ("checksum (%x %d %x)", (unsigned)buf, nbytes, sum);
     58      1.1  christos #endif
     59      1.1  christos 
     60      1.1  christos 	/* Checksum all the pairs of bytes first... */
     61      1.1  christos 	for (i = 0; i < (nbytes & ~1U); i += 2) {
     62      1.1  christos #ifdef DEBUG_CHECKSUM_VERBOSE
     63      1.1  christos 		log_debug ("sum = %x", sum);
     64      1.1  christos #endif
     65      1.1  christos 		sum += (u_int16_t) ntohs(*((u_int16_t *)(buf + i)));
     66      1.1  christos 		/* Add carry. */
     67      1.1  christos 		if (sum > 0xFFFF)
     68      1.1  christos 			sum -= 0xFFFF;
     69  1.2.6.1    martin 	}
     70      1.1  christos 
     71      1.1  christos 	/* If there's a single byte left over, checksum it, too.   Network
     72      1.1  christos 	   byte order is big-endian, so the remaining byte is the high byte. */
     73      1.1  christos 	if (i < nbytes) {
     74      1.1  christos #ifdef DEBUG_CHECKSUM_VERBOSE
     75      1.1  christos 		log_debug ("sum = %x", sum);
     76      1.1  christos #endif
     77      1.1  christos 		sum += buf [i] << 8;
     78      1.1  christos 		/* Add carry. */
     79      1.1  christos 		if (sum > 0xFFFF)
     80      1.1  christos 			sum -= 0xFFFF;
     81      1.1  christos 	}
     82  1.2.6.1    martin 
     83      1.1  christos 	return sum;
     84      1.1  christos }
     85      1.1  christos 
     86      1.1  christos /* Finish computing the checksum, and then put it into network byte order. */
     87      1.1  christos 
     88      1.1  christos u_int32_t wrapsum (sum)
     89      1.1  christos 	u_int32_t sum;
     90      1.1  christos {
     91      1.1  christos #ifdef DEBUG_CHECKSUM
     92      1.1  christos 	log_debug ("wrapsum (%x)", sum);
     93      1.1  christos #endif
     94      1.1  christos 
     95      1.1  christos 	sum = ~sum & 0xFFFF;
     96      1.1  christos #ifdef DEBUG_CHECKSUM_VERBOSE
     97      1.1  christos 	log_debug ("sum = %x", sum);
     98      1.1  christos #endif
     99  1.2.6.1    martin 
    100      1.1  christos #ifdef DEBUG_CHECKSUM
    101      1.1  christos 	log_debug ("wrapsum returns %x", htons (sum));
    102      1.1  christos #endif
    103      1.1  christos 	return htons(sum);
    104      1.1  christos }
    105      1.1  christos 
    106      1.1  christos #ifdef PACKET_ASSEMBLY
    107      1.1  christos void assemble_hw_header (interface, buf, bufix, to)
    108      1.1  christos 	struct interface_info *interface;
    109      1.1  christos 	unsigned char *buf;
    110      1.1  christos 	unsigned *bufix;
    111      1.1  christos 	struct hardware *to;
    112      1.1  christos {
    113      1.1  christos 	switch (interface->hw_address.hbuf[0]) {
    114      1.1  christos #if defined(HAVE_TR_SUPPORT)
    115      1.1  christos 	case HTYPE_IEEE802:
    116      1.1  christos 		assemble_tr_header(interface, buf, bufix, to);
    117      1.1  christos 		break;
    118      1.1  christos #endif
    119      1.1  christos #if defined (DEC_FDDI)
    120      1.1  christos 	case HTYPE_FDDI:
    121      1.1  christos 		assemble_fddi_header(interface, buf, bufix, to);
    122      1.1  christos 		break;
    123      1.1  christos #endif
    124      1.1  christos 	case HTYPE_INFINIBAND:
    125      1.1  christos 		log_error("Attempt to assemble hw header for infiniband");
    126      1.1  christos 		break;
    127      1.1  christos 	case HTYPE_ETHER:
    128      1.1  christos 	default:
    129      1.1  christos 		assemble_ethernet_header(interface, buf, bufix, to);
    130      1.1  christos 		break;
    131      1.1  christos 	}
    132      1.1  christos }
    133      1.1  christos 
    134      1.1  christos /* UDP header and IP header assembled together for convenience. */
    135      1.1  christos 
    136      1.1  christos void assemble_udp_ip_header (interface, buf, bufix,
    137      1.1  christos 			     from, to, port, data, len)
    138      1.1  christos 	struct interface_info *interface;
    139      1.1  christos 	unsigned char *buf;
    140      1.1  christos 	unsigned *bufix;
    141      1.1  christos 	u_int32_t from;
    142      1.1  christos 	u_int32_t to;
    143      1.1  christos 	u_int32_t port;
    144      1.1  christos 	unsigned char *data;
    145      1.1  christos 	unsigned len;
    146      1.1  christos {
    147      1.1  christos 	struct ip ip;
    148      1.1  christos 	struct udphdr udp;
    149      1.1  christos 
    150      1.1  christos 	memset (&ip, 0, sizeof ip);
    151      1.1  christos 
    152      1.1  christos 	/* Fill out the IP header */
    153      1.1  christos 	IP_V_SET (&ip, 4);
    154      1.1  christos 	IP_HL_SET (&ip, 20);
    155      1.1  christos 	ip.ip_tos = IPTOS_LOWDELAY;
    156      1.1  christos 	ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len);
    157      1.1  christos 	ip.ip_id = 0;
    158      1.1  christos 	ip.ip_off = 0;
    159      1.1  christos 	ip.ip_ttl = 128;
    160      1.1  christos 	ip.ip_p = IPPROTO_UDP;
    161      1.1  christos 	ip.ip_sum = 0;
    162      1.1  christos 	ip.ip_src.s_addr = from;
    163      1.1  christos 	ip.ip_dst.s_addr = to;
    164  1.2.6.1    martin 
    165      1.1  christos 	/* Checksum the IP header... */
    166      1.1  christos 	ip.ip_sum = wrapsum (checksum ((unsigned char *)&ip, sizeof ip, 0));
    167  1.2.6.1    martin 
    168      1.1  christos 	/* Copy the ip header into the buffer... */
    169      1.1  christos 	memcpy (&buf [*bufix], &ip, sizeof ip);
    170      1.1  christos 	*bufix += sizeof ip;
    171      1.1  christos 
    172      1.1  christos 	/* Fill out the UDP header */
    173      1.2  christos 	udp.uh_sport = *libdhcp_callbacks.local_port;		/* XXX */
    174      1.1  christos 	udp.uh_dport = port;			/* XXX */
    175      1.1  christos #if defined(RELAY_PORT)
    176      1.1  christos 	/* Change to relay port defined if sending to server */
    177      1.1  christos 	if (relay_port && (port == htons(67))) {
    178      1.1  christos 		udp.uh_sport = relay_port;
    179      1.1  christos 	}
    180      1.1  christos #endif
    181      1.1  christos 	udp.uh_ulen = htons(sizeof(udp) + len);
    182      1.1  christos 	memset (&udp.uh_sum, 0, sizeof udp.uh_sum);
    183      1.1  christos 
    184      1.1  christos 	/* Compute UDP checksums, including the ``pseudo-header'', the UDP
    185      1.1  christos 	   header and the data. */
    186      1.1  christos 
    187      1.1  christos 	udp.uh_sum =
    188      1.1  christos 		wrapsum (checksum ((unsigned char *)&udp, sizeof udp,
    189  1.2.6.1    martin 				   checksum (data, len,
    190      1.1  christos 					     checksum ((unsigned char *)
    191      1.1  christos 						       &ip.ip_src,
    192      1.1  christos 						       2 * sizeof ip.ip_src,
    193      1.1  christos 						       IPPROTO_UDP +
    194      1.1  christos 						       (u_int32_t)
    195      1.1  christos 						       ntohs (udp.uh_ulen)))));
    196      1.1  christos 
    197      1.1  christos 	/* Copy the udp header into the buffer... */
    198      1.1  christos 	memcpy (&buf [*bufix], &udp, sizeof udp);
    199      1.1  christos 	*bufix += sizeof udp;
    200      1.1  christos }
    201      1.1  christos #endif /* PACKET_ASSEMBLY */
    202      1.1  christos 
    203      1.1  christos #ifdef PACKET_DECODING
    204      1.1  christos /* Decode a hardware header... */
    205      1.1  christos /* Support for ethernet, TR and FDDI
    206      1.1  christos  * Doesn't support infiniband yet as the supported oses shouldn't get here
    207      1.1  christos  */
    208      1.1  christos 
    209      1.1  christos ssize_t decode_hw_header (interface, buf, bufix, from)
    210      1.1  christos      struct interface_info *interface;
    211      1.1  christos      unsigned char *buf;
    212      1.1  christos      unsigned bufix;
    213      1.1  christos      struct hardware *from;
    214      1.1  christos {
    215      1.1  christos 	switch(interface->hw_address.hbuf[0]) {
    216      1.1  christos #if defined (HAVE_TR_SUPPORT)
    217      1.1  christos 	case HTYPE_IEEE802:
    218      1.1  christos 		return (decode_tr_header(interface, buf, bufix, from));
    219      1.1  christos #endif
    220      1.1  christos #if defined (DEC_FDDI)
    221      1.1  christos 	case HTYPE_FDDI:
    222      1.1  christos 		return (decode_fddi_header(interface, buf, bufix, from));
    223      1.1  christos #endif
    224      1.1  christos 	case HTYPE_INFINIBAND:
    225      1.1  christos 		log_error("Attempt to decode hw header for infiniband");
    226      1.1  christos 		return (0);
    227      1.1  christos 	case HTYPE_ETHER:
    228      1.1  christos 	default:
    229      1.1  christos 		return (decode_ethernet_header(interface, buf, bufix, from));
    230      1.1  christos 	}
    231      1.1  christos }
    232      1.1  christos 
    233      1.1  christos /*!
    234      1.1  christos  *
    235      1.1  christos  * \brief UDP header and IP header decoded together for convenience.
    236      1.1  christos  *
    237      1.1  christos  * Attempt to decode the UDP and IP headers and, if necessary, checksum
    238      1.1  christos  * the packet.
    239      1.1  christos  *
    240      1.1  christos  * \param inteface - the interface on which the packet was recevied
    241      1.1  christos  * \param buf - a pointer to the buffer for the received packet
    242      1.1  christos  * \param bufix - where to start processing the buffer, previous
    243      1.1  christos  *                routines may have processed parts of the buffer already
    244      1.1  christos  * \param from - space to return the address of the packet sender
    245      1.1  christos  * \param buflen - remaining length of the buffer, this will have been
    246      1.1  christos  *                 decremented by bufix by the caller
    247      1.1  christos  * \param rbuflen - space to return the length of the payload from the udp
    248      1.1  christos  *                  header
    249      1.1  christos  * \param csum_ready - indication if the checksum is valid for use
    250      1.1  christos  *                     non-zero indicates the checksum should be validated
    251      1.1  christos  *
    252      1.1  christos  * \return - the index to the first byte of the udp payload (that is the
    253      1.1  christos  *           start of the DHCP packet
    254      1.1  christos  */
    255      1.1  christos 
    256      1.1  christos ssize_t
    257      1.1  christos decode_udp_ip_header(struct interface_info *interface,
    258      1.1  christos 		     unsigned char *buf, unsigned bufix,
    259      1.1  christos 		     struct sockaddr_in *from, unsigned buflen,
    260      1.1  christos 		     unsigned *rbuflen, int csum_ready)
    261      1.1  christos {
    262      1.1  christos   unsigned char *data;
    263      1.1  christos   struct ip ip;
    264      1.1  christos   struct udphdr udp;
    265      1.1  christos   unsigned char *upp;
    266      1.1  christos   u_int32_t ip_len, ulen, pkt_len;
    267      1.1  christos   static unsigned int ip_packets_seen = 0;
    268      1.1  christos   static unsigned int ip_packets_bad_checksum = 0;
    269      1.1  christos   static unsigned int udp_packets_seen = 0;
    270      1.1  christos   static unsigned int udp_packets_bad_checksum = 0;
    271      1.1  christos   static unsigned int udp_packets_length_checked = 0;
    272      1.1  christos   static unsigned int udp_packets_length_overflow = 0;
    273      1.1  christos   unsigned len;
    274      1.1  christos 
    275      1.1  christos   /* Assure there is at least an IP header there. */
    276      1.1  christos   if (sizeof(ip) > buflen)
    277      1.1  christos 	  return -1;
    278      1.1  christos 
    279      1.1  christos   /* Copy the IP header into a stack aligned structure for inspection.
    280      1.1  christos    * There may be bits in the IP header that we're not decoding, so we
    281      1.1  christos    * copy out the bits we grok and skip ahead by ip.ip_hl * 4.
    282      1.1  christos    */
    283      1.1  christos   upp = buf + bufix;
    284      1.1  christos   memcpy(&ip, upp, sizeof(ip));
    285      1.1  christos   ip_len = (*upp & 0x0f) << 2;
    286      1.1  christos   upp += ip_len;
    287      1.1  christos 
    288      1.1  christos   /* Check packet lengths are within the buffer:
    289      1.1  christos    * first the ip header (ip_len)
    290      1.1  christos    * then the packet length from the ip header (pkt_len)
    291      1.1  christos    * then the udp header (ip_len + sizeof(udp)
    292      1.1  christos    * We are liberal in what we accept, the udp payload should fit within
    293      1.1  christos    * pkt_len, but we only check against the full buffer size.
    294      1.1  christos    */
    295      1.1  christos   pkt_len = ntohs(ip.ip_len);
    296      1.1  christos   if ((ip_len > buflen) ||
    297      1.1  christos       (pkt_len > buflen) ||
    298      1.1  christos       ((ip_len + sizeof(udp)) > buflen))
    299      1.1  christos 	  return -1;
    300      1.1  christos 
    301      1.1  christos   /* Copy the UDP header into a stack aligned structure for inspection. */
    302      1.1  christos   memcpy(&udp, upp, sizeof(udp));
    303      1.1  christos 
    304      1.1  christos #ifdef USERLAND_FILTER
    305      1.1  christos   /* Is it a UDP packet? */
    306      1.1  christos   if (ip.ip_p != IPPROTO_UDP)
    307      1.1  christos 	  return -1;
    308      1.1  christos 
    309      1.1  christos   /* Is it to the port we're serving? */
    310      1.1  christos #if defined(RELAY_PORT)
    311  1.2.6.1    martin   if ((udp.uh_dport != *libdhcp_callbacks.local_port) &&
    312      1.1  christos       ((relay_port == 0) || (udp.uh_dport != relay_port)))
    313      1.1  christos #else
    314  1.2.6.1    martin   if (udp.uh_dport != *libdhcp_callbacks.local_port)
    315      1.1  christos #endif
    316      1.1  christos 	  return -1;
    317      1.1  christos #endif /* USERLAND_FILTER */
    318      1.1  christos 
    319      1.1  christos   ulen = ntohs(udp.uh_ulen);
    320      1.1  christos   if (ulen < sizeof(udp))
    321      1.1  christos 	return -1;
    322      1.1  christos 
    323      1.1  christos   udp_packets_length_checked++;
    324      1.1  christos   /* verify that the payload length from the udp packet fits in the buffer */
    325      1.1  christos   if ((ip_len + ulen) > buflen) {
    326      1.1  christos 	udp_packets_length_overflow++;
    327      1.1  christos 	if (((udp_packets_length_checked > 4) &&
    328      1.1  christos 	     (udp_packets_length_overflow != 0)) &&
    329      1.1  christos 	    ((udp_packets_length_checked / udp_packets_length_overflow) < 2)) {
    330      1.1  christos 		log_info("%u udp packets in %u too long - dropped",
    331      1.1  christos 			 udp_packets_length_overflow,
    332      1.1  christos 			 udp_packets_length_checked);
    333      1.1  christos 		udp_packets_length_overflow = 0;
    334      1.1  christos 		udp_packets_length_checked = 0;
    335      1.1  christos 	}
    336      1.1  christos 	return -1;
    337      1.1  christos   }
    338      1.1  christos 
    339      1.1  christos   /* If at least 5 with less than 50% bad, start over */
    340      1.1  christos   if (udp_packets_length_checked > 4) {
    341      1.1  christos 	udp_packets_length_overflow = 0;
    342      1.1  christos 	udp_packets_length_checked = 0;
    343      1.1  christos   }
    344      1.1  christos 
    345      1.1  christos   /* Check the IP header checksum - it should be zero. */
    346      1.1  christos   ip_packets_seen++;
    347      1.1  christos   if (wrapsum (checksum (buf + bufix, ip_len, 0))) {
    348      1.1  christos 	  ++ip_packets_bad_checksum;
    349      1.1  christos 	  if (((ip_packets_seen > 4) && (ip_packets_bad_checksum != 0)) &&
    350      1.1  christos 	      ((ip_packets_seen / ip_packets_bad_checksum) < 2)) {
    351      1.1  christos 		  log_info ("%u bad IP checksums seen in %u packets",
    352      1.1  christos 			    ip_packets_bad_checksum, ip_packets_seen);
    353      1.1  christos 		  ip_packets_seen = ip_packets_bad_checksum = 0;
    354      1.1  christos 	  }
    355      1.1  christos 	  return -1;
    356      1.1  christos   }
    357      1.1  christos 
    358      1.1  christos   /* If at least 5 with less than 50% bad, start over */
    359      1.1  christos   if (ip_packets_seen > 4) {
    360      1.1  christos 	ip_packets_bad_checksum = 0;
    361      1.1  christos 	ip_packets_seen = 0;
    362      1.1  christos   }
    363      1.1  christos 
    364      1.1  christos   /* Copy out the IP source address... */
    365      1.1  christos   memcpy(&from->sin_addr, &ip.ip_src, 4);
    366      1.1  christos 
    367      1.1  christos   data = upp + sizeof(udp);
    368      1.1  christos   len = ulen - sizeof(udp);
    369      1.1  christos 
    370      1.1  christos   /* UDP check sum may be optional (udp.uh_sum == 0) or not ready if checksum
    371      1.1  christos    * offloading is in use */
    372      1.1  christos   udp_packets_seen++;
    373      1.1  christos   if (udp.uh_sum && csum_ready) {
    374      1.1  christos 	/* Check the UDP header checksum - since the received packet header
    375      1.1  christos 	 * contains the UDP checksum calculated by the transmitter, calculating
    376      1.1  christos 	 * it now should come out to zero. */
    377      1.1  christos 	if (wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
    378      1.1  christos 			       checksum(data, len,
    379      1.1  christos 				        checksum((unsigned char *)&ip.ip_src,
    380      1.1  christos 					         8, IPPROTO_UDP + ulen))))) {
    381      1.1  christos 		udp_packets_bad_checksum++;
    382      1.1  christos 		if (((udp_packets_seen > 4) && (udp_packets_bad_checksum != 0))
    383      1.1  christos 		    && ((udp_packets_seen / udp_packets_bad_checksum) < 2)) {
    384      1.1  christos 			log_debug ("%u bad udp checksums in %u packets",
    385      1.1  christos 			           udp_packets_bad_checksum, udp_packets_seen);
    386      1.1  christos 			udp_packets_seen = udp_packets_bad_checksum = 0;
    387      1.1  christos 		}
    388      1.1  christos 
    389      1.1  christos 		return -1;
    390      1.1  christos 	}
    391      1.1  christos   }
    392      1.1  christos 
    393      1.1  christos   /* If at least 5 with less than 50% bad, start over */
    394      1.1  christos   if (udp_packets_seen > 4) {
    395      1.1  christos 	udp_packets_bad_checksum = 0;
    396      1.1  christos 	udp_packets_seen = 0;
    397      1.1  christos   }
    398      1.1  christos 
    399      1.1  christos   /* Copy out the port... */
    400      1.1  christos   memcpy (&from -> sin_port, &udp.uh_sport, sizeof udp.uh_sport);
    401      1.1  christos 
    402      1.1  christos   /* Save the length of the UDP payload. */
    403      1.1  christos   if (rbuflen != NULL)
    404      1.1  christos 	*rbuflen = len;
    405      1.1  christos 
    406      1.1  christos   /* Return the index to the UDP payload. */
    407      1.1  christos   return ip_len + sizeof udp;
    408      1.1  christos }
    409      1.1  christos #endif /* PACKET_DECODING */
    410