Home | History | Annotate | Line # | Download | only in common
packet.c revision 1.1
      1  1.1  christos /*	$NetBSD: packet.c,v 1.1 2018/04/07 22:34:26 christos 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.1  christos  * Copyright (c) 2004-2017 by 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.1  christos  *   950 Charter Street
     25  1.1  christos  *   Redwood City, CA 94063
     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.1  christos __RCSID("$NetBSD: packet.c,v 1.1 2018/04/07 22:34:26 christos 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.1  christos 	}
     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.1  christos 
     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.1  christos 
    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.1  christos 
    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.1  christos 
    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.1  christos 	udp.uh_sport = 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.1  christos 				   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.1  christos   if ((udp.uh_dport != local_port) &&
    312  1.1  christos       ((relay_port == 0) || (udp.uh_dport != relay_port)))
    313  1.1  christos #else
    314  1.1  christos   if (udp.uh_dport != 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