File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / dhcp / common / packet.c
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 22:30:18 2012 UTC (12 years, 5 months ago) by misho
CVS tags: MAIN, HEAD
Initial revision

    1: /* packet.c
    2: 
    3:    Packet assembly code, originally contributed by Archie Cobbs. */
    4: 
    5: /*
    6:  * Copyright (c) 2004,2005,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
    7:  * Copyright (c) 1996-2003 by Internet Software Consortium
    8:  *
    9:  * Permission to use, copy, modify, and distribute this software for any
   10:  * purpose with or without fee is hereby granted, provided that the above
   11:  * copyright notice and this permission notice appear in all copies.
   12:  *
   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.
   20:  *
   21:  *   Internet Systems Consortium, Inc.
   22:  *   950 Charter Street
   23:  *   Redwood City, CA 94063
   24:  *   <info@isc.org>
   25:  *   https://www.isc.org/
   26:  *
   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
   31:  * still be disabled.
   32:  */
   33: 
   34: #include "dhcpd.h"
   35: 
   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 */
   41: 
   42: /* Compute the easy part of the checksum on a range of bytes. */
   43: 
   44: u_int32_t checksum (buf, nbytes, sum)
   45: 	unsigned char *buf;
   46: 	unsigned nbytes;
   47: 	u_int32_t sum;
   48: {
   49: 	unsigned i;
   50: 
   51: #ifdef DEBUG_CHECKSUM
   52: 	log_debug ("checksum (%x %d %x)", buf, nbytes, sum);
   53: #endif
   54: 
   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);
   59: #endif
   60: 		sum += (u_int16_t) ntohs(*((u_int16_t *)(buf + i)));
   61: 		/* Add carry. */
   62: 		if (sum > 0xFFFF)
   63: 			sum -= 0xFFFF;
   64: 	}	
   65: 
   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. */
   68: 	if (i < nbytes) {
   69: #ifdef DEBUG_CHECKSUM_VERBOSE
   70: 		log_debug ("sum = %x", sum);
   71: #endif
   72: 		sum += buf [i] << 8;
   73: 		/* Add carry. */
   74: 		if (sum > 0xFFFF)
   75: 			sum -= 0xFFFF;
   76: 	}
   77: 	
   78: 	return sum;
   79: }
   80: 
   81: /* Finish computing the checksum, and then put it into network byte order. */
   82: 
   83: u_int32_t wrapsum (sum)
   84: 	u_int32_t sum;
   85: {
   86: #ifdef DEBUG_CHECKSUM
   87: 	log_debug ("wrapsum (%x)", sum);
   88: #endif
   89: 
   90: 	sum = ~sum & 0xFFFF;
   91: #ifdef DEBUG_CHECKSUM_VERBOSE
   92: 	log_debug ("sum = %x", sum);
   93: #endif
   94: 	
   95: #ifdef DEBUG_CHECKSUM
   96: 	log_debug ("wrapsum returns %x", htons (sum));
   97: #endif
   98: 	return htons(sum);
   99: }
  100: 
  101: #ifdef PACKET_ASSEMBLY
  102: void assemble_hw_header (interface, buf, bufix, to)
  103: 	struct interface_info *interface;
  104: 	unsigned char *buf;
  105: 	unsigned *bufix;
  106: 	struct hardware *to;
  107: {
  108: #if defined (HAVE_TR_SUPPORT)
  109: 	if (interface -> hw_address.hbuf [0] == HTYPE_IEEE802)
  110: 		assemble_tr_header (interface, buf, bufix, to);
  111: 	else
  112: #endif
  113: #if defined (DEC_FDDI)
  114: 	     if (interface -> hw_address.hbuf [0] == HTYPE_FDDI)
  115: 		     assemble_fddi_header (interface, buf, bufix, to);
  116: 	else
  117: #endif
  118: 		assemble_ethernet_header (interface, buf, bufix, to);
  119: 
  120: }
  121: 
  122: /* UDP header and IP header assembled together for convenience. */
  123: 
  124: void assemble_udp_ip_header (interface, buf, bufix,
  125: 			     from, to, port, data, len)
  126: 	struct interface_info *interface;
  127: 	unsigned char *buf;
  128: 	unsigned *bufix;
  129: 	u_int32_t from;
  130: 	u_int32_t to;
  131: 	u_int32_t port;
  132: 	unsigned char *data;
  133: 	unsigned len;
  134: {
  135: 	struct ip ip;
  136: 	struct udphdr udp;
  137: 
  138: 	memset (&ip, 0, sizeof ip);
  139: 
  140: 	/* Fill out the IP header */
  141: 	IP_V_SET (&ip, 4);
  142: 	IP_HL_SET (&ip, 20);
  143: 	ip.ip_tos = IPTOS_LOWDELAY;
  144: 	ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len);
  145: 	ip.ip_id = 0;
  146: 	ip.ip_off = 0;
  147: 	ip.ip_ttl = 128;
  148: 	ip.ip_p = IPPROTO_UDP;
  149: 	ip.ip_sum = 0;
  150: 	ip.ip_src.s_addr = from;
  151: 	ip.ip_dst.s_addr = to;
  152: 	
  153: 	/* Checksum the IP header... */
  154: 	ip.ip_sum = wrapsum (checksum ((unsigned char *)&ip, sizeof ip, 0));
  155: 	
  156: 	/* Copy the ip header into the buffer... */
  157: 	memcpy (&buf [*bufix], &ip, sizeof ip);
  158: 	*bufix += sizeof ip;
  159: 
  160: 	/* Fill out the UDP header */
  161: 	udp.uh_sport = local_port;		/* XXX */
  162: 	udp.uh_dport = port;			/* XXX */
  163: 	udp.uh_ulen = htons(sizeof(udp) + len);
  164: 	memset (&udp.uh_sum, 0, sizeof udp.uh_sum);
  165: 
  166: 	/* Compute UDP checksums, including the ``pseudo-header'', the UDP
  167: 	   header and the data. */
  168: 
  169: 	udp.uh_sum =
  170: 		wrapsum (checksum ((unsigned char *)&udp, sizeof udp,
  171: 				   checksum (data, len, 
  172: 					     checksum ((unsigned char *)
  173: 						       &ip.ip_src,
  174: 						       2 * sizeof ip.ip_src,
  175: 						       IPPROTO_UDP +
  176: 						       (u_int32_t)
  177: 						       ntohs (udp.uh_ulen)))));
  178: 
  179: 	/* Copy the udp header into the buffer... */
  180: 	memcpy (&buf [*bufix], &udp, sizeof udp);
  181: 	*bufix += sizeof udp;
  182: }
  183: #endif /* PACKET_ASSEMBLY */
  184: 
  185: #ifdef PACKET_DECODING
  186: /* Decode a hardware header... */
  187: /* XXX currently only supports ethernet; doesn't check for other types. */
  188: 
  189: ssize_t decode_hw_header (interface, buf, bufix, from)
  190:      struct interface_info *interface;
  191:      unsigned char *buf;
  192:      unsigned bufix;
  193:      struct hardware *from;
  194: {
  195: #if defined (HAVE_TR_SUPPORT)
  196: 	if (interface -> hw_address.hbuf [0] == HTYPE_IEEE802)
  197: 		return decode_tr_header (interface, buf, bufix, from);
  198: 	else
  199: #endif
  200: #if defined (DEC_FDDI)
  201: 	     if (interface -> hw_address.hbuf [0] == HTYPE_FDDI)
  202: 		     return decode_fddi_header (interface, buf, bufix, from);
  203: 	else
  204: #endif
  205: 		return decode_ethernet_header (interface, buf, bufix, from);
  206: }
  207: 
  208: /* UDP header and IP header decoded together for convenience. */
  209: 
  210: ssize_t
  211: decode_udp_ip_header(struct interface_info *interface,
  212: 		     unsigned char *buf, unsigned bufix,
  213: 		     struct sockaddr_in *from, unsigned buflen,
  214: 		     unsigned *rbuflen)
  215: {
  216:   unsigned char *data;
  217:   struct ip ip;
  218:   struct udphdr udp;
  219:   unsigned char *upp, *endbuf;
  220:   u_int32_t ip_len, ulen, pkt_len;
  221:   u_int32_t sum, usum;
  222:   static int ip_packets_seen;
  223:   static int ip_packets_bad_checksum;
  224:   static int udp_packets_seen;
  225:   static int udp_packets_bad_checksum;
  226:   static int udp_packets_length_checked;
  227:   static int udp_packets_length_overflow;
  228:   unsigned len;
  229: 
  230:   /* Designate the end of the input buffer for bounds checks. */
  231:   endbuf = buf + bufix + buflen;
  232: 
  233:   /* Assure there is at least an IP header there. */
  234:   if ((buf + bufix + sizeof(ip)) > endbuf)
  235: 	  return -1;
  236: 
  237:   /* Copy the IP header into a stack aligned structure for inspection.
  238:    * There may be bits in the IP header that we're not decoding, so we
  239:    * copy out the bits we grok and skip ahead by ip.ip_hl * 4.
  240:    */
  241:   upp = buf + bufix;
  242:   memcpy(&ip, upp, sizeof(ip));
  243:   ip_len = (*upp & 0x0f) << 2;
  244:   upp += ip_len;
  245: 
  246:   /* Check the IP packet length. */
  247:   pkt_len = ntohs(ip.ip_len);
  248:   if (pkt_len > buflen)
  249: 	return -1;
  250: 
  251:   /* Assure after ip_len bytes that there is enough room for a UDP header. */
  252:   if ((upp + sizeof(udp)) > endbuf)
  253: 	  return -1;
  254: 
  255:   /* Copy the UDP header into a stack aligned structure for inspection. */
  256:   memcpy(&udp, upp, sizeof(udp));
  257: 
  258: #ifdef USERLAND_FILTER
  259:   /* Is it a UDP packet? */
  260:   if (ip.ip_p != IPPROTO_UDP)
  261: 	  return -1;
  262: 
  263:   /* Is it to the port we're serving? */
  264:   if (udp.uh_dport != local_port)
  265: 	  return -1;
  266: #endif /* USERLAND_FILTER */
  267: 
  268:   ulen = ntohs(udp.uh_ulen);
  269:   if (ulen < sizeof(udp))
  270: 	return -1;
  271: 
  272:   udp_packets_length_checked++;
  273:   if ((upp + ulen) > endbuf) {
  274: 	udp_packets_length_overflow++;
  275: 	if ((udp_packets_length_checked > 4) &&
  276: 	    ((udp_packets_length_checked /
  277: 	      udp_packets_length_overflow) < 2)) {
  278: 		log_info("%d udp packets in %d too long - dropped",
  279: 			 udp_packets_length_overflow,
  280: 			 udp_packets_length_checked);
  281: 		udp_packets_length_overflow = 0;
  282: 		udp_packets_length_checked = 0;
  283: 	}
  284: 	return -1;
  285:   }
  286: 
  287:   if ((ulen < sizeof(udp)) || ((upp + ulen) > endbuf))
  288: 	return -1;
  289: 
  290:   /* Check the IP header checksum - it should be zero. */
  291:   ++ip_packets_seen;
  292:   if (wrapsum (checksum (buf + bufix, ip_len, 0))) {
  293: 	  ++ip_packets_bad_checksum;
  294: 	  if (ip_packets_seen > 4 &&
  295: 	      (ip_packets_seen / ip_packets_bad_checksum) < 2) {
  296: 		  log_info ("%d bad IP checksums seen in %d packets",
  297: 			    ip_packets_bad_checksum, ip_packets_seen);
  298: 		  ip_packets_seen = ip_packets_bad_checksum = 0;
  299: 	  }
  300: 	  return -1;
  301:   }
  302: 
  303:   /* Copy out the IP source address... */
  304:   memcpy(&from->sin_addr, &ip.ip_src, 4);
  305: 
  306:   /* Compute UDP checksums, including the ``pseudo-header'', the UDP
  307:      header and the data.   If the UDP checksum field is zero, we're
  308:      not supposed to do a checksum. */
  309: 
  310:   data = upp + sizeof(udp);
  311:   len = ulen - sizeof(udp);
  312: 
  313:   usum = udp.uh_sum;
  314:   udp.uh_sum = 0;
  315: 
  316:   /* XXX: We have to pass &udp, because we have to zero the checksum
  317:    * field before calculating the sum...'upp' isn't zeroed.
  318:    */
  319:   sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
  320: 			 checksum(data, len,
  321: 				  checksum((unsigned char *)&ip.ip_src,
  322: 					   8, IPPROTO_UDP + ulen))));
  323: 
  324:   udp_packets_seen++;
  325:   if (usum && usum != sum) {
  326: 	  udp_packets_bad_checksum++;
  327: 	  if (udp_packets_seen > 4 &&
  328: 	      (udp_packets_seen / udp_packets_bad_checksum) < 2) {
  329: 		  log_info ("%d bad udp checksums in %d packets",
  330: 			    udp_packets_bad_checksum, udp_packets_seen);
  331: 		  udp_packets_seen = udp_packets_bad_checksum = 0;
  332: 	  }
  333: 	  return -1;
  334:   }
  335: 
  336:   /* Copy out the port... */
  337:   memcpy (&from -> sin_port, &udp.uh_sport, sizeof udp.uh_sport);
  338: 
  339:   /* Save the length of the UDP payload. */
  340:   if (rbuflen != NULL)
  341: 	*rbuflen = len;
  342: 
  343:   /* Return the index to the UDP payload. */
  344:   return ip_len + sizeof udp;
  345: }
  346: #endif /* PACKET_DECODING */

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