File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / dhcp / server / bootp.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: /* bootp.c
    2: 
    3:    BOOTP Protocol support. */
    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) 1995-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 software has been written for Internet Systems Consortium
   29:  * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
   30:  * To learn more about Internet Systems Consortium, see
   31:  * ``https://www.isc.org/''.  To learn more about Vixie Enterprises,
   32:  * see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
   33:  * ``http://www.nominum.com''.
   34:  */
   35: 
   36: #include "dhcpd.h"
   37: #include <errno.h>
   38: 
   39: #if defined (TRACING)
   40: # define send_packet trace_packet_send
   41: #endif
   42: 
   43: void bootp (packet)
   44: 	struct packet *packet;
   45: {
   46: 	int result;
   47: 	struct host_decl *hp = (struct host_decl *)0;
   48: 	struct host_decl *host = (struct host_decl *)0;
   49: 	struct packet outgoing;
   50: 	struct dhcp_packet raw;
   51: 	struct sockaddr_in to;
   52: 	struct in_addr from;
   53: 	struct hardware hto;
   54: 	struct option_state *options = (struct option_state *)0;
   55: 	struct lease *lease = (struct lease *)0;
   56: 	unsigned i;
   57: 	struct data_string d1;
   58: 	struct option_cache *oc;
   59: 	char msgbuf [1024];
   60: 	int ignorep;
   61: 	int peer_has_leases = 0;
   62: 
   63: 	if (packet -> raw -> op != BOOTREQUEST)
   64: 		return;
   65: 
   66: 	/* %Audit% This is log output. %2004.06.17,Safe%
   67: 	 * If we truncate we hope the user can get a hint from the log.
   68: 	 */
   69: 	snprintf (msgbuf, sizeof msgbuf, "BOOTREQUEST from %s via %s",
   70: 		 print_hw_addr (packet -> raw -> htype,
   71: 				packet -> raw -> hlen,
   72: 				packet -> raw -> chaddr),
   73: 		 packet -> raw -> giaddr.s_addr
   74: 		 ? inet_ntoa (packet -> raw -> giaddr)
   75: 		 : packet -> interface -> name);
   76: 
   77: 	if (!locate_network (packet)) {
   78: 		log_info ("%s: network unknown", msgbuf);
   79: 		return;
   80: 	}
   81: 
   82: 	find_lease (&lease, packet, packet -> shared_network,
   83: 		    0, 0, (struct lease *)0, MDL);
   84: 
   85: 	if (lease && lease->host)
   86: 		host_reference(&hp, lease->host, MDL);
   87: 
   88: 	if (!lease || ((lease->flags & STATIC_LEASE) == 0)) {
   89: 		struct host_decl *h;
   90: 
   91: 		/* We didn't find an applicable fixed-address host
   92: 		   declaration.  Just in case we may be able to dynamically
   93: 		   assign an address, see if there's a host declaration
   94: 		   that doesn't have an ip address associated with it. */
   95: 
   96: 		if (!hp)
   97: 			find_hosts_by_haddr(&hp, packet->raw->htype,
   98: 					    packet->raw->chaddr,
   99: 					    packet->raw->hlen, MDL);
  100: 
  101: 		for (h = hp; h; h = h -> n_ipaddr) {
  102: 			if (!h -> fixed_addr) {
  103: 				host_reference(&host, h, MDL);
  104: 				break;
  105: 			}
  106: 		}
  107: 
  108: 		if (hp)
  109: 			host_dereference(&hp, MDL);
  110: 
  111: 		if (host) {
  112: 			host_reference(&hp, host, MDL);
  113: 			host_dereference(&host, MDL);
  114: 		}
  115: 
  116: 		/* Allocate a lease if we have not yet found one. */
  117: 		if (!lease)
  118: 			allocate_lease (&lease, packet,
  119: 					packet -> shared_network -> pools,
  120: 					&peer_has_leases);
  121: 
  122: 		if (lease == NULL) {
  123: 			log_info("%s: BOOTP from dynamic client and no "
  124: 				 "dynamic leases", msgbuf);
  125: 			goto out;
  126: 		}
  127: 
  128: #if defined(FAILOVER_PROTOCOL)
  129: 		if ((lease->pool != NULL) &&
  130: 		    (lease->pool->failover_peer != NULL)) {
  131: 			dhcp_failover_state_t *peer;
  132: 
  133: 			peer = lease->pool->failover_peer;
  134: 
  135: 			/* If we are in a failover state that bars us from
  136: 			 * answering, do not do so.
  137: 			 * If we are in a cooperative state, load balance
  138: 			 * (all) responses.
  139: 			 */
  140: 			if ((peer->service_state == not_responding) ||
  141: 			    (peer->service_state == service_startup)) {
  142: 				log_info("%s: not responding%s",
  143: 					 msgbuf, peer->nrr);
  144: 				goto out;
  145: 			} else if((peer->service_state == cooperating) &&
  146: 				  !load_balance_mine(packet, peer)) {
  147: 				log_info("%s: load balance to peer %s",
  148: 					 msgbuf, peer->name);
  149: 				goto out;
  150: 			}
  151: 		}
  152: #endif
  153: 
  154: 		ack_lease (packet, lease, 0, 0, msgbuf, 0, hp);
  155: 		goto out;
  156: 	}
  157: 
  158: 	/* Run the executable statements to compute the client and server
  159: 	   options. */
  160: 	option_state_allocate (&options, MDL);
  161: 
  162: 	/* Execute the subnet statements. */
  163: 	execute_statements_in_scope ((struct binding_value **)0,
  164: 				     packet, lease, (struct client_state *)0,
  165: 				     packet -> options, options,
  166: 				     &lease -> scope, lease -> subnet -> group,
  167: 				     (struct group *)0);
  168: 
  169: 	/* Execute statements from class scopes. */
  170: 	for (i = packet -> class_count; i > 0; i--) {
  171: 		execute_statements_in_scope
  172: 			((struct binding_value **)0,
  173: 			 packet, lease, (struct client_state *)0,
  174: 			 packet -> options, options,
  175: 			 &lease -> scope, packet -> classes [i - 1] -> group,
  176: 			 lease -> subnet -> group);
  177: 	}
  178: 
  179: 	/* Execute the host statements. */
  180: 	if (hp != NULL) {
  181: 		execute_statements_in_scope (NULL, packet, lease, NULL,
  182: 					     packet->options, options,
  183: 					     &lease->scope,
  184: 					     hp->group, lease->subnet->group);
  185: 	}
  186: 	
  187: 	/* Drop the request if it's not allowed for this client. */
  188: 	if ((oc = lookup_option (&server_universe, options, SV_ALLOW_BOOTP)) &&
  189: 	    !evaluate_boolean_option_cache (&ignorep, packet, lease,
  190: 					    (struct client_state *)0,
  191: 					    packet -> options, options,
  192: 					    &lease -> scope, oc, MDL)) {
  193: 		if (!ignorep)
  194: 			log_info ("%s: bootp disallowed", msgbuf);
  195: 		goto out;
  196: 	} 
  197: 
  198: 	if ((oc = lookup_option (&server_universe,
  199: 				 options, SV_ALLOW_BOOTING)) &&
  200: 	    !evaluate_boolean_option_cache (&ignorep, packet, lease,
  201: 					    (struct client_state *)0,
  202: 					    packet -> options, options,
  203: 					    &lease -> scope, oc, MDL)) {
  204: 		if (!ignorep)
  205: 			log_info ("%s: booting disallowed", msgbuf);
  206: 		goto out;
  207: 	}
  208: 
  209: 	/* Set up the outgoing packet... */
  210: 	memset (&outgoing, 0, sizeof outgoing);
  211: 	memset (&raw, 0, sizeof raw);
  212: 	outgoing.raw = &raw;
  213: 
  214: 	/* If we didn't get a known vendor magic number on the way in,
  215: 	   just copy the input options to the output. */
  216: 	if (!packet -> options_valid &&
  217: 	    !(evaluate_boolean_option_cache
  218: 	      (&ignorep, packet, lease, (struct client_state *)0,
  219: 	       packet -> options, options, &lease -> scope,
  220: 	       lookup_option (&server_universe, options,
  221: 			      SV_ALWAYS_REPLY_RFC1048), MDL))) {
  222: 		memcpy (outgoing.raw -> options,
  223: 			packet -> raw -> options, DHCP_MAX_OPTION_LEN);
  224: 		outgoing.packet_length = BOOTP_MIN_LEN;
  225: 	} else {
  226: 
  227: 		/* Use the subnet mask from the subnet declaration if no other
  228: 		   mask has been provided. */
  229: 
  230: 		oc = (struct option_cache *)0;
  231: 		i = DHO_SUBNET_MASK;
  232: 		if (!lookup_option (&dhcp_universe, options, i)) {
  233: 			if (option_cache_allocate (&oc, MDL)) {
  234: 				if (make_const_data
  235: 				    (&oc -> expression,
  236: 				     lease -> subnet -> netmask.iabuf,
  237: 				     lease -> subnet -> netmask.len,
  238: 				     0, 0, MDL)) {
  239: 					option_code_hash_lookup(&oc->option,
  240: 							dhcp_universe.code_hash,
  241: 								&i, 0, MDL);
  242: 					save_option (&dhcp_universe,
  243: 						     options, oc);
  244: 				}
  245: 				option_cache_dereference (&oc, MDL);
  246: 			}
  247: 		}
  248: 
  249: 		/* Pack the options into the buffer.  Unlike DHCP, we
  250: 		   can't pack options into the filename and server
  251: 		   name buffers. */
  252: 
  253: 		outgoing.packet_length =
  254: 			cons_options (packet, outgoing.raw, lease,
  255: 				      (struct client_state *)0, 0,
  256: 				      packet -> options, options,
  257: 				      &lease -> scope,
  258: 				      0, 0, 1, (struct data_string *)0,
  259: 				      (const char *)0);
  260: 		if (outgoing.packet_length < BOOTP_MIN_LEN)
  261: 			outgoing.packet_length = BOOTP_MIN_LEN;
  262: 	}
  263: 
  264: 	/* Take the fields that we care about... */
  265: 	raw.op = BOOTREPLY;
  266: 	raw.htype = packet -> raw -> htype;
  267: 	raw.hlen = packet -> raw -> hlen;
  268: 	memcpy (raw.chaddr, packet -> raw -> chaddr, sizeof raw.chaddr);
  269: 	raw.hops = packet -> raw -> hops;
  270: 	raw.xid = packet -> raw -> xid;
  271: 	raw.secs = packet -> raw -> secs;
  272: 	raw.flags = packet -> raw -> flags;
  273: 	raw.ciaddr = packet -> raw -> ciaddr;
  274: 
  275: 	/* yiaddr is an ipv4 address, it must be 4 octets. */
  276: 	memcpy (&raw.yiaddr, lease->ip_addr.iabuf, 4);
  277: 
  278: 	/* If we're always supposed to broadcast to this client, set
  279: 	   the broadcast bit in the bootp flags field. */
  280: 	if ((oc = lookup_option (&server_universe,
  281: 				options, SV_ALWAYS_BROADCAST)) &&
  282: 	    evaluate_boolean_option_cache (&ignorep, packet, lease,
  283: 					   (struct client_state *)0,
  284: 					   packet -> options, options,
  285: 					   &lease -> scope, oc, MDL))
  286: 		raw.flags |= htons (BOOTP_BROADCAST);
  287: 
  288: 	/* Figure out the address of the next server. */
  289: 	memset (&d1, 0, sizeof d1);
  290: 	oc = lookup_option (&server_universe, options, SV_NEXT_SERVER);
  291: 	if (oc &&
  292: 	    evaluate_option_cache (&d1, packet, lease,
  293: 				   (struct client_state *)0,
  294: 				   packet -> options, options,
  295: 				   &lease -> scope, oc, MDL)) {
  296: 		/* If there was more than one answer, take the first. */
  297: 		if (d1.len >= 4 && d1.data)
  298: 			memcpy (&raw.siaddr, d1.data, 4);
  299: 		data_string_forget (&d1, MDL);
  300: 	} else {
  301: 		if ((lease->subnet->shared_network->interface != NULL) &&
  302: 		    lease->subnet->shared_network->interface->address_count)
  303: 		    raw.siaddr =
  304: 			lease->subnet->shared_network->interface->addresses[0];
  305: 		else if (packet->interface->address_count)
  306: 			raw.siaddr = packet->interface->addresses[0];
  307: 	}
  308: 
  309: 	raw.giaddr = packet -> raw -> giaddr;
  310: 
  311: 	/* Figure out the filename. */
  312: 	oc = lookup_option (&server_universe, options, SV_FILENAME);
  313: 	if (oc &&
  314: 	    evaluate_option_cache (&d1, packet, lease,
  315: 				   (struct client_state *)0,
  316: 				   packet -> options, options,
  317: 				   &lease -> scope, oc, MDL)) {
  318: 		memcpy (raw.file, d1.data,
  319: 			d1.len > sizeof raw.file ? sizeof raw.file : d1.len);
  320: 		if (sizeof raw.file > d1.len)
  321: 			memset (&raw.file [d1.len],
  322: 				0, (sizeof raw.file) - d1.len);
  323: 		data_string_forget (&d1, MDL);
  324: 	} else
  325: 		memcpy (raw.file, packet -> raw -> file, sizeof raw.file);
  326: 
  327: 	/* Choose a server name as above. */
  328: 	oc = lookup_option (&server_universe, options, SV_SERVER_NAME);
  329: 	if (oc &&
  330: 	    evaluate_option_cache (&d1, packet, lease,
  331: 				   (struct client_state *)0,
  332: 				   packet -> options, options,
  333: 				   &lease -> scope, oc, MDL)) {
  334: 		memcpy (raw.sname, d1.data,
  335: 			d1.len > sizeof raw.sname ? sizeof raw.sname : d1.len);
  336: 		if (sizeof raw.sname > d1.len)
  337: 			memset (&raw.sname [d1.len],
  338: 				0, (sizeof raw.sname) - d1.len);
  339: 		data_string_forget (&d1, MDL);
  340: 	}
  341: 
  342: 	/* Execute the commit statements, if there are any. */
  343: 	execute_statements ((struct binding_value **)0,
  344: 			    packet, lease, (struct client_state *)0,
  345: 			    packet -> options,
  346: 			    options, &lease -> scope, lease -> on_commit);
  347: 
  348: 	/* We're done with the option state. */
  349: 	option_state_dereference (&options, MDL);
  350: 
  351: 	/* Set up the hardware destination address... */
  352: 	hto.hbuf [0] = packet -> raw -> htype;
  353: 	hto.hlen = packet -> raw -> hlen + 1;
  354: 	memcpy (&hto.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen);
  355: 
  356: 	if (packet->interface->address_count) {
  357: 		from = packet->interface->addresses[0];
  358: 	} else {
  359: 		log_error("%s: Interface %s appears to have no IPv4 "
  360: 			  "addresses, and so dhcpd cannot select a source "
  361: 			  "address.", msgbuf, packet->interface->name);
  362: 		goto out;
  363: 	}
  364: 
  365: 	/* Report what we're doing... */
  366: 	log_info("%s", msgbuf);
  367: 	log_info("BOOTREPLY for %s to %s (%s) via %s",
  368: 		 piaddr(lease->ip_addr),
  369: 		 ((hp != NULL) && (hp->name != NULL)) ? hp -> name : "unknown",
  370: 		 print_hw_addr (packet->raw->htype,
  371: 				packet->raw->hlen,
  372: 				packet->raw->chaddr),
  373: 		 packet->raw->giaddr.s_addr
  374: 		 ? inet_ntoa (packet->raw->giaddr)
  375: 		 : packet->interface->name);
  376: 
  377: 	/* Set up the parts of the address that are in common. */
  378: 	to.sin_family = AF_INET;
  379: #ifdef HAVE_SA_LEN
  380: 	to.sin_len = sizeof to;
  381: #endif
  382: 	memset (to.sin_zero, 0, sizeof to.sin_zero);
  383: 
  384: 	/* If this was gatewayed, send it back to the gateway... */
  385: 	if (raw.giaddr.s_addr) {
  386: 		to.sin_addr = raw.giaddr;
  387: 		to.sin_port = local_port;
  388: 
  389: 		if (fallback_interface) {
  390: 			result = send_packet (fallback_interface, NULL, &raw,
  391: 					      outgoing.packet_length, from,
  392: 					      &to, &hto);
  393: 			if (result < 0) {
  394: 				log_error ("%s:%d: Failed to send %d byte long "
  395: 					   "packet over %s interface.", MDL,
  396: 					   outgoing.packet_length,
  397: 					   fallback_interface->name);
  398: 			}
  399: 
  400: 			goto out;
  401: 		}
  402: 
  403: 	/* If it comes from a client that already knows its address
  404: 	   and is not requesting a broadcast response, and we can
  405: 	   unicast to a client without using the ARP protocol, sent it
  406: 	   directly to that client. */
  407: 	} else if (!(raw.flags & htons (BOOTP_BROADCAST)) &&
  408: 		   can_unicast_without_arp (packet -> interface)) {
  409: 		to.sin_addr = raw.yiaddr;
  410: 		to.sin_port = remote_port;
  411: 
  412: 	/* Otherwise, broadcast it on the local network. */
  413: 	} else {
  414: 		to.sin_addr = limited_broadcast;
  415: 		to.sin_port = remote_port; /* XXX */
  416: 	}
  417: 
  418: 	errno = 0;
  419: 	result = send_packet(packet->interface, packet, &raw,
  420: 			     outgoing.packet_length, from, &to, &hto);
  421: 	if (result < 0) {
  422: 		log_error ("%s:%d: Failed to send %d byte long packet over %s"
  423: 			   " interface.", MDL, outgoing.packet_length,
  424: 			   packet->interface->name);
  425: 	}
  426: 
  427:       out:
  428: 
  429: 	if (options)
  430: 		option_state_dereference (&options, MDL);
  431: 	if (lease)
  432: 		lease_dereference (&lease, MDL);
  433: 	if (hp)
  434: 		host_dereference (&hp, MDL);
  435: 	if (host)
  436: 		host_dereference (&host, MDL);
  437: }

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