File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / dhcp / common / packet.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Oct 9 09:06:54 2012 UTC (11 years, 8 months ago) by misho
Branches: dhcp, MAIN
CVS tags: v4_1_R7p0, v4_1_R7, v4_1_R4, HEAD
dhcp 4.1 r7

    1: /* packet.c
    2: 
    3:    Packet assembly code, originally contributed by Archie Cobbs. */
    4: 
    5: /*
    6:  * Copyright (c) 2009,2012 by Internet Systems Consortium, Inc. ("ISC")
    7:  * Copyright (c) 2004,2005,2007 by Internet Systems Consortium, Inc. ("ISC")
    8:  * Copyright (c) 1996-2003 by Internet Software Consortium
    9:  *
   10:  * Permission to use, copy, modify, and distribute this software for any
   11:  * purpose with or without fee is hereby granted, provided that the above
   12:  * copyright notice and this permission notice appear in all copies.
   13:  *
   14:  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
   15:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   16:  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
   17:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   18:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   19:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
   20:  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   21:  *
   22:  *   Internet Systems Consortium, Inc.
   23:  *   950 Charter Street
   24:  *   Redwood City, CA 94063
   25:  *   <info@isc.org>
   26:  *   https://www.isc.org/
   27:  *
   28:  * This code was originally contributed by Archie Cobbs, and is still
   29:  * very similar to that contribution, although the packet checksum code
   30:  * has been hacked significantly with the help of quite a few ISC DHCP
   31:  * users, without whose gracious and thorough help the checksum code would
   32:  * still be disabled.
   33:  */
   34: 
   35: #include "dhcpd.h"
   36: 
   37: #if defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING)
   38: #include "includes/netinet/ip.h"
   39: #include "includes/netinet/udp.h"
   40: #include "includes/netinet/if_ether.h"
   41: #endif /* PACKET_ASSEMBLY || PACKET_DECODING */
   42: 
   43: /* Compute the easy part of the checksum on a range of bytes. */
   44: 
   45: u_int32_t checksum (buf, nbytes, sum)
   46: 	unsigned char *buf;
   47: 	unsigned nbytes;
   48: 	u_int32_t sum;
   49: {
   50: 	unsigned i;
   51: 
   52: #ifdef DEBUG_CHECKSUM
   53: 	log_debug ("checksum (%x %d %x)", buf, nbytes, sum);
   54: #endif
   55: 
   56: 	/* Checksum all the pairs of bytes first... */
   57: 	for (i = 0; i < (nbytes & ~1U); i += 2) {
   58: #ifdef DEBUG_CHECKSUM_VERBOSE
   59: 		log_debug ("sum = %x", sum);
   60: #endif
   61: 		sum += (u_int16_t) ntohs(*((u_int16_t *)(buf + i)));
   62: 		/* Add carry. */
   63: 		if (sum > 0xFFFF)
   64: 			sum -= 0xFFFF;
   65: 	}	
   66: 
   67: 	/* If there's a single byte left over, checksum it, too.   Network
   68: 	   byte order is big-endian, so the remaining byte is the high byte. */
   69: 	if (i < nbytes) {
   70: #ifdef DEBUG_CHECKSUM_VERBOSE
   71: 		log_debug ("sum = %x", sum);
   72: #endif
   73: 		sum += buf [i] << 8;
   74: 		/* Add carry. */
   75: 		if (sum > 0xFFFF)
   76: 			sum -= 0xFFFF;
   77: 	}
   78: 	
   79: 	return sum;
   80: }
   81: 
   82: /* Finish computing the checksum, and then put it into network byte order. */
   83: 
   84: u_int32_t wrapsum (sum)
   85: 	u_int32_t sum;
   86: {
   87: #ifdef DEBUG_CHECKSUM
   88: 	log_debug ("wrapsum (%x)", sum);
   89: #endif
   90: 
   91: 	sum = ~sum & 0xFFFF;
   92: #ifdef DEBUG_CHECKSUM_VERBOSE
   93: 	log_debug ("sum = %x", sum);
   94: #endif
   95: 	
   96: #ifdef DEBUG_CHECKSUM
   97: 	log_debug ("wrapsum returns %x", htons (sum));
   98: #endif
   99: 	return htons(sum);
  100: }
  101: 
  102: #ifdef PACKET_ASSEMBLY
  103: void assemble_hw_header (interface, buf, bufix, to)
  104: 	struct interface_info *interface;
  105: 	unsigned char *buf;
  106: 	unsigned *bufix;
  107: 	struct hardware *to;
  108: {
  109: 	switch (interface->hw_address.hbuf[0]) {
  110: #if defined(HAVE_TR_SUPPORT)
  111: 	case HTYPE_IEEE802:
  112: 		assemble_tr_header(interface, buf, bufix, to);
  113: 		break;
  114: #endif
  115: #if defined (DEC_FDDI)
  116: 	case HTYPE_FDDI:
  117: 		assemble_fddi_header(interface, buf, bufix, to);
  118: 		break;
  119: #endif
  120: 	case HTYPE_INFINIBAND:
  121: 		log_error("Attempt to assemble hw header for infiniband");
  122: 		break;
  123: 	case HTYPE_ETHER:
  124: 	default:
  125: 		assemble_ethernet_header(interface, buf, bufix, to);
  126: 		break;
  127: 	}
  128: }
  129: 
  130: /* UDP header and IP header assembled together for convenience. */
  131: 
  132: void assemble_udp_ip_header (interface, buf, bufix,
  133: 			     from, to, port, data, len)
  134: 	struct interface_info *interface;
  135: 	unsigned char *buf;
  136: 	unsigned *bufix;
  137: 	u_int32_t from;
  138: 	u_int32_t to;
  139: 	u_int32_t port;
  140: 	unsigned char *data;
  141: 	unsigned len;
  142: {
  143: 	struct ip ip;
  144: 	struct udphdr udp;
  145: 
  146: 	memset (&ip, 0, sizeof ip);
  147: 
  148: 	/* Fill out the IP header */
  149: 	IP_V_SET (&ip, 4);
  150: 	IP_HL_SET (&ip, 20);
  151: 	ip.ip_tos = IPTOS_LOWDELAY;
  152: 	ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len);
  153: 	ip.ip_id = 0;
  154: 	ip.ip_off = 0;
  155: 	ip.ip_ttl = 128;
  156: 	ip.ip_p = IPPROTO_UDP;
  157: 	ip.ip_sum = 0;
  158: 	ip.ip_src.s_addr = from;
  159: 	ip.ip_dst.s_addr = to;
  160: 	
  161: 	/* Checksum the IP header... */
  162: 	ip.ip_sum = wrapsum (checksum ((unsigned char *)&ip, sizeof ip, 0));
  163: 	
  164: 	/* Copy the ip header into the buffer... */
  165: 	memcpy (&buf [*bufix], &ip, sizeof ip);
  166: 	*bufix += sizeof ip;
  167: 
  168: 	/* Fill out the UDP header */
  169: 	udp.uh_sport = local_port;		/* XXX */
  170: 	udp.uh_dport = port;			/* XXX */
  171: 	udp.uh_ulen = htons(sizeof(udp) + len);
  172: 	memset (&udp.uh_sum, 0, sizeof udp.uh_sum);
  173: 
  174: 	/* Compute UDP checksums, including the ``pseudo-header'', the UDP
  175: 	   header and the data. */
  176: 
  177: 	udp.uh_sum =
  178: 		wrapsum (checksum ((unsigned char *)&udp, sizeof udp,
  179: 				   checksum (data, len, 
  180: 					     checksum ((unsigned char *)
  181: 						       &ip.ip_src,
  182: 						       2 * sizeof ip.ip_src,
  183: 						       IPPROTO_UDP +
  184: 						       (u_int32_t)
  185: 						       ntohs (udp.uh_ulen)))));
  186: 
  187: 	/* Copy the udp header into the buffer... */
  188: 	memcpy (&buf [*bufix], &udp, sizeof udp);
  189: 	*bufix += sizeof udp;
  190: }
  191: #endif /* PACKET_ASSEMBLY */
  192: 
  193: #ifdef PACKET_DECODING
  194: /* Decode a hardware header... */
  195: /* Support for ethernet, TR and FDDI
  196:  * Doesn't support infiniband yet as the supported oses shouldn't get here
  197:  */
  198: 
  199: ssize_t decode_hw_header (interface, buf, bufix, from)
  200:      struct interface_info *interface;
  201:      unsigned char *buf;
  202:      unsigned bufix;
  203:      struct hardware *from;
  204: {
  205: 	switch(interface->hw_address.hbuf[0]) {
  206: #if defined (HAVE_TR_SUPPORT)
  207: 	case HTYPE_IEEE802:
  208: 		return (decode_tr_header(interface, buf, bufix, from));
  209: #endif
  210: #if defined (DEC_FDDI)
  211: 	case HTYPE_FDDI:
  212: 		return (decode_fddi_header(interface, buf, bufix, from));
  213: #endif
  214: 	case HTYPE_INFINIBAND:
  215: 		log_error("Attempt to decode hw header for infiniband");
  216: 		return (0);
  217: 	case HTYPE_ETHER:
  218: 	default:
  219: 		return (decode_ethernet_header(interface, buf, bufix, from));
  220: 	}
  221: }
  222: 
  223: /* UDP header and IP header decoded together for convenience. */
  224: 
  225: ssize_t
  226: decode_udp_ip_header(struct interface_info *interface,
  227: 		     unsigned char *buf, unsigned bufix,
  228: 		     struct sockaddr_in *from, unsigned buflen,
  229: 		     unsigned *rbuflen)
  230: {
  231:   unsigned char *data;
  232:   struct ip ip;
  233:   struct udphdr udp;
  234:   unsigned char *upp, *endbuf;
  235:   u_int32_t ip_len, ulen, pkt_len;
  236:   u_int32_t sum, usum;
  237:   static int ip_packets_seen;
  238:   static int ip_packets_bad_checksum;
  239:   static int udp_packets_seen;
  240:   static int udp_packets_bad_checksum;
  241:   static int udp_packets_length_checked;
  242:   static int udp_packets_length_overflow;
  243:   unsigned len;
  244: 
  245:   /* Designate the end of the input buffer for bounds checks. */
  246:   endbuf = buf + bufix + buflen;
  247: 
  248:   /* Assure there is at least an IP header there. */
  249:   if ((buf + bufix + sizeof(ip)) > endbuf)
  250: 	  return -1;
  251: 
  252:   /* Copy the IP header into a stack aligned structure for inspection.
  253:    * There may be bits in the IP header that we're not decoding, so we
  254:    * copy out the bits we grok and skip ahead by ip.ip_hl * 4.
  255:    */
  256:   upp = buf + bufix;
  257:   memcpy(&ip, upp, sizeof(ip));
  258:   ip_len = (*upp & 0x0f) << 2;
  259:   upp += ip_len;
  260: 
  261:   /* Check the IP packet length. */
  262:   pkt_len = ntohs(ip.ip_len);
  263:   if (pkt_len > buflen)
  264: 	return -1;
  265: 
  266:   /* Assure after ip_len bytes that there is enough room for a UDP header. */
  267:   if ((upp + sizeof(udp)) > endbuf)
  268: 	  return -1;
  269: 
  270:   /* Copy the UDP header into a stack aligned structure for inspection. */
  271:   memcpy(&udp, upp, sizeof(udp));
  272: 
  273: #ifdef USERLAND_FILTER
  274:   /* Is it a UDP packet? */
  275:   if (ip.ip_p != IPPROTO_UDP)
  276: 	  return -1;
  277: 
  278:   /* Is it to the port we're serving? */
  279:   if (udp.uh_dport != local_port)
  280: 	  return -1;
  281: #endif /* USERLAND_FILTER */
  282: 
  283:   ulen = ntohs(udp.uh_ulen);
  284:   if (ulen < sizeof(udp))
  285: 	return -1;
  286: 
  287:   udp_packets_length_checked++;
  288:   if ((upp + ulen) > endbuf) {
  289: 	udp_packets_length_overflow++;
  290: 	if ((udp_packets_length_checked > 4) &&
  291: 	    ((udp_packets_length_checked /
  292: 	      udp_packets_length_overflow) < 2)) {
  293: 		log_info("%d udp packets in %d too long - dropped",
  294: 			 udp_packets_length_overflow,
  295: 			 udp_packets_length_checked);
  296: 		udp_packets_length_overflow = 0;
  297: 		udp_packets_length_checked = 0;
  298: 	}
  299: 	return -1;
  300:   }
  301: 
  302:   if ((ulen < sizeof(udp)) || ((upp + ulen) > endbuf))
  303: 	return -1;
  304: 
  305:   /* Check the IP header checksum - it should be zero. */
  306:   ++ip_packets_seen;
  307:   if (wrapsum (checksum (buf + bufix, ip_len, 0))) {
  308: 	  ++ip_packets_bad_checksum;
  309: 	  if (ip_packets_seen > 4 &&
  310: 	      (ip_packets_seen / ip_packets_bad_checksum) < 2) {
  311: 		  log_info ("%d bad IP checksums seen in %d packets",
  312: 			    ip_packets_bad_checksum, ip_packets_seen);
  313: 		  ip_packets_seen = ip_packets_bad_checksum = 0;
  314: 	  }
  315: 	  return -1;
  316:   }
  317: 
  318:   /* Copy out the IP source address... */
  319:   memcpy(&from->sin_addr, &ip.ip_src, 4);
  320: 
  321:   /* Compute UDP checksums, including the ``pseudo-header'', the UDP
  322:      header and the data.   If the UDP checksum field is zero, we're
  323:      not supposed to do a checksum. */
  324: 
  325:   data = upp + sizeof(udp);
  326:   len = ulen - sizeof(udp);
  327: 
  328:   usum = udp.uh_sum;
  329:   udp.uh_sum = 0;
  330: 
  331:   /* XXX: We have to pass &udp, because we have to zero the checksum
  332:    * field before calculating the sum...'upp' isn't zeroed.
  333:    */
  334:   sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
  335: 			 checksum(data, len,
  336: 				  checksum((unsigned char *)&ip.ip_src,
  337: 					   8, IPPROTO_UDP + ulen))));
  338: 
  339:   udp_packets_seen++;
  340:   if (usum && usum != sum) {
  341: 	  udp_packets_bad_checksum++;
  342: 	  if (udp_packets_seen > 4 &&
  343: 	      (udp_packets_seen / udp_packets_bad_checksum) < 2) {
  344: 		  log_info ("%d bad udp checksums in %d packets",
  345: 			    udp_packets_bad_checksum, udp_packets_seen);
  346: 		  udp_packets_seen = udp_packets_bad_checksum = 0;
  347: 	  }
  348: 	  return -1;
  349:   }
  350: 
  351:   /* Copy out the port... */
  352:   memcpy (&from -> sin_port, &udp.uh_sport, sizeof udp.uh_sport);
  353: 
  354:   /* Save the length of the UDP payload. */
  355:   if (rbuflen != NULL)
  356: 	*rbuflen = len;
  357: 
  358:   /* Return the index to the UDP payload. */
  359:   return ip_len + sizeof udp;
  360: }
  361: #endif /* PACKET_DECODING */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>