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