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

    1: /* failover.c
    2: 
    3:    Failover protocol support code... */
    4: 
    5: /*
    6:  * Copyright (c) 2011 by Internet Systems Consortium, Inc. ("ISC")
    7:  * Copyright (c) 2004-2009 by Internet Systems Consortium, Inc. ("ISC")
    8:  * Copyright (c) 1999-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 <omapip/omapip_p.h>
   38: 
   39: #if defined (FAILOVER_PROTOCOL)
   40: dhcp_failover_state_t *failover_states;
   41: static isc_result_t do_a_failover_option (omapi_object_t *,
   42: 					  dhcp_failover_link_t *);
   43: dhcp_failover_listener_t *failover_listeners;
   44: 
   45: static isc_result_t failover_message_reference (failover_message_t **,
   46: 						failover_message_t *,
   47: 						const char *file, int line);
   48: static isc_result_t failover_message_dereference (failover_message_t **,
   49: 						  const char *file, int line);
   50: 
   51: static void dhcp_failover_pool_balance(dhcp_failover_state_t *state);
   52: static void dhcp_failover_pool_reqbalance(dhcp_failover_state_t *state);
   53: static int dhcp_failover_pool_dobalance(dhcp_failover_state_t *state,
   54: 					isc_boolean_t *sendreq);
   55: static inline int secondary_not_hoarding(dhcp_failover_state_t *state,
   56: 					 struct pool *p);
   57: 
   58: 
   59: void dhcp_failover_startup ()
   60: {
   61: 	dhcp_failover_state_t *state;
   62: 	isc_result_t status;
   63: 	struct timeval tv;
   64: 
   65: 	for (state = failover_states; state; state = state -> next) {
   66: 		dhcp_failover_state_transition (state, "startup");
   67: 
   68: 		if (state -> pool_count == 0) {
   69: 			log_error ("failover peer declaration with no %s",
   70: 				   "referring pools.");
   71: 			log_error ("In order to use failover, you MUST %s",
   72: 				   "refer to your main failover declaration");
   73: 			log_error ("in each pool declaration.   You MUST %s",
   74: 				   "NOT use range declarations outside");
   75: 			log_fatal ("of pool declarations.");
   76: 		}
   77: 		/* In case the peer is already running, immediately try
   78: 		   to establish a connection with it. */
   79: 		status = dhcp_failover_link_initiate ((omapi_object_t *)state);
   80: 		if (status != ISC_R_SUCCESS && status != ISC_R_INCOMPLETE) {
   81: #if defined (DEBUG_FAILOVER_TIMING)
   82: 			log_info ("add_timeout +90 dhcp_failover_reconnect");
   83: #endif
   84: 			tv . tv_sec = cur_time + 90;
   85: 			tv . tv_usec = 0;
   86: 			add_timeout (&tv,
   87: 				     dhcp_failover_reconnect, state,
   88: 				     (tvref_t)
   89: 				     dhcp_failover_state_reference,
   90: 				     (tvunref_t)
   91: 				     dhcp_failover_state_dereference);
   92: 			log_error ("failover peer %s: %s", state -> name,
   93: 				   isc_result_totext (status));
   94: 		}
   95: 
   96: 		status = (dhcp_failover_listen
   97: 			  ((omapi_object_t *)state));
   98: 		if (status != ISC_R_SUCCESS) {
   99: #if defined (DEBUG_FAILOVER_TIMING)
  100: 			log_info ("add_timeout +90 %s",
  101: 				  "dhcp_failover_listener_restart");
  102: #endif
  103: 			tv . tv_sec = cur_time + 90;
  104: 			tv . tv_usec = 0;
  105: 			add_timeout (&tv,
  106: 				     dhcp_failover_listener_restart,
  107: 				     state,
  108: 				     (tvref_t)omapi_object_reference,
  109: 				     (tvunref_t)omapi_object_dereference);
  110: 		}
  111: 	}
  112: }
  113: 
  114: int dhcp_failover_write_all_states ()
  115: {
  116: 	dhcp_failover_state_t *state;
  117: 
  118: 	for (state = failover_states; state; state = state -> next) {
  119: 		if (!write_failover_state (state))
  120: 			return 0;
  121: 	}
  122: 	return 1;
  123: }
  124: 
  125: isc_result_t enter_failover_peer (peer)
  126: 	dhcp_failover_state_t *peer;
  127: {
  128: 	dhcp_failover_state_t *dup = (dhcp_failover_state_t *)0;
  129: 	isc_result_t status;
  130: 
  131: 	status = find_failover_peer (&dup, peer -> name, MDL);
  132: 	if (status == ISC_R_NOTFOUND) {
  133: 		if (failover_states) {
  134: 			dhcp_failover_state_reference (&peer -> next,
  135: 						      failover_states, MDL);
  136: 			dhcp_failover_state_dereference (&failover_states,
  137: 							 MDL);
  138: 		}
  139: 		dhcp_failover_state_reference (&failover_states, peer, MDL);
  140: 		return ISC_R_SUCCESS;
  141: 	}
  142: 	dhcp_failover_state_dereference (&dup, MDL);
  143: 	if (status == ISC_R_SUCCESS)
  144: 		return ISC_R_EXISTS;
  145: 	return status;
  146: }
  147: 
  148: isc_result_t find_failover_peer (peer, name, file, line)
  149: 	dhcp_failover_state_t **peer;
  150: 	const char *name;
  151: 	const char *file;
  152: 	int line;
  153: {
  154: 	dhcp_failover_state_t *p;
  155: 
  156: 	for (p = failover_states; p; p = p -> next)
  157: 		if (!strcmp (name, p -> name))
  158: 			break;
  159: 	if (p)
  160: 		return dhcp_failover_state_reference (peer, p, file, line);
  161: 	return ISC_R_NOTFOUND;
  162: }
  163: 
  164: /* The failover protocol has three objects associated with it.  For
  165:    each failover partner declaration in the dhcpd.conf file, primary
  166:    or secondary, there is a failover_state object.  For any primary or
  167:    secondary state object that has a connection to its peer, there is
  168:    also a failover_link object, which has its own input state separate
  169:    from the failover protocol state for managing the actual bytes
  170:    coming in off the wire.  Finally, there will be one listener object
  171:    for every distinct port number associated with a secondary
  172:    failover_state object.  Normally all secondary failover_state
  173:    objects are expected to listen on the same port number, so there
  174:    need be only one listener object, but if different port numbers are
  175:    specified for each failover object, there could be as many as one
  176:    listener object for each secondary failover_state object. */
  177: 
  178: /* This, then, is the implementation of the failover link object. */
  179: 
  180: isc_result_t dhcp_failover_link_initiate (omapi_object_t *h)
  181: {
  182: 	isc_result_t status;
  183: 	dhcp_failover_link_t *obj;
  184: 	dhcp_failover_state_t *state;
  185: 	omapi_object_t *o;
  186: 	int i;
  187: 	struct data_string ds;
  188: 	omapi_addr_list_t *addrs = (omapi_addr_list_t *)0;
  189: 	omapi_addr_t local_addr;
  190: 
  191: 	/* Find the failover state in the object chain. */
  192: 	for (o = h; o -> outer; o = o -> outer)
  193: 		;
  194: 	for (; o; o = o -> inner) {
  195: 		if (o -> type == dhcp_type_failover_state)
  196: 			break;
  197: 	}
  198: 	if (!o)
  199: 		return ISC_R_INVALIDARG;
  200: 	state = (dhcp_failover_state_t *)o;
  201: 
  202: 	obj = (dhcp_failover_link_t *)0;
  203: 	status = dhcp_failover_link_allocate (&obj, MDL);
  204: 	if (status != ISC_R_SUCCESS)
  205: 		return status;
  206: 	option_cache_reference (&obj -> peer_address,
  207: 				state -> partner.address, MDL);
  208: 	obj -> peer_port = state -> partner.port;
  209: 	dhcp_failover_state_reference (&obj -> state_object, state, MDL);
  210: 
  211: 	memset (&ds, 0, sizeof ds);
  212: 	if (!evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0,
  213: 				    (struct client_state *)0,
  214: 				    (struct option_state *)0,
  215: 				    (struct option_state *)0,
  216: 				    &global_scope, obj -> peer_address, MDL)) {
  217: 		dhcp_failover_link_dereference (&obj, MDL);
  218: 		return ISC_R_UNEXPECTED;
  219: 	}
  220: 
  221: 	/* Make an omapi address list out of a buffer containing zero or more
  222: 	   IPv4 addresses. */
  223: 	status = omapi_addr_list_new (&addrs, ds.len / 4, MDL);
  224: 	if (status != ISC_R_SUCCESS) {
  225: 		dhcp_failover_link_dereference (&obj, MDL);
  226: 		return status;
  227: 	}
  228: 
  229: 	for (i = 0; i  < addrs -> count; i++) {
  230: 		addrs -> addresses [i].addrtype = AF_INET;
  231: 		addrs -> addresses [i].addrlen = sizeof (struct in_addr);
  232: 		memcpy (addrs -> addresses [i].address,
  233: 			&ds.data [i * 4], sizeof (struct in_addr));
  234: 		addrs -> addresses [i].port = obj -> peer_port;
  235: 	}
  236: 	data_string_forget (&ds, MDL);
  237: 
  238: 	/* Now figure out the local address that we're supposed to use. */
  239: 	if (!state -> me.address ||
  240: 	    !evaluate_option_cache (&ds, (struct packet *)0,
  241: 				    (struct lease *)0,
  242: 				    (struct client_state *)0,
  243: 				    (struct option_state *)0,
  244: 				    (struct option_state *)0,
  245: 				    &global_scope, state -> me.address,
  246: 				    MDL)) {
  247: 		memset (&local_addr, 0, sizeof local_addr);
  248: 		local_addr.addrtype = AF_INET;
  249: 		local_addr.addrlen = sizeof (struct in_addr);
  250: 		if (!state -> server_identifier.len) {
  251: 			log_fatal ("failover peer %s: no local address.",
  252: 				   state -> name);
  253: 		}
  254: 	} else {
  255: 		if (ds.len != sizeof (struct in_addr)) {
  256: 			log_error("failover peer %s: 'address' parameter "
  257: 				  "fails to resolve to an IPv4 address",
  258: 				  state->name);
  259: 			data_string_forget (&ds, MDL);
  260: 			dhcp_failover_link_dereference (&obj, MDL);
  261: 			omapi_addr_list_dereference (&addrs, MDL);
  262: 			return ISC_R_INVALIDARG;
  263: 		}
  264: 		local_addr.addrtype = AF_INET;
  265: 		local_addr.addrlen = ds.len;
  266: 		memcpy (local_addr.address, ds.data, ds.len);
  267: 		if (!state -> server_identifier.len)
  268: 			data_string_copy (&state -> server_identifier,
  269: 					  &ds, MDL);
  270: 		data_string_forget (&ds, MDL);
  271: 		local_addr.port = 0;  /* Let the O.S. choose. */
  272: 	}
  273: 
  274: 	status = omapi_connect_list ((omapi_object_t *)obj,
  275: 				     addrs, &local_addr);
  276: 	omapi_addr_list_dereference (&addrs, MDL);
  277: 
  278: 	dhcp_failover_link_dereference (&obj, MDL);
  279: 	return status;
  280: }
  281: 
  282: isc_result_t dhcp_failover_link_signal (omapi_object_t *h,
  283: 					const char *name, va_list ap)
  284: {
  285: 	isc_result_t status;
  286: 	dhcp_failover_link_t *link;
  287: 	omapi_object_t *c;
  288: 	dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
  289: 	char *sname;
  290: 	int slen;
  291: 	struct timeval tv;
  292: 
  293: 	if (h -> type != dhcp_type_failover_link) {
  294: 		/* XXX shouldn't happen.   Put an assert here? */
  295: 		return ISC_R_UNEXPECTED;
  296: 	}
  297: 	link = (dhcp_failover_link_t *)h;
  298: 
  299: 	if (!strcmp (name, "connect")) {
  300: 	    if (link -> state_object -> i_am == primary) {
  301: 		status = dhcp_failover_send_connect (h);
  302: 		if (status != ISC_R_SUCCESS) {
  303: 		    log_info ("dhcp_failover_send_connect: %s",
  304: 			      isc_result_totext (status));
  305: 		    omapi_disconnect (h -> outer, 1);
  306: 		}
  307: 	    } else
  308: 		status = ISC_R_SUCCESS;
  309: 	    /* Allow the peer fifteen seconds to send us a
  310: 	       startup message. */
  311: #if defined (DEBUG_FAILOVER_TIMING)
  312: 	    log_info ("add_timeout +15 %s",
  313: 		      "dhcp_failover_link_startup_timeout");
  314: #endif
  315: 	    tv . tv_sec = cur_time + 15;
  316: 	    tv . tv_usec = 0;
  317: 	    add_timeout (&tv,
  318: 			 dhcp_failover_link_startup_timeout,
  319: 			 link,
  320: 			 (tvref_t)dhcp_failover_link_reference,
  321: 			 (tvunref_t)dhcp_failover_link_dereference);
  322: 	    return status;
  323: 	}
  324: 
  325: 	if (!strcmp (name, "disconnect")) {
  326: 	    if (link -> state_object) {
  327: 		dhcp_failover_state_reference (&state,
  328: 					       link -> state_object, MDL);
  329: 		link -> state = dhcp_flink_disconnected;
  330: 
  331: 		/* Make the transition. */
  332: 		if (state->link_to_peer == link)
  333: 		    dhcp_failover_state_transition(link->state_object, name);
  334: 
  335: 		/* Schedule an attempt to reconnect. */
  336: #if defined (DEBUG_FAILOVER_TIMING)
  337: 		log_info("add_timeout +5 dhcp_failover_reconnect");
  338: #endif
  339: 		tv.tv_sec = cur_time + 5;
  340: 		tv.tv_usec = cur_tv.tv_usec;
  341: 		add_timeout(&tv, dhcp_failover_reconnect, state,
  342: 			    (tvref_t)dhcp_failover_state_reference,
  343: 			    (tvunref_t)dhcp_failover_state_dereference);
  344: 
  345: 		dhcp_failover_state_dereference (&state, MDL);
  346: 	    }
  347: 	    return ISC_R_SUCCESS;
  348: 	}
  349: 
  350: 	if (!strcmp (name, "status")) {
  351: 	  if (link -> state_object) {
  352: 	    isc_result_t	status;
  353: 
  354: 	    status = va_arg(ap, isc_result_t);
  355: 
  356: 	    if ((status == ISC_R_HOSTUNREACH) || (status == ISC_R_TIMEDOUT)) {
  357: 	      dhcp_failover_state_reference (&state,
  358: 					     link -> state_object, MDL);
  359: 	      link -> state = dhcp_flink_disconnected;
  360: 
  361: 	      /* Make the transition. */
  362: 	      dhcp_failover_state_transition (link -> state_object,
  363: 					      "disconnect");
  364: 
  365: 	      /* Start trying to reconnect. */
  366: #if defined (DEBUG_FAILOVER_TIMING)
  367: 	      log_info ("add_timeout +5 %s",
  368: 			"dhcp_failover_reconnect");
  369: #endif
  370: 	      tv . tv_sec = cur_time + 5;
  371: 	      tv . tv_usec = 0;
  372: 	      add_timeout (&tv, dhcp_failover_reconnect,
  373: 			   state,
  374: 			   (tvref_t)dhcp_failover_state_reference,
  375: 			   (tvunref_t)dhcp_failover_state_dereference);
  376: 	    }
  377: 	    dhcp_failover_state_dereference (&state, MDL);
  378: 	  }
  379: 	  return ISC_R_SUCCESS;
  380: 	}
  381: 
  382: 	/* Not a signal we recognize? */
  383: 	if (strcmp (name, "ready")) {
  384: 		if (h -> inner && h -> inner -> type -> signal_handler)
  385: 			return (*(h -> inner -> type -> signal_handler))
  386: 				(h -> inner, name, ap);
  387: 		return ISC_R_NOTFOUND;
  388: 	}
  389: 
  390: 	if (!h -> outer || h -> outer -> type != omapi_type_connection)
  391: 		return ISC_R_INVALIDARG;
  392: 	c = h -> outer;
  393: 
  394: 	/* We get here because we requested that we be woken up after
  395:            some number of bytes were read, and that number of bytes
  396:            has in fact been read. */
  397: 	switch (link -> state) {
  398: 	      case dhcp_flink_start:
  399: 		link -> state = dhcp_flink_message_length_wait;
  400: 		if ((omapi_connection_require (c, 2)) != ISC_R_SUCCESS)
  401: 			break;
  402: 	      case dhcp_flink_message_length_wait:
  403: 	      next_message:
  404: 		link -> state = dhcp_flink_message_wait;
  405: 		link -> imsg = dmalloc (sizeof (failover_message_t), MDL);
  406: 		if (!link -> imsg) {
  407: 			status = ISC_R_NOMEMORY;
  408: 		      dhcp_flink_fail:
  409: 			if (link -> imsg) {
  410: 				failover_message_dereference (&link->imsg,
  411: 							      MDL);
  412: 			}
  413: 			link -> state = dhcp_flink_disconnected;
  414: 			log_info ("message length wait: %s",
  415: 				  isc_result_totext (status));
  416: 			omapi_disconnect (c, 1);
  417: 			/* XXX just blow away the protocol state now?
  418: 			   XXX or will disconnect blow it away? */
  419: 			return ISC_R_UNEXPECTED;
  420: 		}
  421: 		memset (link -> imsg, 0, sizeof (failover_message_t));
  422: 		link -> imsg -> refcnt = 1;
  423: 		/* Get the length: */
  424: 		omapi_connection_get_uint16 (c, &link -> imsg_len);
  425: 		link -> imsg_count = 0;	/* Bytes read. */
  426: 
  427: 		/* Ensure the message is of valid length. */
  428: 		if (link->imsg_len < DHCP_FAILOVER_MIN_MESSAGE_SIZE ||
  429: 		    link->imsg_len > DHCP_FAILOVER_MAX_MESSAGE_SIZE) {
  430: 			status = ISC_R_UNEXPECTED;
  431: 			goto dhcp_flink_fail;
  432: 		}
  433: 
  434: 		if ((omapi_connection_require (c, link -> imsg_len - 2U)) !=
  435: 		    ISC_R_SUCCESS)
  436: 			break;
  437: 	      case dhcp_flink_message_wait:
  438: 		/* Read in the message.  At this point we have the
  439: 		   entire message in the input buffer.  For each
  440: 		   incoming value ID, set a bit in the bitmask
  441: 		   indicating that we've gotten it.  Maybe flag an
  442: 		   error message if the bit is already set.  Once
  443: 		   we're done reading, we can check the bitmask to
  444: 		   make sure that the required fields for each message
  445: 		   have been included. */
  446: 
  447: 		link -> imsg_count += 2;	/* Count the length as read. */
  448: 
  449: 		/* Get message type. */
  450: 		omapi_connection_copyout (&link -> imsg -> type, c, 1);
  451: 		link -> imsg_count++;
  452: 
  453: 		/* Get message payload offset. */
  454: 		omapi_connection_copyout (&link -> imsg_payoff, c, 1);
  455: 		link -> imsg_count++;
  456: 
  457: 		/* Get message time. */
  458: 		omapi_connection_get_uint32 (c, &link -> imsg -> time);
  459: 		link -> imsg_count += 4;
  460: 
  461: 		/* Get transaction ID. */
  462: 		omapi_connection_get_uint32 (c, &link -> imsg -> xid);
  463: 		link -> imsg_count += 4;
  464: 
  465: #if defined (DEBUG_FAILOVER_MESSAGES)
  466: # if !defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
  467: 		if (link->imsg->type == FTM_CONTACT)
  468: 			goto skip_contact;
  469: # endif
  470: 		log_info ("link: message %s  payoff %d  time %ld  xid %ld",
  471: 			  dhcp_failover_message_name (link -> imsg -> type),
  472: 			  link -> imsg_payoff,
  473: 			  (unsigned long)link -> imsg -> time,
  474: 			  (unsigned long)link -> imsg -> xid);
  475: # if !defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
  476: 	      skip_contact:
  477: # endif
  478: #endif
  479: 		/* Skip over any portions of the message header that we
  480: 		   don't understand. */
  481: 		if (link -> imsg_payoff - link -> imsg_count) {
  482: 			omapi_connection_copyout ((unsigned char *)0, c,
  483: 						  (link -> imsg_payoff -
  484: 						   link -> imsg_count));
  485: 			link -> imsg_count = link -> imsg_payoff;
  486: 		}
  487: 				
  488: 		/* Now start sucking options off the wire. */
  489: 		while (link -> imsg_count < link -> imsg_len) {
  490: 			status = do_a_failover_option (c, link);
  491: 			if (status != ISC_R_SUCCESS)
  492: 				goto dhcp_flink_fail;
  493: 		}
  494: 
  495: 		/* If it's a connect message, try to associate it with
  496: 		   a state object. */
  497: 		/* XXX this should be authenticated! */
  498: 		if (link -> imsg -> type == FTM_CONNECT) {
  499: 		    const char *errmsg;
  500: 		    int reason;
  501: 
  502: 		    if (!(link->imsg->options_present &
  503: 				FTB_RELATIONSHIP_NAME)) {
  504: 			errmsg = "missing relationship-name";
  505: 			reason = FTR_INVALID_PARTNER;
  506: 			goto badconnect;
  507: 		    }
  508: 
  509: 		    /* See if we can find a failover_state object that
  510: 		       matches this connection.  This message should only
  511: 		       be received by a secondary from a primary. */
  512: 		    for (s = failover_states; s; s = s -> next) {
  513: 			if (dhcp_failover_state_match_by_name(s,
  514: 			    &link->imsg->relationship_name))
  515: 				state = s;
  516: 		    }
  517: 
  518: 		    /* If we can't find a failover protocol state
  519: 		       for this remote host, drop the connection */
  520: 		    if (!state) {
  521: 			    errmsg = "unknown failover relationship name";
  522: 			    reason = FTR_INVALID_PARTNER;
  523: 
  524: 			  badconnect:
  525: 				/* XXX Send a refusal message first?
  526: 				   XXX Look in protocol spec for guidance. */
  527: 
  528: 			    if (state != NULL) {
  529: 				sname = state->name;
  530: 				slen = strlen(sname);
  531: 			    } else if (link->imsg->options_present &
  532: 				       FTB_RELATIONSHIP_NAME) {
  533: 				sname = (char *)link->imsg->
  534: 						relationship_name.data;
  535: 				slen = link->imsg->relationship_name.count;
  536: 			    } else {
  537: 				sname = "unknown";
  538: 				slen = strlen(sname);
  539: 			    }
  540: 
  541: 			    log_error("Failover CONNECT from %.*s: %s",
  542: 				      slen, sname, errmsg);
  543: 			    dhcp_failover_send_connectack
  544: 				    ((omapi_object_t *)link, state,
  545: 				     reason, errmsg);
  546: 			    log_info ("failover: disconnect: %s", errmsg);
  547: 			    omapi_disconnect (c, 0);
  548: 			    link -> state = dhcp_flink_disconnected;
  549: 			    return ISC_R_SUCCESS;
  550: 		    }
  551: 
  552: 		    if ((cur_time > link -> imsg -> time &&
  553: 			 cur_time - link -> imsg -> time > 60) ||
  554: 			(cur_time < link -> imsg -> time &&
  555: 			 link -> imsg -> time - cur_time > 60)) {
  556: 			    errmsg = "time offset too large";
  557: 			    reason = FTR_TIMEMISMATCH;
  558: 			    goto badconnect;
  559: 		    }
  560: 
  561: 		    if (!(link -> imsg -> options_present & FTB_HBA) ||
  562: 			link -> imsg -> hba.count != 32) {
  563: 			    errmsg = "invalid HBA";
  564: 			    reason = FTR_HBA_CONFLICT; /* XXX */
  565: 			    goto badconnect;
  566: 		    }
  567: 		    if (state -> hba)
  568: 			    dfree (state -> hba, MDL);
  569: 		    state -> hba = dmalloc (32, MDL);
  570: 		    if (!state -> hba) {
  571: 			    errmsg = "no memory";
  572: 			    reason = FTR_MISC_REJECT;
  573: 			    goto badconnect;
  574: 		    }
  575: 		    memcpy (state -> hba, link -> imsg -> hba.data, 32);
  576: 
  577: 		    if (!link -> state_object)
  578: 			    dhcp_failover_state_reference
  579: 				    (&link -> state_object, state, MDL);
  580: 		    if (!link -> peer_address)
  581: 			    option_cache_reference
  582: 				    (&link -> peer_address,
  583: 				     state -> partner.address, MDL);
  584: 		}
  585: 
  586: 		/* If we don't have a state object at this point, it's
  587: 		   some kind of bogus situation, so just drop the
  588: 		   connection. */
  589: 		if (!link -> state_object) {
  590: 			log_info ("failover: connect: no matching state.");
  591: 			omapi_disconnect (c, 1);
  592: 			link -> state = dhcp_flink_disconnected;
  593: 			return ISC_R_INVALIDARG;
  594: 		}
  595: 
  596: 		/* Once we have the entire message, and we've validated
  597: 		   it as best we can here, pass it to the parent. */
  598: 		omapi_signal ((omapi_object_t *)link -> state_object,
  599: 			      "message", link);
  600: 		link -> state = dhcp_flink_message_length_wait;
  601: 		if (link -> imsg)
  602: 			failover_message_dereference (&link -> imsg, MDL);
  603: 		/* XXX This is dangerous because we could get into a tight
  604: 		   XXX loop reading input without servicing any other stuff.
  605: 		   XXX There needs to be a way to relinquish control but
  606: 		   XXX get it back immediately if there's no other work to
  607: 		   XXX do. */
  608: 		if ((omapi_connection_require (c, 2)) == ISC_R_SUCCESS)
  609: 			goto next_message;
  610: 		break;
  611: 
  612: 	      default:
  613: 		log_fatal("Impossible case at %s:%d.", MDL);
  614: 		break;
  615: 	}
  616: 	return ISC_R_SUCCESS;
  617: }
  618: 
  619: static isc_result_t do_a_failover_option (c, link)
  620: 	omapi_object_t *c;
  621: 	dhcp_failover_link_t *link;
  622: {
  623: 	u_int16_t option_code;
  624: 	u_int16_t option_len;
  625: 	unsigned char *op;
  626: 	unsigned op_size;
  627: 	unsigned op_count;
  628: 	int i;
  629: 	
  630: 	if (link -> imsg_count + 2 > link -> imsg_len) {
  631: 		log_error ("FAILOVER: message overflow at option code.");
  632: 		return ISC_R_PROTOCOLERROR;
  633: 	}
  634: 
  635: 	/* Get option code. */
  636: 	omapi_connection_get_uint16 (c, &option_code);
  637: 	link -> imsg_count += 2;
  638: 	
  639: 	if (link -> imsg_count + 2 > link -> imsg_len) {
  640: 		log_error ("FAILOVER: message overflow at length.");
  641: 		return ISC_R_PROTOCOLERROR;
  642: 	}
  643: 
  644: 	/* Get option length. */
  645: 	omapi_connection_get_uint16 (c, &option_len);
  646: 	link -> imsg_count += 2;
  647: 
  648: 	if (link -> imsg_count + option_len > link -> imsg_len) {
  649: 		log_error ("FAILOVER: message overflow at data.");
  650: 		return ISC_R_PROTOCOLERROR;
  651: 	}
  652: 
  653: 	/* If it's an unknown code, skip over it. */
  654: 	if ((option_code > FTO_MAX) ||
  655: 	    (ft_options[option_code].type == FT_UNDEF)) {
  656: #if defined (DEBUG_FAILOVER_MESSAGES)
  657: 		log_debug ("  option code %d (%s) len %d (not recognized)",
  658: 			   option_code,
  659: 			   dhcp_failover_option_name (option_code),
  660: 			   option_len);
  661: #endif
  662: 		omapi_connection_copyout ((unsigned char *)0, c, option_len);
  663: 		link -> imsg_count += option_len;
  664: 		return ISC_R_SUCCESS;
  665: 	}
  666: 
  667: 	/* If it's the digest, do it now. */
  668: 	if (ft_options [option_code].type == FT_DIGEST) {
  669: 		link -> imsg_count += option_len;
  670: 		if (link -> imsg_count != link -> imsg_len) {
  671: 			log_error ("FAILOVER: digest not at end of message");
  672: 			return ISC_R_PROTOCOLERROR;
  673: 		}
  674: #if defined (DEBUG_FAILOVER_MESSAGES)
  675: 		log_debug ("  option %s len %d",
  676: 			   ft_options [option_code].name, option_len);
  677: #endif
  678: 		/* For now, just dump it. */
  679: 		omapi_connection_copyout ((unsigned char *)0, c, option_len);
  680: 		return ISC_R_SUCCESS;
  681: 	}
  682: 	
  683: 	/* Only accept an option once. */
  684: 	if (link -> imsg -> options_present & ft_options [option_code].bit) {
  685: 		log_error ("FAILOVER: duplicate option %s",
  686: 			   ft_options [option_code].name);
  687: 		return ISC_R_PROTOCOLERROR;
  688: 	}
  689: 
  690: 	/* Make sure the option is appropriate for this type of message.
  691: 	   Really, any option is generally allowed for any message, and the
  692: 	   cases where this is not true are too complicated to represent in
  693: 	   this way - what this code is doing is to just avoid saving the
  694: 	   value of an option we don't have any way to use, which allows
  695: 	   us to make the failover_message structure smaller. */
  696: 	if (ft_options [option_code].bit &&
  697: 	    !(fto_allowed [link -> imsg -> type] &
  698: 	      ft_options [option_code].bit)) {
  699: 		omapi_connection_copyout ((unsigned char *)0, c, option_len);
  700: 		link -> imsg_count += option_len;
  701: 		return ISC_R_SUCCESS;
  702: 	}		
  703: 
  704: 	/* Figure out how many elements, how big they are, and where
  705: 	   to store them. */
  706: 	if (ft_options [option_code].num_present) {
  707: 		/* If this option takes a fixed number of elements,
  708: 		   we expect the space for them to be preallocated,
  709: 		   and we can just read the data in. */
  710: 
  711: 		op = ((unsigned char *)link -> imsg) +
  712: 			ft_options [option_code].offset;
  713: 		op_size = ft_sizes [ft_options [option_code].type];
  714: 		op_count = ft_options [option_code].num_present;
  715: 
  716: 		if (option_len != op_size * op_count) {
  717: 			log_error ("FAILOVER: option size (%d:%d), option %s",
  718: 				   option_len,
  719: 				   (ft_sizes [ft_options [option_code].type] *
  720: 				    ft_options [option_code].num_present),
  721: 				   ft_options [option_code].name);
  722: 			return ISC_R_PROTOCOLERROR;
  723: 		}
  724: 	} else {
  725: 		failover_option_t *fo;
  726: 
  727: 		/* FT_DDNS* are special - one or two bytes of status
  728: 		   followed by the client FQDN. */
  729: 		if (ft_options [option_code].type == FT_DDNS1 ||
  730: 		    ft_options [option_code].type == FT_DDNS1) {
  731: 			ddns_fqdn_t *ddns =
  732: 				((ddns_fqdn_t *)
  733: 				 (((char *)link -> imsg) +
  734: 				  ft_options [option_code].offset));
  735: 
  736: 			op_count = (ft_options [option_code].type == FT_DDNS1
  737: 				    ? 1 : 2);
  738: 
  739: 			omapi_connection_copyout (&ddns -> codes [0],
  740: 						  c, op_count);
  741: 			link -> imsg_count += op_count;
  742: 			if (op_count == 1)
  743: 				ddns -> codes [1] = 0;
  744: 			op_size = 1;
  745: 			op_count = option_len - op_count;
  746: 
  747: 			ddns -> length = op_count;
  748: 			ddns -> data = dmalloc (op_count, MDL);
  749: 			if (!ddns -> data) {
  750: 				log_error ("FAILOVER: no memory getting%s(%d)",
  751: 					   " DNS data ", op_count);
  752: 
  753: 				/* Actually, NO_MEMORY, but if we lose here
  754: 				   we have to drop the connection. */
  755: 				return ISC_R_PROTOCOLERROR;
  756: 			}
  757: 			omapi_connection_copyout (ddns -> data, c, op_count);
  758: 			goto out;
  759: 		}
  760: 
  761: 		/* A zero for num_present means that any number of
  762: 		   elements can appear, so we have to figure out how
  763: 		   many we got from the length of the option, and then
  764: 		   fill out a failover_option structure describing the
  765: 		   data. */
  766: 		op_size = ft_sizes [ft_options [option_code].type];
  767: 
  768: 		/* Make sure that option data length is a multiple of the
  769: 		   size of the data type being sent. */
  770: 		if (op_size > 1 && option_len % op_size) {
  771: 			log_error ("FAILOVER: option_len %d not %s%d",
  772: 				   option_len, "multiple of ", op_size);
  773: 			return ISC_R_PROTOCOLERROR;
  774: 		}
  775: 
  776: 		op_count = option_len / op_size;
  777: 		
  778: 		fo = ((failover_option_t *)
  779: 		      (((char *)link -> imsg) +
  780: 		       ft_options [option_code].offset));
  781: 
  782: 		fo -> count = op_count;
  783: 		fo -> data = dmalloc (option_len, MDL);
  784: 		if (!fo -> data) {
  785: 			log_error ("FAILOVER: no memory getting %s (%d)",
  786: 				   "option data", op_count);
  787: 
  788: 			return ISC_R_PROTOCOLERROR;
  789: 		}			
  790: 		op = fo -> data;
  791: 	}
  792: 
  793: 	/* For single-byte message values and multi-byte values that
  794:            don't need swapping, just read them in all at once. */
  795: 	if (op_size == 1 || ft_options [option_code].type == FT_IPADDR) {
  796: 		omapi_connection_copyout ((unsigned char *)op, c, option_len);
  797: 		link -> imsg_count += option_len;
  798: 
  799: 		/*
  800: 		 * As of 3.1.0, many option codes were changed to conform to
  801: 		 * draft revision 12 (which alphabetized, then renumbered all
  802: 		 * the option codes without preserving the version option code
  803: 		 * nor bumping its value).  As it turns out, the message codes
  804: 		 * for CONNECT and CONNECTACK turn out the same, so it tries
  805: 		 * its darndest to connect, and falls short (when TLS_REQUEST
  806: 		 * comes up size 2 rather than size 1 as draft revision 12 also
  807: 		 * mandates).
  808: 		 *
  809: 		 * The VENDOR_CLASS code in 3.0.x was 11, which is now the HBA
  810: 		 * code.  Both work out to be arbitrarily long text-or-byte
  811: 		 * strings, so they pass parsing.
  812: 		 *
  813: 		 * Note that it is possible (or intentional), if highly
  814: 		 * improbable, for the HBA bit array to exactly match
  815: 		 * isc-V3.0.x.  Warning here is not an issue; if it really is
  816: 		 * 3.0.x, there will be a protocol error later on.  If it isn't
  817: 		 * actually 3.0.x, then I guess the lucky user will have to
  818: 		 * live with a weird warning.
  819: 		 */
  820: 		if ((option_code == 11) && (option_len > 9) &&
  821: 		    (strncmp((const char *)op, "isc-V3.0.", 9) == 0)) {
  822: 		        log_error("WARNING: failover as of versions 3.1.0 and "
  823: 				  "on are not reverse compatible with "
  824: 				  "versions 3.0.x.");
  825: 		}
  826: 
  827: 		goto out;
  828: 	}
  829: 
  830: 	/* For values that require swapping, read them in one at a time
  831: 	   using routines that swap bytes. */
  832: 	for (i = 0; i < op_count; i++) {
  833: 		switch (ft_options [option_code].type) {
  834: 		      case FT_UINT32:
  835: 			omapi_connection_get_uint32 (c, (u_int32_t *)op);
  836: 			op += 4;
  837: 			link -> imsg_count += 4;
  838: 			break;
  839: 			
  840: 		      case FT_UINT16:
  841: 			omapi_connection_get_uint16 (c, (u_int16_t *)op);
  842: 			op += 2;
  843: 			link -> imsg_count += 2;
  844: 			break;
  845: 			
  846: 		      default:
  847: 			/* Everything else should have been handled
  848: 			   already. */
  849: 			log_error ("FAILOVER: option %s: bad type %d",
  850: 				   ft_options [option_code].name,
  851: 				   ft_options [option_code].type);
  852: 			return ISC_R_PROTOCOLERROR;
  853: 		}
  854: 	}
  855:       out:
  856: 	/* Remember that we got this option. */
  857: 	link -> imsg -> options_present |= ft_options [option_code].bit;
  858: 	return ISC_R_SUCCESS;
  859: }
  860: 
  861: isc_result_t dhcp_failover_link_set_value (omapi_object_t *h,
  862: 					   omapi_object_t *id,
  863: 					   omapi_data_string_t *name,
  864: 					   omapi_typed_data_t *value)
  865: {
  866: 	if (h -> type != omapi_type_protocol)
  867: 		return ISC_R_INVALIDARG;
  868: 
  869: 	/* Never valid to set these. */
  870: 	if (!omapi_ds_strcmp (name, "link-port") ||
  871: 	    !omapi_ds_strcmp (name, "link-name") ||
  872: 	    !omapi_ds_strcmp (name, "link-state"))
  873: 		return ISC_R_NOPERM;
  874: 
  875: 	if (h -> inner && h -> inner -> type -> set_value)
  876: 		return (*(h -> inner -> type -> set_value))
  877: 			(h -> inner, id, name, value);
  878: 	return ISC_R_NOTFOUND;
  879: }
  880: 
  881: isc_result_t dhcp_failover_link_get_value (omapi_object_t *h,
  882: 					   omapi_object_t *id,
  883: 					   omapi_data_string_t *name,
  884: 					   omapi_value_t **value)
  885: {
  886: 	dhcp_failover_link_t *link;
  887: 
  888: 	if (h -> type != omapi_type_protocol)
  889: 		return ISC_R_INVALIDARG;
  890: 	link = (dhcp_failover_link_t *)h;
  891: 	
  892: 	if (!omapi_ds_strcmp (name, "link-port")) {
  893: 		return omapi_make_int_value (value, name,
  894: 					     (int)link -> peer_port, MDL);
  895: 	} else if (!omapi_ds_strcmp (name, "link-state")) {
  896: 		if (link -> state < 0 ||
  897: 		    link -> state >= dhcp_flink_state_max)
  898: 			return omapi_make_string_value (value, name,
  899: 							"invalid link state",
  900: 							MDL);
  901: 		return omapi_make_string_value
  902: 			(value, name,
  903: 			 dhcp_flink_state_names [link -> state], MDL);
  904: 	}
  905: 
  906: 	if (h -> inner && h -> inner -> type -> get_value)
  907: 		return (*(h -> inner -> type -> get_value))
  908: 			(h -> inner, id, name, value);
  909: 	return ISC_R_NOTFOUND;
  910: }
  911: 
  912: isc_result_t dhcp_failover_link_destroy (omapi_object_t *h,
  913: 					 const char *file, int line)
  914: {
  915: 	dhcp_failover_link_t *link;
  916: 	if (h -> type != dhcp_type_failover_link)
  917: 		return ISC_R_INVALIDARG;
  918: 	link = (dhcp_failover_link_t *)h;
  919: 
  920: 	if (link -> peer_address)
  921: 		option_cache_dereference (&link -> peer_address, file, line);
  922: 	if (link -> imsg)
  923: 		failover_message_dereference (&link -> imsg, file, line);
  924: 	if (link -> state_object)
  925: 		dhcp_failover_state_dereference (&link -> state_object,
  926: 						 file, line);
  927: 	return ISC_R_SUCCESS;
  928: }
  929: 
  930: /* Write all the published values associated with the object through the
  931:    specified connection. */
  932: 
  933: isc_result_t dhcp_failover_link_stuff_values (omapi_object_t *c,
  934: 					      omapi_object_t *id,
  935: 					      omapi_object_t *l)
  936: {
  937: 	dhcp_failover_link_t *link;
  938: 	isc_result_t status;
  939: 
  940: 	if (l -> type != dhcp_type_failover_link)
  941: 		return ISC_R_INVALIDARG;
  942: 	link = (dhcp_failover_link_t *)l;
  943: 	
  944: 	status = omapi_connection_put_name (c, "link-port");
  945: 	if (status != ISC_R_SUCCESS)
  946: 		return status;
  947: 	status = omapi_connection_put_uint32 (c, sizeof (int));
  948: 	if (status != ISC_R_SUCCESS)
  949: 		return status;
  950: 	status = omapi_connection_put_uint32 (c, link -> peer_port);
  951: 	if (status != ISC_R_SUCCESS)
  952: 		return status;
  953: 	
  954: 	status = omapi_connection_put_name (c, "link-state");
  955: 	if (status != ISC_R_SUCCESS)
  956: 		return status;
  957: 	if (link -> state < 0 ||
  958: 	    link -> state >= dhcp_flink_state_max)
  959: 		status = omapi_connection_put_string (c, "invalid link state");
  960: 	else
  961: 		status = (omapi_connection_put_string
  962: 			  (c, dhcp_flink_state_names [link -> state]));
  963: 	if (status != ISC_R_SUCCESS)
  964: 		return status;
  965: 
  966: 	if (link -> inner && link -> inner -> type -> stuff_values)
  967: 		return (*(link -> inner -> type -> stuff_values)) (c, id,
  968: 								link -> inner);
  969: 	return ISC_R_SUCCESS;
  970: }
  971: 
  972: /* Set up a listener for the omapi protocol.    The handle stored points to
  973:    a listener object, not a protocol object. */
  974: 
  975: isc_result_t dhcp_failover_listen (omapi_object_t *h)
  976: {
  977: 	isc_result_t status;
  978: 	dhcp_failover_listener_t *obj, *l;
  979: 	omapi_value_t *value = (omapi_value_t *)0;
  980: 	omapi_addr_t local_addr;
  981: 	unsigned long port;
  982: 
  983: 	status = omapi_get_value_str (h, (omapi_object_t *)0,
  984: 				      "local-port", &value);
  985: 	if (status != ISC_R_SUCCESS)
  986: 		return status;
  987: 	if (!value -> value) {
  988: 		omapi_value_dereference (&value, MDL);
  989: 		return ISC_R_INVALIDARG;
  990: 	}
  991: 	
  992: 	status = omapi_get_int_value (&port, value -> value);
  993: 	omapi_value_dereference (&value, MDL);
  994: 	if (status != ISC_R_SUCCESS)
  995: 		return status;
  996: 	local_addr.port = port;
  997: 
  998: 	status = omapi_get_value_str (h, (omapi_object_t *)0,
  999: 				      "local-address", &value);
 1000: 	if (status != ISC_R_SUCCESS)
 1001: 		return status;
 1002: 	if (!value -> value) {
 1003: 	      nogood:
 1004: 		omapi_value_dereference (&value, MDL);
 1005: 		return ISC_R_INVALIDARG;
 1006: 	}
 1007: 	
 1008: 	if (value -> value -> type != omapi_datatype_data ||
 1009: 	    value -> value -> u.buffer.len != sizeof (struct in_addr))
 1010: 		goto nogood;
 1011: 
 1012: 	memcpy (local_addr.address, value -> value -> u.buffer.value,
 1013: 		value -> value -> u.buffer.len);
 1014: 	local_addr.addrlen = value -> value -> u.buffer.len;
 1015: 	local_addr.addrtype = AF_INET;
 1016: 
 1017: 	omapi_value_dereference (&value, MDL);
 1018: 
 1019: 	/* Are we already listening on this port and address? */
 1020: 	for (l = failover_listeners; l; l = l -> next) {
 1021: 		if (l -> address.port == local_addr.port &&
 1022: 		    l -> address.addrtype == local_addr.addrtype &&
 1023: 		    l -> address.addrlen == local_addr.addrlen &&
 1024: 		    !memcmp (l -> address.address, local_addr.address,
 1025: 			     local_addr.addrlen))
 1026: 			break;
 1027: 	}
 1028: 	/* Already listening. */
 1029: 	if (l)
 1030: 		return ISC_R_SUCCESS;
 1031: 
 1032: 	obj = (dhcp_failover_listener_t *)0;
 1033: 	status = dhcp_failover_listener_allocate (&obj, MDL);
 1034: 	if (status != ISC_R_SUCCESS)
 1035: 		return status;
 1036: 	obj -> address = local_addr;
 1037: 	
 1038: 	status = omapi_listen_addr ((omapi_object_t *)obj, &obj -> address, 1);
 1039: 	if (status != ISC_R_SUCCESS)
 1040: 		return status;
 1041: 
 1042: 	status = omapi_object_reference (&h -> outer,
 1043: 					 (omapi_object_t *)obj, MDL);
 1044: 	if (status != ISC_R_SUCCESS) {
 1045: 		dhcp_failover_listener_dereference (&obj, MDL);
 1046: 		return status;
 1047: 	}
 1048: 	status = omapi_object_reference (&obj -> inner, h, MDL);
 1049: 	if (status != ISC_R_SUCCESS) {
 1050: 		dhcp_failover_listener_dereference (&obj, MDL);
 1051: 		return status;
 1052: 	}
 1053: 
 1054: 	/* Put this listener on the list. */
 1055: 	if (failover_listeners) {
 1056: 		dhcp_failover_listener_reference (&obj -> next,
 1057: 						  failover_listeners, MDL);
 1058: 		dhcp_failover_listener_dereference (&failover_listeners, MDL);
 1059: 	}
 1060: 	dhcp_failover_listener_reference (&failover_listeners, obj, MDL);
 1061: 
 1062: 	return dhcp_failover_listener_dereference (&obj, MDL);
 1063: }
 1064: 
 1065: /* Signal handler for protocol listener - if we get a connect signal,
 1066:    create a new protocol connection, otherwise pass the signal down. */
 1067: 
 1068: isc_result_t dhcp_failover_listener_signal (omapi_object_t *o,
 1069: 					    const char *name, va_list ap)
 1070: {
 1071: 	isc_result_t status;
 1072: 	omapi_connection_object_t *c;
 1073: 	dhcp_failover_link_t *obj;
 1074: 	dhcp_failover_listener_t *p;
 1075: 	dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
 1076: 
 1077: 	if (!o || o -> type != dhcp_type_failover_listener)
 1078: 		return ISC_R_INVALIDARG;
 1079: 	p = (dhcp_failover_listener_t *)o;
 1080: 
 1081: 	/* Not a signal we recognize? */
 1082: 	if (strcmp (name, "connect")) {
 1083: 		if (p -> inner && p -> inner -> type -> signal_handler)
 1084: 			return (*(p -> inner -> type -> signal_handler))
 1085: 				(p -> inner, name, ap);
 1086: 		return ISC_R_NOTFOUND;
 1087: 	}
 1088: 
 1089: 	c = va_arg (ap, omapi_connection_object_t *);
 1090: 	if (!c || c -> type != omapi_type_connection)
 1091: 		return ISC_R_INVALIDARG;
 1092: 
 1093: 	/* See if we can find a failover_state object that
 1094: 	   matches this connection. */
 1095: 	for (s = failover_states; s; s = s -> next) {
 1096: 		if (dhcp_failover_state_match
 1097: 		    (s, (u_int8_t *)&c -> remote_addr.sin_addr,
 1098: 		    sizeof c -> remote_addr.sin_addr)) {
 1099: 			state = s;
 1100: 			break;
 1101: 		}
 1102: 	}		
 1103: 	if (!state) {
 1104: 		log_info ("failover: listener: no matching state");
 1105: 		omapi_disconnect ((omapi_object_t *)c, 1);
 1106: 		return(ISC_R_NOTFOUND);
 1107: 	}
 1108: 
 1109: 	obj = (dhcp_failover_link_t *)0;
 1110: 	status = dhcp_failover_link_allocate (&obj, MDL);
 1111: 	if (status != ISC_R_SUCCESS)
 1112: 		return status;
 1113: 	obj -> peer_port = ntohs (c -> remote_addr.sin_port);
 1114: 
 1115: 	status = omapi_object_reference (&obj -> outer,
 1116: 					 (omapi_object_t *)c, MDL);
 1117: 	if (status != ISC_R_SUCCESS) {
 1118: 	      lose:
 1119: 		dhcp_failover_link_dereference (&obj, MDL);
 1120: 		log_info ("failover: listener: picayune failure.");
 1121: 		omapi_disconnect ((omapi_object_t *)c, 1);
 1122: 		return status;
 1123: 	}
 1124: 
 1125: 	status = omapi_object_reference (&c -> inner,
 1126: 					 (omapi_object_t *)obj, MDL);
 1127: 	if (status != ISC_R_SUCCESS)
 1128: 		goto lose;
 1129: 
 1130: 	status = dhcp_failover_state_reference (&obj -> state_object,
 1131: 						state, MDL);
 1132: 	if (status != ISC_R_SUCCESS)
 1133: 		goto lose;
 1134: 
 1135: 	omapi_signal_in ((omapi_object_t *)obj, "connect");
 1136: 
 1137: 	return dhcp_failover_link_dereference (&obj, MDL);
 1138: }
 1139: 
 1140: isc_result_t dhcp_failover_listener_set_value (omapi_object_t *h,
 1141: 						omapi_object_t *id,
 1142: 						omapi_data_string_t *name,
 1143: 						omapi_typed_data_t *value)
 1144: {
 1145: 	if (h -> type != dhcp_type_failover_listener)
 1146: 		return ISC_R_INVALIDARG;
 1147: 	
 1148: 	if (h -> inner && h -> inner -> type -> set_value)
 1149: 		return (*(h -> inner -> type -> set_value))
 1150: 			(h -> inner, id, name, value);
 1151: 	return ISC_R_NOTFOUND;
 1152: }
 1153: 
 1154: isc_result_t dhcp_failover_listener_get_value (omapi_object_t *h,
 1155: 						omapi_object_t *id,
 1156: 						omapi_data_string_t *name,
 1157: 						omapi_value_t **value)
 1158: {
 1159: 	if (h -> type != dhcp_type_failover_listener)
 1160: 		return ISC_R_INVALIDARG;
 1161: 	
 1162: 	if (h -> inner && h -> inner -> type -> get_value)
 1163: 		return (*(h -> inner -> type -> get_value))
 1164: 			(h -> inner, id, name, value);
 1165: 	return ISC_R_NOTFOUND;
 1166: }
 1167: 
 1168: isc_result_t dhcp_failover_listener_destroy (omapi_object_t *h,
 1169: 					      const char *file, int line)
 1170: {
 1171: 	dhcp_failover_listener_t *l;
 1172: 
 1173: 	if (h -> type != dhcp_type_failover_listener)
 1174: 		return ISC_R_INVALIDARG;
 1175: 	l = (dhcp_failover_listener_t *)h;
 1176: 	if (l -> next)
 1177: 		dhcp_failover_listener_dereference (&l -> next, file, line);
 1178: 
 1179: 	return ISC_R_SUCCESS;
 1180: }
 1181: 
 1182: /* Write all the published values associated with the object through the
 1183:    specified connection. */
 1184: 
 1185: isc_result_t dhcp_failover_listener_stuff (omapi_object_t *c,
 1186: 					   omapi_object_t *id,
 1187: 					   omapi_object_t *p)
 1188: {
 1189: 	if (p -> type != dhcp_type_failover_listener)
 1190: 		return ISC_R_INVALIDARG;
 1191: 
 1192: 	if (p -> inner && p -> inner -> type -> stuff_values)
 1193: 		return (*(p -> inner -> type -> stuff_values)) (c, id,
 1194: 								p -> inner);
 1195: 	return ISC_R_SUCCESS;
 1196: }
 1197: 
 1198: /* Set up master state machine for the failover protocol. */
 1199: 
 1200: isc_result_t dhcp_failover_register (omapi_object_t *h)
 1201: {
 1202: 	isc_result_t status;
 1203: 	dhcp_failover_state_t *obj;
 1204: 	unsigned long port;
 1205: 	omapi_value_t *value = (omapi_value_t *)0;
 1206: 
 1207: 	status = omapi_get_value_str (h, (omapi_object_t *)0,
 1208: 				      "local-port", &value);
 1209: 	if (status != ISC_R_SUCCESS)
 1210: 		return status;
 1211: 	if (!value -> value) {
 1212: 		omapi_value_dereference (&value, MDL);
 1213: 		return ISC_R_INVALIDARG;
 1214: 	}
 1215: 	
 1216: 	status = omapi_get_int_value (&port, value -> value);
 1217: 	omapi_value_dereference (&value, MDL);
 1218: 	if (status != ISC_R_SUCCESS)
 1219: 		return status;
 1220: 
 1221: 	obj = (dhcp_failover_state_t *)0;
 1222: 	dhcp_failover_state_allocate (&obj, MDL);
 1223: 	obj -> me.port = port;
 1224: 	
 1225: 	status = omapi_listen ((omapi_object_t *)obj, port, 1);
 1226: 	if (status != ISC_R_SUCCESS) {
 1227: 		dhcp_failover_state_dereference (&obj, MDL);
 1228: 		return status;
 1229: 	}
 1230: 
 1231: 	status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj,
 1232: 					 MDL);
 1233: 	if (status != ISC_R_SUCCESS) {
 1234: 		dhcp_failover_state_dereference (&obj, MDL);
 1235: 		return status;
 1236: 	}
 1237: 	status = omapi_object_reference (&obj -> inner, h, MDL);
 1238: 	dhcp_failover_state_dereference (&obj, MDL);
 1239: 	return status;
 1240: }
 1241: 
 1242: /* Signal handler for protocol state machine. */
 1243: 
 1244: isc_result_t dhcp_failover_state_signal (omapi_object_t *o,
 1245: 					 const char *name, va_list ap)
 1246: {
 1247: 	isc_result_t status;
 1248: 	dhcp_failover_state_t *state;
 1249: 	dhcp_failover_link_t *link;
 1250: 	struct timeval tv;
 1251: 
 1252: 	if (!o || o -> type != dhcp_type_failover_state)
 1253: 		return ISC_R_INVALIDARG;
 1254: 	state = (dhcp_failover_state_t *)o;
 1255: 
 1256: 	/* Not a signal we recognize? */
 1257: 	if (strcmp (name, "disconnect") &&
 1258: 	    strcmp (name, "message")) {
 1259: 		if (state -> inner && state -> inner -> type -> signal_handler)
 1260: 			return (*(state -> inner -> type -> signal_handler))
 1261: 				(state -> inner, name, ap);
 1262: 		return ISC_R_NOTFOUND;
 1263: 	}
 1264: 
 1265: 	/* Handle connect signals by seeing what state we're in
 1266: 	   and potentially doing a state transition. */
 1267: 	if (!strcmp (name, "disconnect")) {
 1268: 		link = va_arg (ap, dhcp_failover_link_t *);
 1269: 
 1270: 		dhcp_failover_link_dereference (&state -> link_to_peer, MDL);
 1271: 		dhcp_failover_state_transition (state, "disconnect");
 1272: 		if (state -> i_am == primary) {
 1273: #if defined (DEBUG_FAILOVER_TIMING)
 1274: 			log_info ("add_timeout +90 %s",
 1275: 				  "dhcp_failover_reconnect");
 1276: #endif
 1277: 			tv . tv_sec = cur_time + 90;
 1278: 			tv . tv_usec = 0;
 1279: 			add_timeout (&tv, dhcp_failover_reconnect,
 1280: 				     state,
 1281: 				     (tvref_t)dhcp_failover_state_reference,
 1282: 				     (tvunref_t)
 1283: 				     dhcp_failover_state_dereference);
 1284: 		}
 1285: 	} else if (!strcmp (name, "message")) {
 1286: 		link = va_arg (ap, dhcp_failover_link_t *);
 1287: 
 1288: 		if (link -> imsg -> type == FTM_CONNECT) {
 1289: 			/* If we already have a link to the peer, it must be
 1290: 			   dead, so drop it.
 1291: 			   XXX Is this the right thing to do?
 1292: 			   XXX Probably not - what if both peers start at
 1293: 			   XXX the same time? */
 1294: 			if (state -> link_to_peer) {
 1295: 				dhcp_failover_send_connectack
 1296: 					((omapi_object_t *)link, state,
 1297: 					 FTR_DUP_CONNECTION,
 1298: 					 "already connected");
 1299: 				omapi_disconnect (link -> outer, 1);
 1300: 				return ISC_R_SUCCESS;
 1301: 			}
 1302: 			if (!(link -> imsg -> options_present & FTB_MCLT)) {
 1303: 				dhcp_failover_send_connectack
 1304: 					((omapi_object_t *)link, state,
 1305: 					 FTR_INVALID_MCLT,
 1306: 					 "no MCLT provided");
 1307: 				omapi_disconnect (link -> outer, 1);
 1308: 				return ISC_R_SUCCESS;
 1309: 			}				
 1310: 
 1311: 			dhcp_failover_link_reference (&state -> link_to_peer,
 1312: 						      link, MDL);
 1313: 			status = (dhcp_failover_send_connectack
 1314: 				  ((omapi_object_t *)link, state, 0, 0));
 1315: 			if (status != ISC_R_SUCCESS) {
 1316: 				dhcp_failover_link_dereference
 1317: 					(&state -> link_to_peer, MDL);
 1318: 				log_info ("dhcp_failover_send_connectack: %s",
 1319: 					  isc_result_totext (status));
 1320: 				omapi_disconnect (link -> outer, 1);
 1321: 				return ISC_R_SUCCESS;
 1322: 			}
 1323: 			if (link -> imsg -> options_present & FTB_MAX_UNACKED)
 1324: 				state -> partner.max_flying_updates =
 1325: 					link -> imsg -> max_unacked;
 1326: 			if (link -> imsg -> options_present & FTB_RECEIVE_TIMER)
 1327: 				state -> partner.max_response_delay =
 1328: 					link -> imsg -> receive_timer;
 1329: 			state -> mclt = link -> imsg -> mclt;
 1330: 			dhcp_failover_send_state (state);
 1331: 			cancel_timeout (dhcp_failover_link_startup_timeout,
 1332: 					link);
 1333: 		} else if (link -> imsg -> type == FTM_CONNECTACK) {
 1334: 		    const char *errmsg;
 1335: 		    char errbuf[1024];
 1336: 		    int reason;
 1337: 
 1338: 		    cancel_timeout (dhcp_failover_link_startup_timeout,
 1339: 				    link);
 1340: 
 1341: 		    if (!(link->imsg->options_present &
 1342: 			  FTB_RELATIONSHIP_NAME)) {
 1343: 			errmsg = "missing relationship-name";
 1344: 			reason = FTR_INVALID_PARTNER;
 1345: 			goto badconnectack;
 1346: 		    }
 1347: 
 1348: 		    if (link->imsg->options_present & FTB_REJECT_REASON) {
 1349: 			/* XXX: add message option to text output. */
 1350: 			log_error ("Failover CONNECT to %s rejected: %s",
 1351: 				   state ? state->name : "unknown",
 1352: 				   (dhcp_failover_reject_reason_print
 1353: 				    (link -> imsg -> reject_reason)));
 1354: 			/* XXX print message from peer if peer sent message. */
 1355: 			omapi_disconnect (link -> outer, 1);
 1356: 			return ISC_R_SUCCESS;
 1357: 		    }
 1358: 
 1359: 		    if (!dhcp_failover_state_match_by_name(state,
 1360: 			&link->imsg->relationship_name)) {
 1361: 			/* XXX: Overflow results in log truncation, safe. */
 1362: 			snprintf(errbuf, sizeof(errbuf), "remote failover "
 1363: 				 "relationship name %.*s does not match",
 1364: 				 (int)link->imsg->relationship_name.count,
 1365: 				 link->imsg->relationship_name.data);
 1366: 			errmsg = errbuf;
 1367: 			reason = FTR_INVALID_PARTNER;
 1368: 		      badconnectack:
 1369: 			log_error("Failover CONNECTACK from %s: %s",
 1370: 				  state->name, errmsg);
 1371: 			dhcp_failover_send_disconnect ((omapi_object_t *)link,
 1372: 						       reason, errmsg);
 1373: 			omapi_disconnect (link -> outer, 0);
 1374: 			return ISC_R_SUCCESS;
 1375: 		    }
 1376: 
 1377: 		    if (state -> link_to_peer) {
 1378: 			errmsg = "already connected";
 1379: 			reason = FTR_DUP_CONNECTION;
 1380: 			goto badconnectack;
 1381: 		    }
 1382: 
 1383: 		    if ((cur_time > link -> imsg -> time &&
 1384: 			 cur_time - link -> imsg -> time > 60) ||
 1385: 			(cur_time < link -> imsg -> time &&
 1386: 			 link -> imsg -> time - cur_time > 60)) {
 1387: 			    errmsg = "time offset too large";
 1388: 			    reason = FTR_TIMEMISMATCH;
 1389: 			    goto badconnectack;
 1390: 		    }
 1391: 
 1392: 		    dhcp_failover_link_reference (&state -> link_to_peer,
 1393: 						  link, MDL);
 1394: #if 0
 1395: 		    /* XXX This is probably the right thing to do, but
 1396: 		       XXX for release three, to make the smallest possible
 1397: 		       XXX change, we are doing this when the peer state
 1398: 		       XXX changes instead. */
 1399: 		    if (state -> me.state == startup)
 1400: 			    dhcp_failover_set_state (state,
 1401: 						     state -> saved_state);
 1402: 		    else
 1403: #endif
 1404: 			    dhcp_failover_send_state (state);
 1405: 
 1406: 		    if (link -> imsg -> options_present & FTB_MAX_UNACKED)
 1407: 			    state -> partner.max_flying_updates =
 1408: 				    link -> imsg -> max_unacked;
 1409: 		    if (link -> imsg -> options_present & FTB_RECEIVE_TIMER)
 1410: 			    state -> partner.max_response_delay =
 1411: 				    link -> imsg -> receive_timer;
 1412: #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
 1413: 		    log_info ("add_timeout +%d %s",
 1414: 			      (int)state -> partner.max_response_delay / 3,
 1415: 			      "dhcp_failover_send_contact");
 1416: #endif
 1417: 		    tv . tv_sec = cur_time +
 1418: 			    (int)state -> partner.max_response_delay / 3;
 1419: 		    tv . tv_usec = 0;
 1420: 		    add_timeout (&tv,
 1421: 				 dhcp_failover_send_contact, state,
 1422: 				 (tvref_t)dhcp_failover_state_reference,
 1423: 				 (tvunref_t)dhcp_failover_state_dereference);
 1424: #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
 1425: 		    log_info ("add_timeout +%d %s",
 1426: 			      (int)state -> me.max_response_delay,
 1427: 			      "dhcp_failover_timeout");
 1428: #endif
 1429: 		    tv . tv_sec = cur_time +
 1430: 			    (int)state -> me.max_response_delay;
 1431: 		    tv . tv_usec = 0;
 1432: 		    add_timeout (&tv,
 1433: 				 dhcp_failover_timeout, state,
 1434: 				 (tvref_t)dhcp_failover_state_reference,
 1435: 				 (tvunref_t)dhcp_failover_state_dereference);
 1436: 		} else if (link -> imsg -> type == FTM_DISCONNECT) {
 1437: 		    if (link -> imsg -> reject_reason) {
 1438: 			log_error ("Failover DISCONNECT from %s: %s",
 1439: 				   state ? state->name : "unknown",
 1440: 				   (dhcp_failover_reject_reason_print
 1441: 				    (link -> imsg -> reject_reason)));
 1442: 		    }
 1443: 		    omapi_disconnect (link -> outer, 1);
 1444: 		} else if (link -> imsg -> type == FTM_BNDUPD) {
 1445: 			dhcp_failover_process_bind_update (state,
 1446: 							   link -> imsg);
 1447: 		} else if (link -> imsg -> type == FTM_BNDACK) {
 1448: 			dhcp_failover_process_bind_ack (state, link -> imsg);
 1449: 		} else if (link -> imsg -> type == FTM_UPDREQ) {
 1450: 			dhcp_failover_process_update_request (state,
 1451: 							      link -> imsg);
 1452: 		} else if (link -> imsg -> type == FTM_UPDREQALL) {
 1453: 			dhcp_failover_process_update_request_all
 1454: 				(state, link -> imsg);
 1455: 		} else if (link -> imsg -> type == FTM_UPDDONE) {
 1456: 			dhcp_failover_process_update_done (state,
 1457: 							   link -> imsg);
 1458: 		} else if (link -> imsg -> type == FTM_POOLREQ) {
 1459: 			dhcp_failover_pool_reqbalance(state);
 1460: 		} else if (link -> imsg -> type == FTM_POOLRESP) {
 1461: 			log_info ("pool response: %ld leases",
 1462: 				  (unsigned long)
 1463: 				  link -> imsg -> addresses_transferred);
 1464: 		} else if (link -> imsg -> type == FTM_STATE) {
 1465: 			dhcp_failover_peer_state_changed (state,
 1466: 							  link -> imsg);
 1467: 		}
 1468: 
 1469: 		/* Add a timeout so that if the partner doesn't send
 1470: 		   another message for the maximum transmit idle time
 1471: 		   plus a grace of one second, we close the
 1472: 		   connection. */
 1473: 		if (state -> link_to_peer &&
 1474: 		    state -> link_to_peer == link &&
 1475: 		    state -> link_to_peer -> state != dhcp_flink_disconnected)
 1476: 		{
 1477: #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
 1478: 		    log_info ("add_timeout +%d %s",
 1479: 			      (int)state -> me.max_response_delay,
 1480: 			      "dhcp_failover_timeout");
 1481: #endif
 1482: 		    tv . tv_sec = cur_time +
 1483: 			    (int)state -> me.max_response_delay;
 1484: 		    tv . tv_usec = 0;
 1485: 		    add_timeout (&tv,
 1486: 				 dhcp_failover_timeout, state,
 1487: 				 (tvref_t)dhcp_failover_state_reference,
 1488: 				 (tvunref_t)dhcp_failover_state_dereference);
 1489: 
 1490: 		}
 1491: 	}
 1492: 
 1493: 	/* Handle all the events we care about... */
 1494: 	return ISC_R_SUCCESS;
 1495: }
 1496: 
 1497: isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *state,
 1498: 					     const char *name)
 1499: {
 1500: 	isc_result_t status;
 1501: 
 1502: 	/* XXX Check these state transitions against the spec! */
 1503: 	if (!strcmp (name, "disconnect")) {
 1504: 		if (state -> link_to_peer) {
 1505: 		    log_info ("peer %s: disconnected", state -> name);
 1506: 		    if (state -> link_to_peer -> state_object)
 1507: 			dhcp_failover_state_dereference
 1508: 				(&state -> link_to_peer -> state_object, MDL);
 1509: 		    dhcp_failover_link_dereference (&state -> link_to_peer,
 1510: 						    MDL);
 1511: 		}
 1512: 		cancel_timeout (dhcp_failover_send_contact, state);
 1513: 		cancel_timeout (dhcp_failover_timeout, state);
 1514: 		cancel_timeout (dhcp_failover_startup_timeout, state);
 1515: 
 1516: 		switch (state -> me.state == startup ?
 1517: 			state -> saved_state : state -> me.state) {
 1518: 		      /* In these situations, we remain in the current
 1519: 		       * state, or if in startup enter those states.
 1520: 		       */
 1521: 		      case communications_interrupted:
 1522: 		      case conflict_done:
 1523: 		      case partner_down:
 1524: 		      case paused:
 1525: 		      case recover:
 1526: 		      case recover_done:
 1527: 		      case recover_wait:
 1528: 		      case resolution_interrupted:
 1529: 		      case shut_down:
 1530: 			/* Already in the right state? */
 1531: 			if (state -> me.state == startup)
 1532: 				return (dhcp_failover_set_state
 1533: 					(state, state -> saved_state));
 1534: 			return ISC_R_SUCCESS;
 1535: 		
 1536: 		      case potential_conflict:
 1537: 			return dhcp_failover_set_state
 1538: 				(state, resolution_interrupted);
 1539: 				
 1540: 		      case normal:
 1541: 			return dhcp_failover_set_state
 1542: 				(state, communications_interrupted);
 1543: 
 1544: 		      case unknown_state:
 1545: 			return dhcp_failover_set_state
 1546: 				(state, resolution_interrupted);
 1547: 
 1548: 		      default:
 1549: 			log_fatal("Impossible case at %s:%d.", MDL);
 1550: 			break;	/* can't happen. */
 1551: 		}
 1552: 	} else if (!strcmp (name, "connect")) {
 1553: 		switch (state -> me.state) {
 1554: 		      case communications_interrupted:
 1555: 			status = dhcp_failover_set_state (state, normal);
 1556: 			dhcp_failover_send_updates (state);
 1557: 			return status;
 1558: 
 1559: 		      case resolution_interrupted:
 1560: 			return dhcp_failover_set_state (state,
 1561: 							potential_conflict);
 1562: 
 1563: 		      case conflict_done:
 1564: 		      case partner_down:
 1565: 		      case potential_conflict:
 1566: 		      case normal:
 1567: 		      case recover:
 1568: 		      case shut_down:
 1569: 		      case paused:
 1570: 		      case unknown_state:
 1571: 		      case recover_done:
 1572: 		      case startup:
 1573: 		      case recover_wait:
 1574: 			return dhcp_failover_send_state (state);
 1575: 
 1576: 		      default:
 1577: 			log_fatal("Impossible case at %s:%d.", MDL);
 1578: 			break;
 1579: 		}
 1580: 	} else if (!strcmp (name, "startup")) {
 1581: 		dhcp_failover_set_state (state, startup);
 1582: 		return ISC_R_SUCCESS;
 1583: 	} else if (!strcmp (name, "connect-timeout")) {
 1584: 		switch (state -> me.state) {
 1585: 		      case communications_interrupted:
 1586: 		      case partner_down:
 1587: 		      case resolution_interrupted:
 1588: 		      case paused:
 1589: 		      case startup:
 1590: 		      case shut_down:
 1591: 		      case conflict_done:
 1592: 			return ISC_R_SUCCESS;
 1593: 
 1594: 		      case normal:
 1595: 		      case recover:
 1596: 		      case recover_wait:
 1597: 		      case recover_done:
 1598: 		      case unknown_state:
 1599: 			return dhcp_failover_set_state
 1600: 				(state, communications_interrupted);
 1601: 
 1602: 		      case potential_conflict:
 1603: 			return dhcp_failover_set_state
 1604: 				(state, resolution_interrupted);
 1605: 
 1606: 		      default:
 1607: 			log_fatal("Impossible case at %s:%d.", MDL);
 1608: 			break;
 1609: 		}
 1610: 	}
 1611: 	return ISC_R_INVALIDARG;
 1612: }
 1613: 
 1614: isc_result_t dhcp_failover_set_service_state (dhcp_failover_state_t *state)
 1615: {
 1616: 	switch (state -> me.state) {
 1617: 	      case unknown_state:
 1618: 		state -> service_state = not_responding;
 1619: 		state -> nrr = " (my state unknown)";
 1620: 		break;
 1621: 
 1622: 	      case partner_down:
 1623: 		state -> service_state = service_partner_down;
 1624: 		state -> nrr = "";
 1625: 		break;
 1626: 
 1627: 	      case normal:
 1628: 		state -> service_state = cooperating;
 1629: 		state -> nrr = "";
 1630: 		break;
 1631: 
 1632: 	      case communications_interrupted:
 1633: 		state -> service_state = not_cooperating;
 1634: 		state -> nrr = "";
 1635: 		break;
 1636: 
 1637: 	      case resolution_interrupted:
 1638: 	      case potential_conflict:
 1639: 	      case conflict_done:
 1640: 		state -> service_state = not_responding;
 1641: 		state -> nrr = " (resolving conflicts)";
 1642: 		break;
 1643: 
 1644: 	      case recover:
 1645: 		state -> service_state = not_responding;
 1646: 		state -> nrr = " (recovering)";
 1647: 		break;
 1648: 
 1649: 	      case shut_down:
 1650: 		state -> service_state = not_responding;
 1651: 		state -> nrr = " (shut down)";
 1652: 		break;
 1653: 
 1654: 	      case paused:
 1655: 		state -> service_state = not_responding;
 1656: 		state -> nrr = " (paused)";
 1657: 		break;
 1658: 
 1659: 	      case recover_wait:
 1660: 		state -> service_state = not_responding;
 1661: 		state -> nrr = " (recover wait)";
 1662: 		break;
 1663: 
 1664: 	      case recover_done:
 1665: 		state -> service_state = not_responding;
 1666: 		state -> nrr = " (recover done)";
 1667: 		break;
 1668: 
 1669: 	      case startup:
 1670: 		state -> service_state = service_startup;
 1671: 		state -> nrr = " (startup)";
 1672: 		break;
 1673: 
 1674: 	      default:
 1675: 		log_fatal("Impossible case at %s:%d.\n", MDL);
 1676: 		break;
 1677: 	}
 1678: 
 1679: 	/* Some peer states can require us not to respond, even if our
 1680: 	   state doesn't. */
 1681: 	/* XXX hm.   I suspect this isn't true anymore. */
 1682: 	if (state -> service_state != not_responding) {
 1683: 		switch (state -> partner.state) {
 1684: 		      case partner_down:
 1685: 			state -> service_state = not_responding;
 1686: 			state -> nrr = " (peer demands: recovering)";
 1687: 			break;
 1688: 
 1689: 		      case potential_conflict:
 1690: 		      case conflict_done:
 1691: 		      case resolution_interrupted:
 1692: 			state -> service_state = not_responding;
 1693: 			state -> nrr = " (peer demands: resolving conflicts)";
 1694: 			break;
 1695: 
 1696: 			/* Other peer states don't affect our behaviour. */
 1697: 		      default:
 1698: 			break;
 1699: 		}
 1700: 	}
 1701: 
 1702: 	return ISC_R_SUCCESS;
 1703: }
 1704: 
 1705: isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *state,
 1706: 				      enum failover_state new_state)
 1707: {
 1708:     enum failover_state saved_state;
 1709:     TIME saved_stos;
 1710:     struct pool *p;
 1711:     struct shared_network *s;
 1712:     struct lease *l;
 1713:     struct timeval tv;
 1714: 
 1715:     /* If we're in certain states where we're sending updates, and the peer
 1716:      * state changes, we need to re-schedule any pending updates just to
 1717:      * be on the safe side.  This results in retransmission.
 1718:      */
 1719:     switch (state -> me.state) {
 1720:       case normal:
 1721:       case potential_conflict:
 1722:       case partner_down:
 1723: 	if (state -> ack_queue_tail) {
 1724: 	    struct lease *lp;
 1725: 		
 1726: 	    /* Zap the flags. */
 1727: 	    for (lp = state -> ack_queue_head; lp; lp = lp -> next_pending)
 1728: 		    lp -> flags = ((lp -> flags & ~ON_ACK_QUEUE) |
 1729: 				   ON_UPDATE_QUEUE);
 1730: 
 1731: 	    /* Now hook the ack queue to the beginning of the update
 1732: 	       queue. */
 1733: 	    if (state -> update_queue_head) {
 1734: 		lease_reference (&state -> ack_queue_tail -> next_pending,
 1735: 				 state -> update_queue_head, MDL);
 1736: 		lease_dereference (&state -> update_queue_head, MDL);
 1737: 	    }
 1738: 	    lease_reference (&state -> update_queue_head,
 1739: 			     state -> ack_queue_head, MDL);
 1740: 	    if (!state -> update_queue_tail) {
 1741: #if defined (POINTER_DEBUG)
 1742: 		if (state -> ack_queue_tail -> next_pending) {
 1743: 		    log_error ("next pending on ack queue tail.");
 1744: 		    abort ();
 1745: 		}
 1746: #endif
 1747: 		lease_reference (&state -> update_queue_tail,
 1748: 				 state -> ack_queue_tail, MDL);
 1749: 	    }
 1750: 	    lease_dereference (&state -> ack_queue_tail, MDL);
 1751: 	    lease_dereference (&state -> ack_queue_head, MDL);
 1752: 	    state -> cur_unacked_updates = 0;
 1753: 	}
 1754: 	/* We will re-queue a timeout later, if applicable. */
 1755: 	cancel_timeout (dhcp_failover_keepalive, state);
 1756: 	break;
 1757: 	
 1758:       default:
 1759: 	break;
 1760:     }
 1761: 
 1762:     /* Tentatively make the transition. */
 1763:     saved_state = state -> me.state;
 1764:     saved_stos = state -> me.stos;
 1765: 
 1766:     /* Keep the old stos if we're going into recover_wait or if we're
 1767:        coming into or out of startup. */
 1768:     if (new_state != recover_wait && new_state != startup &&
 1769: 	saved_state != startup)
 1770: 	    state -> me.stos = cur_time;
 1771: 
 1772:     /* If we're in shutdown, peer is in partner_down, and we're moving
 1773:        to recover, we can skip waiting for MCLT to expire.    This happens
 1774:        when a server is moved administratively into shutdown prior to
 1775:        actually shutting down.   Of course, if there are any updates
 1776:        pending we can't actually do this. */
 1777:     if (new_state == recover && saved_state == shut_down &&
 1778: 	state -> partner.state == partner_down &&
 1779: 	!state -> update_queue_head && !state -> ack_queue_head)
 1780: 	    state -> me.stos = cur_time - state -> mclt;
 1781: 
 1782:     state -> me.state = new_state;
 1783:     if (new_state == startup && saved_state != startup)
 1784: 	state -> saved_state = saved_state;
 1785: 
 1786:     /* If we can't record the new state, we can't make a state transition. */
 1787:     if (!write_failover_state (state) || !commit_leases ()) {
 1788: 	    log_error ("Unable to record current failover state for %s",
 1789: 		       state -> name);
 1790: 	    state -> me.state = saved_state;
 1791: 	    state -> me.stos = saved_stos;
 1792: 	    return ISC_R_IOERROR;
 1793:     }
 1794: 
 1795:     log_info ("failover peer %s: I move from %s to %s",
 1796: 	      state -> name, dhcp_failover_state_name_print (saved_state),
 1797: 	      dhcp_failover_state_name_print (state -> me.state));
 1798:     
 1799:     /* If we were in startup and we just left it, cancel the timeout. */
 1800:     if (new_state != startup && saved_state == startup)
 1801: 	cancel_timeout (dhcp_failover_startup_timeout, state);
 1802: 
 1803:     /* Set our service state. */
 1804:     dhcp_failover_set_service_state (state);
 1805: 
 1806:     /* Tell the peer about it. */
 1807:     if (state -> link_to_peer)
 1808: 	    dhcp_failover_send_state (state);
 1809: 
 1810:     switch (new_state) {
 1811: 	  case normal:
 1812: 	    /* Upon entering normal state, the server is expected to retransmit
 1813: 	     * all pending binding updates.  This is a good opportunity to
 1814: 	     * rebalance the pool (potentially making new pending updates),
 1815: 	     * which also schedules the next pool rebalance.
 1816: 	     */
 1817: 	    dhcp_failover_pool_balance(state);
 1818: 	    dhcp_failover_generate_update_queue(state, 0);
 1819: 
 1820: 	    if (state->update_queue_tail != NULL) {
 1821: 		dhcp_failover_send_updates(state);
 1822: 		log_info("Sending updates to %s.", state->name);
 1823: 	    }
 1824: 
 1825: 	    break;
 1826: 	    
 1827: 	  case potential_conflict:
 1828: 	    if (state -> i_am == primary)
 1829: 		    dhcp_failover_send_update_request (state);
 1830: 	    break;
 1831: 	    
 1832: 	  case startup:
 1833: #if defined (DEBUG_FAILOVER_TIMING)
 1834: 	    log_info ("add_timeout +15 %s",
 1835: 		      "dhcp_failover_startup_timeout");
 1836: #endif
 1837: 	    tv . tv_sec = cur_time + 15;
 1838: 	    tv . tv_usec = 0;
 1839: 	    add_timeout (&tv,
 1840: 			 dhcp_failover_startup_timeout,
 1841: 			 state,
 1842: 			 (tvref_t)omapi_object_reference,
 1843: 			 (tvunref_t)
 1844: 			 omapi_object_dereference);
 1845: 	    break;
 1846: 	    
 1847: 	    /* If we come back in recover_wait and there's still waiting
 1848: 	       to do, set a timeout. */
 1849: 	  case recover_wait:
 1850: 	    if (state -> me.stos + state -> mclt > cur_time) {
 1851: #if defined (DEBUG_FAILOVER_TIMING)
 1852: 		    log_info ("add_timeout +%d %s",
 1853: 			      (int)(cur_time -
 1854: 				    state -> me.stos + state -> mclt),
 1855: 			      "dhcp_failover_startup_timeout");
 1856: #endif
 1857: 		    tv . tv_sec = (int)(state -> me.stos + state -> mclt);
 1858: 		    tv . tv_usec = 0;
 1859: 		    add_timeout (&tv,
 1860: 				 dhcp_failover_recover_done,
 1861: 				 state,
 1862: 				 (tvref_t)omapi_object_reference,
 1863: 				 (tvunref_t)
 1864: 				 omapi_object_dereference);
 1865: 	    } else
 1866: 		    dhcp_failover_recover_done (state);
 1867: 	    break;
 1868: 	    
 1869: 	  case recover:
 1870: 	    /* XXX: We're supposed to calculate if updreq or updreqall is
 1871: 	     * needed.  In theory, we should only have to updreqall if we
 1872: 	     * are positive we lost our stable storage.
 1873: 	     */
 1874: 	    if (state -> link_to_peer)
 1875: 		    dhcp_failover_send_update_request_all (state);
 1876: 	    break;
 1877: 
 1878: 	  case partner_down:
 1879: 	    /* For every expired lease, set a timeout for it to become free. */
 1880: 	    for (s = shared_networks; s; s = s -> next) {
 1881: 		for (p = s -> pools; p; p = p -> next) {
 1882: 		    if (p -> failover_peer == state) {
 1883: 			for (l = p->expired ; l ; l = l->next) {
 1884: 			    l->tsfp = state->me.stos + state->mclt;
 1885: 			    l->sort_time = (l->tsfp > l->ends) ?
 1886: 					   l->tsfp : l->ends;
 1887: 			}
 1888: 			if (p->expired &&
 1889: 			    (p->expired->sort_time < p->next_event_time)) {
 1890: 
 1891: 			    p->next_event_time = p->expired->sort_time;
 1892: #if defined (DEBUG_FAILOVER_TIMING)
 1893: 			    log_info ("add_timeout +%d %s",
 1894: 				      (int)(cur_time - p->next_event_time),
 1895: 				      "pool_timer");
 1896: #endif
 1897: 			    tv.tv_sec = p->next_event_time;
 1898: 			    tv.tv_usec = 0;
 1899: 			    add_timeout(&tv, pool_timer, p,
 1900: 					(tvref_t)pool_reference,
 1901: 					(tvunref_t)pool_dereference);
 1902: 			}
 1903: 		    }
 1904: 		}
 1905: 	    }
 1906: 	    break;
 1907: 
 1908: 
 1909: 	  default:
 1910: 	    break;
 1911:     }
 1912: 
 1913:     return ISC_R_SUCCESS;
 1914: }
 1915: 
 1916: isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state,
 1917: 					       failover_message_t *msg)
 1918: {
 1919: 	enum failover_state previous_state = state -> partner.state;
 1920: 	enum failover_state new_state;
 1921: 	int startupp;
 1922: 
 1923: 	new_state = msg -> server_state;
 1924: 	startupp = (msg -> server_flags & FTF_SERVER_STARTUP) ? 1 : 0;
 1925: 
 1926: 	if (state -> partner.state == new_state && state -> me.state) {
 1927: 		switch (state -> me.state) {
 1928: 		      case startup:
 1929: 			dhcp_failover_set_state (state, state -> saved_state);
 1930: 			return ISC_R_SUCCESS;
 1931: 
 1932: 		      case unknown_state:
 1933: 		      case normal:
 1934: 		      case potential_conflict:
 1935: 		      case recover_done:
 1936: 		      case shut_down:
 1937: 		      case paused:
 1938: 		      case recover_wait:
 1939: 			return ISC_R_SUCCESS;
 1940: 
 1941: 			/* If we get a peer state change when we're
 1942: 			   disconnected, we always process it. */
 1943: 		      case partner_down:
 1944: 		      case communications_interrupted:
 1945: 		      case resolution_interrupted:
 1946: 		      case recover:
 1947: 		      case conflict_done:
 1948: 			break;
 1949: 
 1950: 		      default:
 1951: 			log_fatal("Impossible case at %s:%d.", MDL);
 1952: 			break;
 1953: 		}
 1954: 	}
 1955: 
 1956: 	state -> partner.state = new_state;
 1957: 
 1958: 	log_info ("failover peer %s: peer moves from %s to %s",
 1959: 		  state -> name,
 1960: 		  dhcp_failover_state_name_print (previous_state),
 1961: 		  dhcp_failover_state_name_print (state -> partner.state));
 1962:     
 1963: 	if (!write_failover_state (state) || !commit_leases ()) {
 1964: 		/* This is bad, but it's not fatal.  Of course, if we
 1965: 		   can't write to the lease database, we're not going to
 1966: 		   get much done anyway. */
 1967: 		log_error ("Unable to record current failover state for %s",
 1968: 			   state -> name);
 1969: 	}
 1970: 
 1971: 	/* Quickly validate the new state as being one of the 13 known
 1972: 	 * states.
 1973: 	 */
 1974: 	switch (new_state) {
 1975: 	      case unknown_state:
 1976: 	      case startup:
 1977: 	      case normal:
 1978: 	      case communications_interrupted:
 1979: 	      case partner_down:
 1980: 	      case potential_conflict:
 1981: 	      case recover:
 1982: 	      case paused:
 1983: 	      case shut_down:
 1984: 	      case recover_done:
 1985: 	      case resolution_interrupted:
 1986: 	      case conflict_done:
 1987: 	      case recover_wait:
 1988: 		break;
 1989: 
 1990: 	      default:
 1991: 		log_error("failover peer %s: Invalid state: %d", state->name,
 1992: 			  new_state);
 1993: 		dhcp_failover_set_state(state, shut_down);
 1994: 		return ISC_R_SUCCESS;
 1995: 	}
 1996: 
 1997: 	/* Do any state transitions that are required as a result of the
 1998: 	   peer's state transition. */
 1999: 
 2000: 	switch (state -> me.state == startup ?
 2001: 		state -> saved_state : state -> me.state) {
 2002: 	      case normal:
 2003: 		switch (new_state) {
 2004: 		      case normal:
 2005: 			dhcp_failover_state_pool_check (state);
 2006: 			break;
 2007: 
 2008: 		      case partner_down:
 2009: 			if (state -> me.state == startup)
 2010: 				dhcp_failover_set_state (state, recover);
 2011: 			else
 2012: 				dhcp_failover_set_state (state,
 2013: 							 potential_conflict);
 2014: 			break;
 2015: 
 2016: 		      case potential_conflict:
 2017: 		      case resolution_interrupted:
 2018: 		      case conflict_done:
 2019: 			/* None of these transitions should ever occur. */
 2020: 			log_error("Peer %s: Invalid state transition %s "
 2021: 				"to %s.", state->name,
 2022: 				dhcp_failover_state_name_print(previous_state),
 2023: 				dhcp_failover_state_name_print(new_state));
 2024: 			dhcp_failover_set_state (state, shut_down);
 2025: 			break;
 2026: 
 2027: 		      case recover:
 2028: 		      case shut_down:
 2029: 			dhcp_failover_set_state (state, partner_down);
 2030: 			break;
 2031: 
 2032: 		      case paused:
 2033: 			dhcp_failover_set_state (state,
 2034: 						 communications_interrupted);
 2035: 			break;
 2036: 
 2037: 		      default:
 2038: 			/* recover_wait, recover_done, unknown_state, startup,
 2039: 			 * communications_interrupted
 2040: 			 */
 2041: 			break;
 2042: 		}
 2043: 		break;
 2044: 
 2045: 	      case recover:
 2046: 		switch (new_state) {
 2047: 		      case recover:
 2048: 			log_info ("failover peer %s: requesting %s",
 2049: 				  state -> name, "full update from peer");
 2050: 			/* Don't send updreqall if we're really in the
 2051: 			   startup state, because that will result in two
 2052: 			   being sent. */
 2053: 			if (state -> me.state == recover)
 2054: 				dhcp_failover_send_update_request_all (state);
 2055: 			break;
 2056: 
 2057: 		      case potential_conflict:
 2058: 		      case resolution_interrupted:
 2059: 		      case conflict_done:
 2060: 		      case normal:
 2061: 			dhcp_failover_set_state (state, potential_conflict);
 2062: 			break;
 2063: 
 2064: 		      case partner_down:
 2065: 		      case communications_interrupted:
 2066: 			/* We're supposed to send an update request at this
 2067: 			   point. */
 2068: 			/* XXX we don't currently have code here to do any
 2069: 			   XXX clever detection of when we should send an
 2070: 			   XXX UPDREQALL message rather than an UPDREQ
 2071: 			   XXX message.   What to do, what to do? */
 2072: 			/* Currently when we enter recover state, no matter
 2073: 			 * the reason, we send an UPDREQALL.  So, it makes
 2074: 			 * the most sense to stick to that until something
 2075: 			 * better is done.
 2076: 			 * Furthermore, we only want to send the update
 2077: 			 * request if we are not in startup state.
 2078: 			 */
 2079: 			if (state -> me.state == recover)
 2080: 				dhcp_failover_send_update_request_all (state);
 2081: 			break;
 2082: 
 2083: 		      case shut_down:
 2084: 			/* XXX We're not explicitly told what to do in this
 2085: 			   XXX case, but this transition is consistent with
 2086: 			   XXX what is elsewhere in the draft. */
 2087: 			dhcp_failover_set_state (state, partner_down);
 2088: 			break;
 2089: 
 2090: 			/* We can't really do anything in this case. */
 2091: 		      default:
 2092: 			/* paused, recover_done, recover_wait, unknown_state,
 2093: 			 * startup.
 2094: 			 */
 2095: 			break;
 2096: 		}
 2097: 		break;
 2098: 
 2099: 	      case potential_conflict:
 2100: 		switch (new_state) {
 2101: 		      case normal:
 2102: 			/* This is an illegal transition. */
 2103: 			log_error("Peer %s moves to normal during conflict "
 2104: 				  "resolution - panic, shutting down.",
 2105: 				  state->name);
 2106: 			dhcp_failover_set_state(state, shut_down);
 2107: 			break;
 2108: 
 2109: 		      case conflict_done:
 2110: 			if (previous_state == potential_conflict)
 2111: 				dhcp_failover_send_update_request (state);
 2112: 			else
 2113: 				log_error("Peer %s: Unexpected move to "
 2114: 					  "conflict-done.", state->name);
 2115: 			break;
 2116: 
 2117: 		      case recover_done:
 2118: 		      case recover_wait:
 2119: 		      case potential_conflict:
 2120: 		      case partner_down:
 2121: 		      case communications_interrupted:
 2122: 		      case resolution_interrupted:
 2123: 		      case paused:
 2124: 			break;
 2125: 
 2126: 		      case recover:
 2127: 			dhcp_failover_set_state (state, recover);
 2128: 			break;
 2129: 
 2130: 		      case shut_down:
 2131: 			dhcp_failover_set_state (state, partner_down);
 2132: 			break;
 2133: 
 2134: 		      default:
 2135: 			/* unknown_state, startup */
 2136: 			break;
 2137: 		}
 2138: 		break;
 2139: 
 2140: 	      case conflict_done:
 2141: 		switch (new_state) {
 2142: 		      case normal:
 2143: 		      case shut_down:
 2144: 			dhcp_failover_set_state(state, new_state);
 2145: 			break;
 2146: 
 2147: 		      default:
 2148: 			log_fatal("Peer %s: Invalid attempt to move from %s "
 2149: 				"to %s while local state is conflict-done.",
 2150: 				state->name,
 2151: 				dhcp_failover_state_name_print(previous_state),
 2152: 				dhcp_failover_state_name_print(new_state));
 2153: 		}
 2154: 		break;
 2155: 
 2156: 	      case partner_down:
 2157: 		/* Take no action if other server is starting up. */
 2158: 		if (startupp)
 2159: 			break;
 2160: 
 2161: 		switch (new_state) {
 2162: 			/* This is where we should be. */
 2163: 		      case recover:
 2164: 		      case recover_wait:
 2165: 			break;
 2166: 
 2167: 		      case recover_done:
 2168: 			dhcp_failover_set_state (state, normal);
 2169: 			break;
 2170: 
 2171: 		      case normal:
 2172: 		      case potential_conflict:
 2173: 		      case partner_down:
 2174: 		      case communications_interrupted:
 2175: 		      case resolution_interrupted:
 2176: 		      case conflict_done:
 2177: 			dhcp_failover_set_state (state, potential_conflict);
 2178: 			break;
 2179: 
 2180: 		      default:
 2181: 			/* shut_down, paused, unknown_state, startup */
 2182: 			break;
 2183: 		}
 2184: 		break;
 2185: 
 2186: 	      case communications_interrupted:
 2187: 		switch (new_state) {
 2188: 		      case paused:
 2189: 			/* Stick with the status quo. */
 2190: 			break;
 2191: 
 2192: 			/* If we're in communications-interrupted and an
 2193: 			   amnesic peer connects, go to the partner_down
 2194: 			   state immediately. */
 2195: 		      case recover:
 2196: 			dhcp_failover_set_state (state, partner_down);
 2197: 			break;
 2198: 
 2199: 		      case normal:
 2200: 		      case communications_interrupted:
 2201: 		      case recover_done:
 2202: 		      case recover_wait:
 2203: 			/* XXX so we don't need to do this specially in
 2204: 			   XXX the CONNECT and CONNECTACK handlers. */
 2205: 			dhcp_failover_send_updates (state);
 2206: 			dhcp_failover_set_state (state, normal);
 2207: 			break;
 2208: 
 2209: 		      case potential_conflict:
 2210: 		      case partner_down:
 2211: 		      case resolution_interrupted:
 2212: 		      case conflict_done:
 2213: 			dhcp_failover_set_state (state, potential_conflict);
 2214: 			break;
 2215: 
 2216: 		      case shut_down:
 2217: 			dhcp_failover_set_state (state, partner_down);
 2218: 			break;
 2219: 
 2220: 		      default:
 2221: 			/* unknown_state, startup */
 2222: 			break;
 2223: 		}
 2224: 		break;
 2225: 
 2226: 	      case resolution_interrupted:
 2227: 		switch (new_state) {
 2228: 		      case normal:
 2229: 		      case recover:
 2230: 		      case potential_conflict:
 2231: 		      case partner_down:
 2232: 		      case communications_interrupted:
 2233: 		      case resolution_interrupted:
 2234: 		      case conflict_done:
 2235: 		      case recover_done:
 2236: 		      case recover_wait:
 2237: 			dhcp_failover_set_state (state, potential_conflict);
 2238: 			break;
 2239: 
 2240: 		      case shut_down:
 2241: 			dhcp_failover_set_state (state, partner_down);
 2242: 			break;
 2243: 
 2244: 		      default:
 2245: 			/* paused, unknown_state, startup */
 2246: 			break;
 2247: 		}
 2248: 		break;
 2249: 
 2250: 	      /* Make no transitions while in recover_wait...just wait. */
 2251: 	      case recover_wait:
 2252: 		break;
 2253: 
 2254: 	      case recover_done:
 2255: 		switch (new_state) {
 2256: 		      case recover_done:
 2257: 			log_error("Both servers have entered recover-done!");
 2258: 		      case normal:
 2259: 			dhcp_failover_set_state (state, normal);
 2260: 			break;
 2261: 
 2262: 		      case shut_down:
 2263: 			dhcp_failover_set_state (state, partner_down);
 2264: 			break;
 2265: 
 2266: 		      default:
 2267: 			/* potential_conflict, partner_down,
 2268: 			 * communications_interrupted, resolution_interrupted,
 2269: 			 * paused, recover, recover_wait, unknown_state,
 2270: 			 * startup.
 2271: 			 */
 2272: 			break;
 2273: 		}
 2274: 		break;
 2275: 
 2276: 		/* We are essentially dead in the water when we're in
 2277: 		   either shut_down or paused states, and do not do any
 2278: 		   automatic state transitions. */
 2279: 	      case shut_down:
 2280: 	      case paused:
 2281: 		break;
 2282: 
 2283: 	      /* XXX: Shouldn't this be a fatal condition? */
 2284: 	      case unknown_state:
 2285: 		break;
 2286: 
 2287: 	      default:
 2288: 		log_fatal("Impossible condition at %s:%d.", MDL);
 2289: 		break;
 2290: 
 2291: 	}
 2292: 
 2293: 	/* If we didn't make a transition out of startup as a result of
 2294: 	   the peer's state change, do it now as a result of the fact that
 2295: 	   we got a state change from the peer. */
 2296: 	if (state -> me.state == startup && state -> saved_state != startup)
 2297: 		dhcp_failover_set_state (state, state -> saved_state);
 2298: 	
 2299: 	/* For now, just set the service state based on the peer's state
 2300: 	   if necessary. */
 2301: 	dhcp_failover_set_service_state (state);
 2302: 
 2303: 	return ISC_R_SUCCESS;
 2304: }
 2305: 
 2306: /*
 2307:  * Balance operation manual entry; startup, entrance to normal state.  No
 2308:  * sense sending a POOLREQ at this stage; the peer is likely about to schedule
 2309:  * their own rebalance event upon entering normal themselves.
 2310:  */
 2311: static void
 2312: dhcp_failover_pool_balance(dhcp_failover_state_t *state)
 2313: {
 2314: 	/* Cancel pending event. */
 2315: 	cancel_timeout(dhcp_failover_pool_rebalance, state);
 2316: 	state->sched_balance = 0;
 2317: 
 2318: 	dhcp_failover_pool_dobalance(state, NULL);
 2319: }
 2320: 
 2321: /*
 2322:  * Balance operation entry from timer event.  Once per timer interval is
 2323:  * the only time we want to emit POOLREQs (asserting an interrupt in our
 2324:  * peer).
 2325:  */
 2326: void
 2327: dhcp_failover_pool_rebalance(void *failover_state)
 2328: {
 2329: 	dhcp_failover_state_t *state;
 2330: 	isc_boolean_t sendreq = ISC_FALSE;
 2331: 
 2332: 	state = (dhcp_failover_state_t *)failover_state;
 2333: 
 2334: 	/* Clear scheduled event indicator. */
 2335: 	state->sched_balance = 0;
 2336: 
 2337: 	if (dhcp_failover_pool_dobalance(state, &sendreq))
 2338: 		dhcp_failover_send_updates(state);
 2339: 
 2340: 	if (sendreq)
 2341: 		dhcp_failover_send_poolreq(state);
 2342: }
 2343: 
 2344: /*
 2345:  * Balance operation entry from POOLREQ protocol message.  Do not permit a
 2346:  * POOLREQ to send back a POOLREQ.  Ping pong.
 2347:  */
 2348: static void
 2349: dhcp_failover_pool_reqbalance(dhcp_failover_state_t *state)
 2350: {
 2351: 	int queued;
 2352: 
 2353: 	/* Cancel pending event. */
 2354: 	cancel_timeout(dhcp_failover_pool_rebalance, state);
 2355: 	state->sched_balance = 0;
 2356: 
 2357: 	queued = dhcp_failover_pool_dobalance(state, NULL);
 2358: 
 2359: 	dhcp_failover_send_poolresp(state, queued);
 2360: 
 2361: 	if (queued)
 2362: 		dhcp_failover_send_updates(state);
 2363: 	else
 2364: 		log_info("peer %s: Got POOLREQ, answering negatively!  "
 2365: 			 "Peer may be out of leases or database inconsistent.",
 2366: 			 state->name);
 2367: }
 2368: 
 2369: /*
 2370:  * Do the meat of the work common to all forms of pool rebalance.  If the
 2371:  * caller deems it appropriate to transmit POOLREQ messages, it can use the
 2372:  * sendreq pointer to pass in the address of a FALSE value which this function
 2373:  * will conditionally turn TRUE if a POOLREQ is determined to be necessary.
 2374:  * A NULL value may be passed, in which case no action is taken.
 2375:  */
 2376: static int
 2377: dhcp_failover_pool_dobalance(dhcp_failover_state_t *state,
 2378: 			    isc_boolean_t *sendreq)
 2379: {
 2380: 	int lts, total, thresh, hold, panic, pass;
 2381: 	int leases_queued = 0;
 2382: 	struct lease *lp = (struct lease *)0;
 2383: 	struct lease *next = (struct lease *)0;
 2384: 	struct shared_network *s;
 2385: 	struct pool *p;
 2386: 	binding_state_t peer_lease_state;
 2387: 	binding_state_t my_lease_state;
 2388: 	struct lease **lq;
 2389: 	int (*log_func)(const char *, ...);
 2390: 	const char *result, *reqlog;
 2391: 
 2392: 	if (state -> me.state != normal)
 2393: 		return 0;
 2394: 
 2395: 	state->last_balance = cur_time;
 2396: 
 2397: 	for (s = shared_networks ; s ; s = s->next) {
 2398: 	    for (p = s->pools ; p ; p = p->next) {
 2399: 		if (p->failover_peer != state)
 2400: 		    continue;
 2401: 
 2402: 		/* Right now we're giving the peer half of the free leases.
 2403: 		   If we have more leases than the peer (i.e., more than
 2404: 		   half), then the number of leases we have, less the number
 2405: 		   of leases the peer has, will be how many more leases we
 2406: 		   have than the peer has.   So if we send half that number
 2407: 		   to the peer, we should be even. */
 2408: 		if (p->failover_peer->i_am == primary) {
 2409: 			lts = (p->free_leases - p->backup_leases) / 2;
 2410: 			peer_lease_state = FTS_BACKUP;
 2411: 			my_lease_state = FTS_FREE;
 2412: 			lq = &p->free;
 2413: 		} else {
 2414: 			lts = (p->backup_leases - p->free_leases) / 2;
 2415: 			peer_lease_state = FTS_FREE;
 2416: 			my_lease_state = FTS_BACKUP;
 2417: 			lq = &p->backup;
 2418: 		}
 2419: 
 2420: 		total = p->backup_leases + p->free_leases;
 2421: 
 2422: 		thresh = ((total * state->max_lease_misbalance) + 50) / 100;
 2423: 		hold = ((total * state->max_lease_ownership) + 50) / 100;
 2424: 
 2425: 		/*
 2426: 		 * If we need leases (so lts is negative) more than negative
 2427: 		 * double the thresh%, panic and send poolreq to hopefully wake
 2428: 		 * up the peer (but more likely the db is inconsistent).  But,
 2429: 		 * if this comes out zero, switch to -1 so that the POOLREQ is
 2430: 		 * sent on lts == -2 rather than right away at -1.
 2431: 		 *
 2432: 		 * Note that we do not subtract -1 from panic all the time
 2433: 		 * because thresh% and hold% may come out to the same number,
 2434: 		 * and that is correct operation...where thresh% and hold% are
 2435: 		 * both -1, we want to send poolreq when lts reaches -3.  So,
 2436: 		 * "-3 < -2", lts < panic.
 2437: 		 */
 2438: 		panic = thresh * -2;
 2439: 
 2440: 		if (panic == 0)
 2441: 			panic = -1;
 2442: 
 2443: 		if ((sendreq != NULL) && (lts < panic)) {
 2444: 			reqlog = "  (requesting peer rebalance!)";
 2445: 			*sendreq = ISC_TRUE;
 2446: 		} else
 2447: 			reqlog = "";
 2448: 
 2449: 		log_info("balancing pool %lx %s  total %d  free %d  "
 2450: 			 "backup %d  lts %d  max-own (+/-)%d%s",
 2451: 			 (unsigned long)p,
 2452: 			 (p->shared_network ?
 2453: 			  p->shared_network->name : ""), p->lease_count,
 2454: 			 p->free_leases, p->backup_leases, lts, hold,
 2455: 			 reqlog);
 2456: 
 2457: 		/* In the first pass, try to allocate leases to the
 2458: 		 * peer which it would normally be responsible for (if
 2459: 		 * the lease has a hardware address or client-identifier,
 2460: 		 * and the load-balance-algorithm chooses the peer to
 2461: 		 * answer that address), up to a hold% excess in the peer's
 2462: 		 * favor.  In the second pass, just send the oldest (first
 2463: 		 * on the list) leases up to a hold% excess in our favor.
 2464: 		 *
 2465: 		 * This could make for additional pool rebalance
 2466: 		 * events, but preserving MAC possession should be
 2467: 		 * worth it.
 2468: 		 */
 2469: 		pass = 0;
 2470: 		lease_reference(&lp, *lq, MDL);
 2471: 
 2472: 		while (lp) {
 2473: 			if (next)
 2474: 			    lease_dereference(&next, MDL);
 2475: 			if (lp->next)
 2476: 			    lease_reference(&next, lp->next, MDL);
 2477: 
 2478: 			/*
 2479: 			 * Stop if the pool is 'balanced enough.'
 2480: 			 *
 2481: 			 * The pool is balanced enough if:
 2482: 			 *
 2483: 			 * 1) We're on the first run through and the peer has
 2484: 			 *    its fair share of leases already (lts reaches
 2485: 			 *    -hold).
 2486: 			 * 2) We're on the second run through, we are shifting
 2487: 			 *    never-used leases, and there is a perfectly even
 2488: 			 *    balance (lts reaches zero).
 2489: 			 * 3) Second run through, we are shifting previously
 2490: 			 *    used leases, and the local system has its fair
 2491: 			 *    share but no more (lts reaches hold).
 2492: 			 *
 2493: 			 * Note that this is implemented below in 3,2,1 order.
 2494: 			 */
 2495: 			if (pass) {
 2496: 				if (lp->ends) {
 2497: 					if (lts <= hold)
 2498: 						break;
 2499: 				} else {
 2500: 					if (lts <= 0)
 2501: 						break;
 2502: 				}
 2503: 			} else if (lts <= -hold)
 2504: 				break;
 2505: 
 2506: 			if (pass || peer_wants_lease(lp)) {
 2507: 			    --lts;
 2508: 			    ++leases_queued;
 2509: 			    lp->next_binding_state = peer_lease_state;
 2510: 			    lp->tstp = cur_time;
 2511: 			    lp->starts = cur_time;
 2512: 
 2513: 			    if (!supersede_lease(lp, NULL, 0, 1, 0) ||
 2514: 			        !write_lease(lp))
 2515: 			    	    log_error("can't commit lease %s on "
 2516: 					      "giveaway", piaddr(lp->ip_addr));
 2517: 			}
 2518: 
 2519: 			lease_dereference(&lp, MDL);
 2520: 			if (next)
 2521: 				lease_reference(&lp, next, MDL);
 2522: 			else if (!pass) {
 2523: 				pass = 1;
 2524: 				lease_reference(&lp, *lq, MDL);
 2525: 			}
 2526: 		}
 2527: 
 2528: 		if (next)
 2529: 			lease_dereference(&next, MDL);
 2530: 		if (lp)
 2531: 			lease_dereference(&lp, MDL);
 2532: 
 2533: 		if (lts > thresh) {
 2534: 			result = "IMBALANCED";
 2535: 			log_func = log_error;
 2536: 		} else {
 2537: 			result = "balanced";
 2538: 			log_func = log_info;
 2539: 		}
 2540: 
 2541: 		log_func("%s pool %lx %s  total %d  free %d  backup %d  "
 2542: 			 "lts %d  max-misbal %d", result, (unsigned long)p,
 2543: 			 (p->shared_network ?
 2544: 			  p->shared_network->name : ""), p->lease_count,
 2545: 			 p->free_leases, p->backup_leases, lts, thresh);
 2546:  
 2547: 		/* Recalculate next rebalance event timer. */
 2548: 		dhcp_failover_pool_check(p);
 2549: 	    }
 2550: 	}
 2551: 
 2552: 	if (leases_queued)
 2553: 		commit_leases();
 2554: 
 2555: 	return leases_queued;
 2556: }
 2557: 
 2558: /* dhcp_failover_pool_check: Called whenever FREE or BACKUP leases change
 2559:  * states, on both servers.  Check the scheduled time to rebalance the pool
 2560:  * and lower it if applicable.
 2561:  */
 2562: void
 2563: dhcp_failover_pool_check(struct pool *pool)
 2564: {
 2565: 	dhcp_failover_state_t *peer;
 2566: 	TIME est1, est2;
 2567: 	struct timeval tv;
 2568: 
 2569: 	peer = pool->failover_peer;
 2570: 
 2571: 	if(!peer || peer->me.state != normal)
 2572: 		return;
 2573: 
 2574: 	/* Estimate the time left until lease exhaustion.
 2575: 	 * The first lease on the backup or free lists is also the oldest
 2576: 	 * lease.  It is reasonable to guess that it will take at least
 2577: 	 * as much time for a pool to run out of leases, as the present
 2578: 	 * age of the oldest lease (seconds since it expired).
 2579: 	 *
 2580: 	 * Note that this isn't so sane of an assumption if the oldest
 2581: 	 * lease is a virgin (ends = 0), we wind up sending this against
 2582: 	 * the max_balance bounds check.
 2583: 	 */
 2584: 	if(pool->free && pool->free->ends < cur_time)
 2585: 		est1 = cur_time - pool->free->ends;
 2586: 	else
 2587: 		est1 = 0;
 2588: 
 2589: 	if(pool->backup && pool->backup->ends < cur_time)
 2590: 		est2 = cur_time - pool->backup->ends;
 2591: 	else
 2592: 		est2 = 0;
 2593: 
 2594: 	/* We don't want to schedule rebalance for when we think we'll run
 2595: 	 * out of leases, we want to schedule the rebalance for when we think
 2596: 	 * the disparity will be 'large enough' to warrant action.
 2597: 	 */
 2598: 	est1 = ((est1 * peer->max_lease_misbalance) + 50) / 100;
 2599: 	est2 = ((est2 * peer->max_lease_misbalance) + 50) / 100;
 2600: 
 2601: 	/* Guess when the local system will begin issuing POOLREQ panic
 2602: 	 * attacks because "max_lease_misbalance*2" has been exceeded.
 2603: 	 */
 2604: 	if(peer->i_am == primary)
 2605: 		est1 *= 2;
 2606: 	else
 2607: 		est2 *= 2;
 2608: 
 2609: 	/* Select the smallest time. */
 2610: 	if(est1 > est2)
 2611: 		est1 = est2;
 2612: 
 2613: 	/* Bounded by the maximum configured value. */
 2614: 	if(est1 > peer->max_balance)
 2615: 		est1 = peer->max_balance;
 2616: 
 2617: 	/* Project this time into the future. */
 2618: 	est1 += cur_time;
 2619: 
 2620: 	/* Do not move the time down under the minimum. */
 2621: 	est2 = peer->last_balance + peer->min_balance;
 2622: 	if(peer->last_balance && (est1 < est2))
 2623: 		est1 = est2;
 2624: 
 2625: 	/* Introduce a random delay. */
 2626: 	est1 += random() % 5;
 2627: 
 2628: 	/* Do not move the time forward, or reset to the same time. */
 2629: 	if(peer->sched_balance) {
 2630: 		if (est1 >= peer->sched_balance)
 2631: 			return;
 2632: 
 2633: 		/* We are about to schedule the time down, cancel the
 2634: 		 * current timeout.
 2635: 		 */
 2636: 		cancel_timeout(dhcp_failover_pool_rebalance, peer);
 2637: 	}
 2638: 
 2639: 	/* The time is different, and lower, use it. */
 2640: 	peer->sched_balance = est1;
 2641: 
 2642: #if defined(DEBUG_FAILOVER_TIMING)
 2643: 	log_info("add_timeout +%d dhcp_failover_pool_rebalance",
 2644: 		 (int)(est1 - cur_time));
 2645: #endif
 2646: 	tv.tv_sec = est1;
 2647: 	tv.tv_usec = 0;
 2648: 	add_timeout(&tv, dhcp_failover_pool_rebalance, peer,
 2649: 			(tvref_t)dhcp_failover_state_reference,
 2650: 			(tvunref_t)dhcp_failover_state_dereference);
 2651: }
 2652: 
 2653: int dhcp_failover_state_pool_check (dhcp_failover_state_t *state)
 2654: {
 2655: 	struct shared_network *s;
 2656: 	struct pool *p;
 2657: 
 2658: 	for (s = shared_networks; s; s = s -> next) {
 2659: 		for (p = s -> pools; p; p = p -> next) {
 2660: 			if (p -> failover_peer != state)
 2661: 				continue;
 2662: 			dhcp_failover_pool_check (p);
 2663: 		}
 2664: 	}
 2665: 	return 0;
 2666: }
 2667: 
 2668: isc_result_t dhcp_failover_send_updates (dhcp_failover_state_t *state)
 2669: {
 2670: 	struct lease *lp = (struct lease *)0;
 2671: 	isc_result_t status;
 2672: 
 2673: 	/* Can't update peer if we're not talking to it! */
 2674: 	if (!state -> link_to_peer)
 2675: 		return ISC_R_SUCCESS;
 2676: 
 2677: 	/* If there are acks pending, transmit them prior to potentially
 2678: 	 * sending new updates for the same lease.
 2679: 	 */
 2680: 	if (state->toack_queue_head != NULL)
 2681: 		dhcp_failover_send_acks(state);
 2682: 
 2683: 	while ((state -> partner.max_flying_updates >
 2684: 		state -> cur_unacked_updates) && state -> update_queue_head) {
 2685: 		/* Grab the head of the update queue. */
 2686: 		lease_reference (&lp, state -> update_queue_head, MDL);
 2687: 
 2688: 		/* Send the update to the peer. */
 2689: 		status = dhcp_failover_send_bind_update (state, lp);
 2690: 		if (status != ISC_R_SUCCESS) {
 2691: 			lease_dereference (&lp, MDL);
 2692: 			return status;
 2693: 		}
 2694: 		lp -> flags &= ~ON_UPDATE_QUEUE;
 2695: 
 2696: 		/* Take it off the head of the update queue and put the next
 2697: 		   item in the update queue at the head. */
 2698: 		lease_dereference (&state -> update_queue_head, MDL);
 2699: 		if (lp -> next_pending) {
 2700: 			lease_reference (&state -> update_queue_head,
 2701: 					 lp -> next_pending, MDL);
 2702: 			lease_dereference (&lp -> next_pending, MDL);
 2703: 		} else {
 2704: 			lease_dereference (&state -> update_queue_tail, MDL);
 2705: 		}
 2706: 
 2707: 		if (state -> ack_queue_head) {
 2708: 			lease_reference
 2709: 				(&state -> ack_queue_tail -> next_pending,
 2710: 				 lp, MDL);
 2711: 			lease_dereference (&state -> ack_queue_tail, MDL);
 2712: 		} else {
 2713: 			lease_reference (&state -> ack_queue_head, lp, MDL);
 2714: 		}
 2715: #if defined (POINTER_DEBUG)
 2716: 		if (lp -> next_pending) {
 2717: 			log_error ("ack_queue_tail: lp -> next_pending");
 2718: 			abort ();
 2719: 		}
 2720: #endif
 2721: 		lease_reference (&state -> ack_queue_tail, lp, MDL);
 2722: 		lp -> flags |= ON_ACK_QUEUE;
 2723: 		lease_dereference (&lp, MDL);
 2724: 
 2725: 		/* Count the object as an unacked update. */
 2726: 		state -> cur_unacked_updates++;
 2727: 	}
 2728: 	return ISC_R_SUCCESS;
 2729: }
 2730: 
 2731: /* Queue an update for a lease.   Always returns 1 at this point - it's
 2732:    not an error for this to be called on a lease for which there's no
 2733:    failover peer. */
 2734: 
 2735: int dhcp_failover_queue_update (struct lease *lease, int immediate)
 2736: {
 2737: 	dhcp_failover_state_t *state;
 2738: 
 2739: 	if (!lease -> pool ||
 2740: 	    !lease -> pool -> failover_peer)
 2741: 		return 1;
 2742: 
 2743: 	/* If it's already on the update queue, leave it there. */
 2744: 	if (lease -> flags & ON_UPDATE_QUEUE)
 2745: 		return 1;
 2746: 
 2747: 	/* Get the failover state structure for this lease. */
 2748: 	state = lease -> pool -> failover_peer;
 2749: 
 2750: 	/* If it's on the ack queue, take it off. */
 2751: 	if (lease -> flags & ON_ACK_QUEUE)
 2752: 		dhcp_failover_ack_queue_remove (state, lease);
 2753: 
 2754: 	if (state -> update_queue_head) {
 2755: 		lease_reference (&state -> update_queue_tail -> next_pending,
 2756: 				 lease, MDL);
 2757: 		lease_dereference (&state -> update_queue_tail, MDL);
 2758: 	} else {
 2759: 		lease_reference (&state -> update_queue_head, lease, MDL);
 2760: 	}
 2761: #if defined (POINTER_DEBUG)
 2762: 	if (lease -> next_pending) {
 2763: 		log_error ("next pending on update queue lease.");
 2764: #if defined (DEBUG_RC_HISTORY)
 2765: 		dump_rc_history (lease);
 2766: #endif
 2767: 		abort ();
 2768: 	}
 2769: #endif
 2770: 	lease_reference (&state -> update_queue_tail, lease, MDL);
 2771: 	lease -> flags |= ON_UPDATE_QUEUE;
 2772: 	if (immediate)
 2773: 		dhcp_failover_send_updates (state);
 2774: 	return 1;
 2775: }
 2776: 
 2777: int dhcp_failover_send_acks (dhcp_failover_state_t *state)
 2778: {
 2779: 	failover_message_t *msg = (failover_message_t *)0;
 2780: 
 2781: 	/* Must commit all leases prior to acking them. */
 2782: 	if (!commit_leases ())
 2783: 		return 0;
 2784: 
 2785: 	while (state -> toack_queue_head) {
 2786: 		failover_message_reference
 2787: 			(&msg, state -> toack_queue_head, MDL);
 2788: 		failover_message_dereference
 2789: 			(&state -> toack_queue_head, MDL);
 2790: 		if (msg -> next) {
 2791: 			failover_message_reference
 2792: 				(&state -> toack_queue_head, msg -> next, MDL);
 2793: 		}
 2794: 
 2795: 		dhcp_failover_send_bind_ack (state, msg, 0, (const char *)0);
 2796: 
 2797: 		failover_message_dereference (&msg, MDL);
 2798: 	}
 2799: 
 2800: 	if (state -> toack_queue_tail)
 2801: 		failover_message_dereference (&state -> toack_queue_tail, MDL);
 2802: 	state -> pending_acks = 0;
 2803: 
 2804: 	return 1;
 2805: }
 2806: 
 2807: void dhcp_failover_toack_queue_timeout (void *vs)
 2808: {
 2809: 	dhcp_failover_state_t *state = vs;
 2810: 
 2811: #if defined (DEBUG_FAILOVER_TIMING)
 2812: 	log_info ("dhcp_failover_toack_queue_timeout");
 2813: #endif
 2814: 
 2815: 	dhcp_failover_send_acks (state);
 2816: }
 2817: 
 2818: /* Queue an ack for a message.  There is currently no way to queue a
 2819:    negative ack -- these need to be sent directly. */
 2820: 
 2821: int dhcp_failover_queue_ack (dhcp_failover_state_t *state,
 2822: 			     failover_message_t *msg)
 2823: {
 2824: 	struct timeval tv;
 2825: 
 2826: 	if (state -> toack_queue_head) {
 2827: 		failover_message_reference
 2828: 			(&state -> toack_queue_tail -> next, msg, MDL);
 2829: 		failover_message_dereference (&state -> toack_queue_tail, MDL);
 2830: 	} else {
 2831: 		failover_message_reference (&state -> toack_queue_head,
 2832: 					    msg, MDL);
 2833: 	}
 2834: 	failover_message_reference (&state -> toack_queue_tail, msg, MDL);
 2835: 
 2836: 	state -> pending_acks++;
 2837: 
 2838: 	/* Flush the toack queue whenever we exceed half the number of
 2839: 	   allowed unacked updates. */
 2840: 	if (state -> pending_acks >= state -> partner.max_flying_updates / 2) {
 2841: 		dhcp_failover_send_acks (state);
 2842: 	}
 2843: 
 2844: 	/* Schedule a timeout to flush the ack queue. */
 2845: 	if (state -> pending_acks > 0) {
 2846: #if defined (DEBUG_FAILOVER_TIMING)
 2847: 		log_info ("add_timeout +2 %s",
 2848: 			  "dhcp_failover_toack_queue_timeout");
 2849: #endif
 2850: 		tv . tv_sec = cur_time + 2;
 2851: 		tv . tv_usec = 0;
 2852: 		add_timeout (&tv,
 2853: 			     dhcp_failover_toack_queue_timeout, state,
 2854: 			     (tvref_t)dhcp_failover_state_reference,
 2855: 			     (tvunref_t)dhcp_failover_state_dereference);
 2856: 	}
 2857: 
 2858: 	return 1;
 2859: }
 2860: 
 2861: void dhcp_failover_ack_queue_remove (dhcp_failover_state_t *state,
 2862: 				     struct lease *lease)
 2863: {
 2864: 	struct lease *lp;
 2865: 
 2866: 	if (!(lease -> flags & ON_ACK_QUEUE))
 2867: 		return;
 2868: 
 2869: 	if (state -> ack_queue_head == lease) {
 2870: 		lease_dereference (&state -> ack_queue_head, MDL);
 2871: 		if (lease -> next_pending) {
 2872: 			lease_reference (&state -> ack_queue_head,
 2873: 					 lease -> next_pending, MDL);
 2874: 			lease_dereference (&lease -> next_pending, MDL);
 2875: 		} else {
 2876: 			lease_dereference (&state -> ack_queue_tail, MDL);
 2877: 		}
 2878: 	} else {
 2879: 		for (lp = state -> ack_queue_head;
 2880: 		     lp && lp -> next_pending != lease;
 2881: 		     lp = lp -> next_pending)
 2882: 			;
 2883: 
 2884: 		if (!lp)
 2885: 			return;
 2886: 
 2887: 		lease_dereference (&lp -> next_pending, MDL);
 2888: 		if (lease -> next_pending) {
 2889: 			lease_reference (&lp -> next_pending,
 2890: 					 lease -> next_pending, MDL);
 2891: 			lease_dereference (&lease -> next_pending, MDL);
 2892: 		} else {
 2893: 			lease_dereference (&state -> ack_queue_tail, MDL);
 2894: 			if (lp -> next_pending) {
 2895: 				log_error ("state -> ack_queue_tail");
 2896: 				abort ();
 2897: 			}
 2898: 			lease_reference (&state -> ack_queue_tail, lp, MDL);
 2899: 		}
 2900: 	}
 2901: 
 2902: 	lease -> flags &= ~ON_ACK_QUEUE;
 2903: 	/* Multiple acks on one XID is an error and may cause badness. */
 2904: 	lease->last_xid = 0;
 2905: 	/* XXX: this violates draft-failover.  We can't send another
 2906: 	 * update just because we forgot about an old one that hasn't
 2907: 	 * been acked yet.
 2908: 	 */
 2909: 	state -> cur_unacked_updates--;
 2910: 
 2911: 	/*
 2912: 	 * When updating leases as a result of an ack, we defer the commit
 2913: 	 * for performance reasons.  When there are no more acks pending,
 2914: 	 * do a commit.
 2915: 	 */
 2916: 	if (state -> cur_unacked_updates == 0) {
 2917: 		commit_leases();
 2918: 	}
 2919: }
 2920: 
 2921: isc_result_t dhcp_failover_state_set_value (omapi_object_t *h,
 2922: 					    omapi_object_t *id,
 2923: 					    omapi_data_string_t *name,
 2924: 					    omapi_typed_data_t *value)
 2925: {
 2926: 	isc_result_t status;
 2927: 
 2928: 	if (h -> type != dhcp_type_failover_state)
 2929: 		return ISC_R_INVALIDARG;
 2930: 
 2931: 	/* This list of successful returns is completely wrong, but the
 2932: 	   fastest way to make dhcpctl do something vaguely sane when
 2933: 	   you try to change the local state. */
 2934: 
 2935: 	if (!omapi_ds_strcmp (name, "name")) {
 2936: 		return ISC_R_SUCCESS;
 2937: 	} else if (!omapi_ds_strcmp (name, "partner-address")) {
 2938: 		return ISC_R_SUCCESS;
 2939: 	} else if (!omapi_ds_strcmp (name, "local-address")) {
 2940: 		return ISC_R_SUCCESS;
 2941: 	} else if (!omapi_ds_strcmp (name, "partner-port")) {
 2942: 		return ISC_R_SUCCESS;
 2943: 	} else if (!omapi_ds_strcmp (name, "local-port")) {
 2944: 		return ISC_R_SUCCESS;
 2945: 	} else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
 2946: 		return ISC_R_SUCCESS;
 2947: 	} else if (!omapi_ds_strcmp (name, "mclt")) {
 2948: 		return ISC_R_SUCCESS;
 2949: 	} else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
 2950: 		return ISC_R_SUCCESS;
 2951: 	} else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
 2952: 		return ISC_R_SUCCESS;
 2953: 	} else if (!omapi_ds_strcmp (name, "partner-state")) {
 2954: 		return ISC_R_SUCCESS;
 2955: 	} else if (!omapi_ds_strcmp (name, "local-state")) {
 2956: 		unsigned long l;
 2957: 		status = omapi_get_int_value (&l, value);
 2958: 		if (status != ISC_R_SUCCESS)
 2959: 			return status;
 2960: 		return dhcp_failover_set_state ((dhcp_failover_state_t *)h, l);
 2961: 	} else if (!omapi_ds_strcmp (name, "partner-stos")) {
 2962: 		return ISC_R_SUCCESS;
 2963: 	} else if (!omapi_ds_strcmp (name, "local-stos")) {
 2964: 		return ISC_R_SUCCESS;
 2965: 	} else if (!omapi_ds_strcmp (name, "hierarchy")) {
 2966: 		return ISC_R_SUCCESS;
 2967: 	} else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
 2968: 		return ISC_R_SUCCESS;
 2969: 	} else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
 2970: 		return ISC_R_SUCCESS;
 2971: 	} else if (!omapi_ds_strcmp (name, "skew")) {
 2972: 		return ISC_R_SUCCESS;
 2973: 	} else if (!omapi_ds_strcmp (name, "max-response-delay")) {
 2974: 		return ISC_R_SUCCESS;
 2975: 	} else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
 2976: 		return ISC_R_SUCCESS;
 2977: 	}
 2978: 		
 2979: 	if (h -> inner && h -> inner -> type -> set_value)
 2980: 		return (*(h -> inner -> type -> set_value))
 2981: 			(h -> inner, id, name, value);
 2982: 	return ISC_R_NOTFOUND;
 2983: }
 2984: 
 2985: void dhcp_failover_keepalive (void *vs)
 2986: {
 2987: }
 2988: 
 2989: void dhcp_failover_reconnect (void *vs)
 2990: {
 2991: 	dhcp_failover_state_t *state = vs;
 2992: 	isc_result_t status;
 2993: 	struct timeval tv;
 2994: 
 2995: #if defined (DEBUG_FAILOVER_TIMING)
 2996: 	log_info ("dhcp_failover_reconnect");
 2997: #endif
 2998: 	/* If we already connected the other way, let the connection
 2999:            recovery code initiate any retry that may be required. */
 3000: 	if (state -> link_to_peer)
 3001: 		return;
 3002: 
 3003: 	status = dhcp_failover_link_initiate ((omapi_object_t *)state);
 3004: 	if (status != ISC_R_SUCCESS && status != ISC_R_INCOMPLETE) {
 3005: 		log_info ("failover peer %s: %s", state -> name,
 3006: 			  isc_result_totext (status));
 3007: #if defined (DEBUG_FAILOVER_TIMING)
 3008: 		log_info("add_timeout +90 dhcp_failover_reconnect");
 3009: #endif
 3010: 		tv . tv_sec = cur_time + 90;
 3011: 		tv . tv_usec = 0;
 3012: 		add_timeout(&tv, dhcp_failover_reconnect, state,
 3013: 			    (tvref_t)dhcp_failover_state_reference,
 3014: 			    (tvunref_t)dhcp_failover_state_dereference);
 3015: 	}
 3016: }
 3017: 
 3018: void dhcp_failover_startup_timeout (void *vs)
 3019: {
 3020: 	dhcp_failover_state_t *state = vs;
 3021: 
 3022: #if defined (DEBUG_FAILOVER_TIMING)
 3023: 	log_info ("dhcp_failover_startup_timeout");
 3024: #endif
 3025: 
 3026: 	dhcp_failover_state_transition (state, "disconnect");
 3027: }
 3028: 
 3029: void dhcp_failover_link_startup_timeout (void *vl)
 3030: {
 3031: 	dhcp_failover_link_t *link = vl;
 3032: 	omapi_object_t *p;
 3033: 
 3034: 	for (p = (omapi_object_t *)link; p -> inner; p = p -> inner)
 3035: 		;
 3036: 	for (; p; p = p -> outer)
 3037: 		if (p -> type == omapi_type_connection)
 3038: 			break;
 3039: 	if (p) {
 3040: 		log_info ("failover: link startup timeout");
 3041: 		omapi_disconnect (p, 1);
 3042: 	}
 3043: }
 3044: 
 3045: void dhcp_failover_listener_restart (void *vs)
 3046: {
 3047: 	dhcp_failover_state_t *state = vs;
 3048: 	isc_result_t status;
 3049: 	struct timeval tv;
 3050: 
 3051: #if defined (DEBUG_FAILOVER_TIMING)
 3052: 	log_info ("dhcp_failover_listener_restart");
 3053: #endif
 3054: 
 3055: 	status = dhcp_failover_listen ((omapi_object_t *)state);
 3056: 	if (status != ISC_R_SUCCESS) {
 3057: 		log_info ("failover peer %s: %s", state -> name,
 3058: 			  isc_result_totext (status));
 3059: #if defined (DEBUG_FAILOVER_TIMING)
 3060: 		log_info ("add_timeout +90 %s",
 3061: 			  "dhcp_failover_listener_restart");
 3062: #endif
 3063: 		tv . tv_sec = cur_time + 90;
 3064: 		tv . tv_usec = 0;
 3065: 		add_timeout (&tv,
 3066: 			     dhcp_failover_listener_restart, state,
 3067: 			     (tvref_t)dhcp_failover_state_reference,
 3068: 			     (tvunref_t)dhcp_failover_state_dereference);
 3069: 	}
 3070: }
 3071: 
 3072: isc_result_t dhcp_failover_state_get_value (omapi_object_t *h,
 3073: 					    omapi_object_t *id,
 3074: 					    omapi_data_string_t *name,
 3075: 					    omapi_value_t **value)
 3076: {
 3077: 	dhcp_failover_state_t *s;
 3078: 	struct option_cache *oc;
 3079: 	struct data_string ds;
 3080: 	isc_result_t status;
 3081: 
 3082: 	if (h -> type != dhcp_type_failover_state)
 3083: 		return ISC_R_INVALIDARG;
 3084: 	s = (dhcp_failover_state_t *)h;
 3085: 	
 3086: 	if (!omapi_ds_strcmp (name, "name")) {
 3087: 		if (s -> name)
 3088: 			return omapi_make_string_value (value,
 3089: 							name, s -> name, MDL);
 3090: 		return ISC_R_NOTFOUND;
 3091: 	} else if (!omapi_ds_strcmp (name, "partner-address")) {
 3092: 		oc = s -> partner.address;
 3093: 	      getaddr:
 3094: 		memset (&ds, 0, sizeof ds);
 3095: 		if (!evaluate_option_cache (&ds, (struct packet *)0,
 3096: 					    (struct lease *)0,
 3097: 					    (struct client_state *)0,
 3098: 					    (struct option_state *)0,
 3099: 					    (struct option_state *)0,
 3100: 					    &global_scope, oc, MDL)) {
 3101: 			return ISC_R_NOTFOUND;
 3102: 		}
 3103: 		status = omapi_make_const_value (value,
 3104: 						 name, ds.data, ds.len, MDL);
 3105: 		/* Disgusting kludge: */
 3106: 		if (oc == s -> me.address && !s -> server_identifier.len)
 3107: 			data_string_copy (&s -> server_identifier, &ds, MDL);
 3108: 		data_string_forget (&ds, MDL);
 3109: 		return status;
 3110: 	} else if (!omapi_ds_strcmp (name, "local-address")) {
 3111: 		oc = s -> me.address;
 3112: 		goto getaddr;
 3113: 	} else if (!omapi_ds_strcmp (name, "partner-port")) {
 3114: 		return omapi_make_int_value (value, name,
 3115: 					     s -> partner.port, MDL);
 3116: 	} else if (!omapi_ds_strcmp (name, "local-port")) {
 3117: 		return omapi_make_int_value (value,
 3118: 					     name, s -> me.port, MDL);
 3119: 	} else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
 3120: 		return omapi_make_uint_value (value, name,
 3121: 					      s -> me.max_flying_updates,
 3122: 					      MDL);
 3123: 	} else if (!omapi_ds_strcmp (name, "mclt")) {
 3124: 		return omapi_make_uint_value (value, name, s -> mclt, MDL);
 3125: 	} else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
 3126: 		return omapi_make_int_value (value, name,
 3127: 					     s -> load_balance_max_secs, MDL);
 3128: 	} else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
 3129: 		if (s -> hba)
 3130: 			return omapi_make_const_value (value, name,
 3131: 						       s -> hba, 32, MDL);
 3132: 		return ISC_R_NOTFOUND;
 3133: 	} else if (!omapi_ds_strcmp (name, "partner-state")) {
 3134: 		return omapi_make_uint_value (value, name,
 3135: 					     s -> partner.state, MDL);
 3136: 	} else if (!omapi_ds_strcmp (name, "local-state")) {
 3137: 		return omapi_make_uint_value (value, name,
 3138: 					      s -> me.state, MDL);
 3139: 	} else if (!omapi_ds_strcmp (name, "partner-stos")) {
 3140: 		return omapi_make_int_value (value, name,
 3141: 					     s -> partner.stos, MDL);
 3142: 	} else if (!omapi_ds_strcmp (name, "local-stos")) {
 3143: 		return omapi_make_int_value (value, name,
 3144: 					     s -> me.stos, MDL);
 3145: 	} else if (!omapi_ds_strcmp (name, "hierarchy")) {
 3146: 		return omapi_make_uint_value (value, name, s -> i_am, MDL);
 3147: 	} else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
 3148: 		return omapi_make_int_value (value, name,
 3149: 					     s -> last_packet_sent, MDL);
 3150: 	} else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
 3151: 		return omapi_make_int_value (value, name,
 3152: 					     s -> last_timestamp_received,
 3153: 					     MDL);
 3154: 	} else if (!omapi_ds_strcmp (name, "skew")) {
 3155: 		return omapi_make_int_value (value, name, s -> skew, MDL);
 3156: 	} else if (!omapi_ds_strcmp (name, "max-response-delay")) {
 3157: 		return omapi_make_uint_value (value, name,
 3158: 					     s -> me.max_response_delay,
 3159: 					      MDL);
 3160: 	} else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
 3161: 		return omapi_make_int_value (value, name,
 3162: 					     s -> cur_unacked_updates, MDL);
 3163: 	}
 3164: 		
 3165: 	if (h -> inner && h -> inner -> type -> get_value)
 3166: 		return (*(h -> inner -> type -> get_value))
 3167: 			(h -> inner, id, name, value);
 3168: 	return ISC_R_NOTFOUND;
 3169: }
 3170: 
 3171: isc_result_t dhcp_failover_state_destroy (omapi_object_t *h,
 3172: 					      const char *file, int line)
 3173: {
 3174: 	dhcp_failover_state_t *s;
 3175: 
 3176: 	if (h -> type != dhcp_type_failover_state)
 3177: 		return ISC_R_INVALIDARG;
 3178: 	s = (dhcp_failover_state_t *)h;
 3179: 
 3180: 	if (s -> link_to_peer)
 3181: 	    dhcp_failover_link_dereference (&s -> link_to_peer, file, line);
 3182: 	if (s -> name) {
 3183: 		dfree (s -> name, MDL);
 3184: 		s -> name = (char *)0;
 3185: 	}
 3186: 	if (s -> partner.address)
 3187: 		option_cache_dereference (&s -> partner.address, file, line);
 3188: 	if (s -> me.address)
 3189: 		option_cache_dereference (&s -> me.address, file, line);
 3190: 	if (s -> hba) {
 3191: 		dfree (s -> hba, file, line);
 3192: 		s -> hba = (u_int8_t *)0;
 3193: 	}
 3194: 	if (s -> update_queue_head)
 3195: 		lease_dereference (&s -> update_queue_head, file, line);
 3196: 	if (s -> update_queue_tail)
 3197: 		lease_dereference (&s -> update_queue_tail, file, line);
 3198: 	if (s -> ack_queue_head)
 3199: 		lease_dereference (&s -> ack_queue_head, file, line);
 3200: 	if (s -> ack_queue_tail)
 3201: 		lease_dereference (&s -> ack_queue_tail, file, line);
 3202: 	if (s -> send_update_done)
 3203: 		lease_dereference (&s -> send_update_done, file, line);
 3204: 	if (s -> toack_queue_head)
 3205: 		failover_message_dereference (&s -> toack_queue_head,
 3206: 					      file, line);
 3207: 	if (s -> toack_queue_tail)
 3208: 		failover_message_dereference (&s -> toack_queue_tail,
 3209: 					      file, line);
 3210: 	return ISC_R_SUCCESS;
 3211: }
 3212: 
 3213: /* Write all the published values associated with the object through the
 3214:    specified connection. */
 3215: 
 3216: isc_result_t dhcp_failover_state_stuff (omapi_object_t *c,
 3217: 					omapi_object_t *id,
 3218: 					omapi_object_t *h)
 3219: {
 3220: 	dhcp_failover_state_t *s;
 3221: 	omapi_connection_object_t *conn;
 3222: 	isc_result_t status;
 3223: 
 3224: 	if (c -> type != omapi_type_connection)
 3225: 		return ISC_R_INVALIDARG;
 3226: 	conn = (omapi_connection_object_t *)c;
 3227: 
 3228: 	if (h -> type != dhcp_type_failover_state)
 3229: 		return ISC_R_INVALIDARG;
 3230: 	s = (dhcp_failover_state_t *)h;
 3231: 	
 3232: 	status = omapi_connection_put_name (c, "name");
 3233: 	if (status != ISC_R_SUCCESS)
 3234: 		return status;
 3235: 	status = omapi_connection_put_string (c, s -> name);
 3236: 	if (status != ISC_R_SUCCESS)
 3237: 		return status;
 3238: 
 3239: 	status = omapi_connection_put_name (c, "partner-address");
 3240: 	if (status != ISC_R_SUCCESS)
 3241: 		return status;
 3242: 	status = omapi_connection_put_uint32 (c, sizeof s -> partner.address);
 3243: 	if (status != ISC_R_SUCCESS)
 3244: 		return status;
 3245: 	status = omapi_connection_copyin (c, (u_int8_t *)&s -> partner.address,
 3246: 					  sizeof s -> partner.address);
 3247: 	if (status != ISC_R_SUCCESS)
 3248: 		return status;
 3249: 	
 3250: 	status = omapi_connection_put_name (c, "partner-port");
 3251: 	if (status != ISC_R_SUCCESS)
 3252: 		return status;
 3253: 	status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
 3254: 	if (status != ISC_R_SUCCESS)
 3255: 		return status;
 3256: 	status = omapi_connection_put_uint32 (c, (u_int32_t)s -> partner.port);
 3257: 	if (status != ISC_R_SUCCESS)
 3258: 		return status;
 3259: 	
 3260: 	status = omapi_connection_put_name (c, "local-address");
 3261: 	if (status != ISC_R_SUCCESS)
 3262: 		return status;
 3263: 	status = omapi_connection_put_uint32 (c, sizeof s -> me.address);
 3264: 	if (status != ISC_R_SUCCESS)
 3265: 		return status;
 3266: 	status = omapi_connection_copyin (c, (u_int8_t *)&s -> me.address,
 3267: 					  sizeof s -> me.address);
 3268: 	if (status != ISC_R_SUCCESS)
 3269: 		return status;
 3270: 	
 3271: 	status = omapi_connection_put_name (c, "local-port");
 3272: 	if (status != ISC_R_SUCCESS)
 3273: 		return status;
 3274: 	status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
 3275: 	if (status != ISC_R_SUCCESS)
 3276: 		return status;
 3277: 	status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.port);
 3278: 	if (status != ISC_R_SUCCESS)
 3279: 		return status;
 3280: 	
 3281: 	status = omapi_connection_put_name (c, "max-outstanding-updates");
 3282: 	if (status != ISC_R_SUCCESS)
 3283: 		return status;
 3284: 	status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
 3285: 	if (status != ISC_R_SUCCESS)
 3286: 		return status;
 3287: 	status = omapi_connection_put_uint32 (c,
 3288: 					      s -> me.max_flying_updates);
 3289: 	if (status != ISC_R_SUCCESS)
 3290: 		return status;
 3291: 
 3292: 	status = omapi_connection_put_name (c, "mclt");
 3293: 	if (status != ISC_R_SUCCESS)
 3294: 		return status;
 3295: 	status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
 3296: 	if (status != ISC_R_SUCCESS)
 3297: 		return status;
 3298: 	status = omapi_connection_put_uint32 (c, s -> mclt);
 3299: 	if (status != ISC_R_SUCCESS)
 3300: 		return status;
 3301: 
 3302: 	status = omapi_connection_put_name (c, "load-balance-max-secs");
 3303: 	if (status != ISC_R_SUCCESS)
 3304: 		return status;
 3305: 	status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
 3306: 	if (status != ISC_R_SUCCESS)
 3307: 		return status;
 3308: 	status = (omapi_connection_put_uint32
 3309: 		  (c, (u_int32_t)s -> load_balance_max_secs));
 3310: 	if (status != ISC_R_SUCCESS)
 3311: 		return status;
 3312: 
 3313: 	
 3314: 	if (s -> hba) {
 3315: 		status = omapi_connection_put_name (c, "load-balance-hba");
 3316: 		if (status != ISC_R_SUCCESS)
 3317: 			return status;
 3318: 		status = omapi_connection_put_uint32 (c, 32);
 3319: 		if (status != ISC_R_SUCCESS)
 3320: 			return status;
 3321: 		status = omapi_connection_copyin (c, s -> hba, 32);
 3322: 		if (status != ISC_R_SUCCESS)
 3323: 			return status;
 3324: 	}
 3325: 
 3326: 	status = omapi_connection_put_name (c, "partner-state");
 3327: 	if (status != ISC_R_SUCCESS)
 3328: 		return status;
 3329: 	status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
 3330: 	if (status != ISC_R_SUCCESS)
 3331: 		return status;
 3332: 	status = omapi_connection_put_uint32 (c, s -> partner.state);
 3333: 	if (status != ISC_R_SUCCESS)
 3334: 		return status;
 3335: 	
 3336: 	status = omapi_connection_put_name (c, "local-state");
 3337: 	if (status != ISC_R_SUCCESS)
 3338: 		return status;
 3339: 	status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
 3340: 	if (status != ISC_R_SUCCESS)
 3341: 		return status;
 3342: 	status = omapi_connection_put_uint32 (c, s -> me.state);
 3343: 	if (status != ISC_R_SUCCESS)
 3344: 		return status;
 3345: 	
 3346: 	status = omapi_connection_put_name (c, "partner-stos");
 3347: 	if (status != ISC_R_SUCCESS)
 3348: 		return status;
 3349: 	status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
 3350: 	if (status != ISC_R_SUCCESS)
 3351: 		return status;
 3352: 	status = omapi_connection_put_uint32 (c,
 3353: 					      (u_int32_t)s -> partner.stos);
 3354: 	if (status != ISC_R_SUCCESS)
 3355: 		return status;
 3356: 
 3357: 	status = omapi_connection_put_name (c, "local-stos");
 3358: 	if (status != ISC_R_SUCCESS)
 3359: 		return status;
 3360: 	status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
 3361: 	if (status != ISC_R_SUCCESS)
 3362: 		return status;
 3363: 	status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.stos);
 3364: 	if (status != ISC_R_SUCCESS)
 3365: 		return status;
 3366: 
 3367: 	status = omapi_connection_put_name (c, "hierarchy");
 3368: 	if (status != ISC_R_SUCCESS)
 3369: 		return status;
 3370: 	status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
 3371: 	if (status != ISC_R_SUCCESS)
 3372: 		return status;
 3373: 	status = omapi_connection_put_uint32 (c, s -> i_am);
 3374: 	if (status != ISC_R_SUCCESS)
 3375: 		return status;
 3376: 
 3377: 	status = omapi_connection_put_name (c, "last-packet-sent");
 3378: 	if (status != ISC_R_SUCCESS)
 3379: 		return status;
 3380: 	status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
 3381: 	if (status != ISC_R_SUCCESS)
 3382: 		return status;
 3383: 	status = (omapi_connection_put_uint32
 3384: 		  (c, (u_int32_t)s -> last_packet_sent));
 3385: 	if (status != ISC_R_SUCCESS)
 3386: 		return status;
 3387: 
 3388: 	status = omapi_connection_put_name (c, "last-timestamp-received");
 3389: 	if (status != ISC_R_SUCCESS)
 3390: 		return status;
 3391: 	status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
 3392: 	if (status != ISC_R_SUCCESS)
 3393: 		return status;
 3394: 	status = (omapi_connection_put_uint32
 3395: 		  (c, (u_int32_t)s -> last_timestamp_received));
 3396: 	if (status != ISC_R_SUCCESS)
 3397: 		return status;
 3398: 
 3399: 	status = omapi_connection_put_name (c, "skew");
 3400: 	if (status != ISC_R_SUCCESS)
 3401: 		return status;
 3402: 	status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
 3403: 	if (status != ISC_R_SUCCESS)
 3404: 		return status;
 3405: 	status = omapi_connection_put_uint32 (c, (u_int32_t)s -> skew);
 3406: 	if (status != ISC_R_SUCCESS)
 3407: 		return status;
 3408: 
 3409: 	status = omapi_connection_put_name (c, "max-response-delay");
 3410: 	if (status != ISC_R_SUCCESS)
 3411: 		return status;
 3412: 	status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
 3413: 	if (status != ISC_R_SUCCESS)
 3414: 		return status;
 3415: 	status = (omapi_connection_put_uint32
 3416: 		  (c, (u_int32_t)s -> me.max_response_delay));
 3417: 	if (status != ISC_R_SUCCESS)
 3418: 		return status;
 3419: 	
 3420: 	status = omapi_connection_put_name (c, "cur-unacked-updates");
 3421: 	if (status != ISC_R_SUCCESS)
 3422: 		return status;
 3423: 	status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
 3424: 	if (status != ISC_R_SUCCESS)
 3425: 		return status;
 3426: 	status = (omapi_connection_put_uint32
 3427: 		  (c, (u_int32_t)s -> cur_unacked_updates));
 3428: 	if (status != ISC_R_SUCCESS)
 3429: 		return status;
 3430: 
 3431: 	if (h -> inner && h -> inner -> type -> stuff_values)
 3432: 		return (*(h -> inner -> type -> stuff_values)) (c, id,
 3433: 								h -> inner);
 3434: 	return ISC_R_SUCCESS;
 3435: }
 3436: 
 3437: isc_result_t dhcp_failover_state_lookup (omapi_object_t **sp,
 3438: 					 omapi_object_t *id,
 3439: 					 omapi_object_t *ref)
 3440: {
 3441: 	omapi_value_t *tv = (omapi_value_t *)0;
 3442: 	isc_result_t status;
 3443: 	dhcp_failover_state_t *s;
 3444: 
 3445: 	if (!ref)
 3446: 		return ISC_R_NOKEYS;
 3447: 
 3448: 	/* First see if we were sent a handle. */
 3449: 	status = omapi_get_value_str (ref, id, "handle", &tv);
 3450: 	if (status == ISC_R_SUCCESS) {
 3451: 		status = omapi_handle_td_lookup (sp, tv -> value);
 3452: 
 3453: 		omapi_value_dereference (&tv, MDL);
 3454: 		if (status != ISC_R_SUCCESS)
 3455: 			return status;
 3456: 
 3457: 		/* Don't return the object if the type is wrong. */
 3458: 		if ((*sp) -> type != dhcp_type_failover_state) {
 3459: 			omapi_object_dereference (sp, MDL);
 3460: 			return ISC_R_INVALIDARG;
 3461: 		}
 3462: 	}
 3463: 
 3464: 	/* Look the failover state up by peer name. */
 3465: 	status = omapi_get_value_str (ref, id, "name", &tv);
 3466: 	if (status == ISC_R_SUCCESS) {
 3467: 		for (s = failover_states; s; s = s -> next) {
 3468: 			unsigned l = strlen (s -> name);
 3469: 			if (l == tv -> value -> u.buffer.len &&
 3470: 			    !memcmp (s -> name,
 3471: 				     tv -> value -> u.buffer.value, l))
 3472: 				break;
 3473: 		}
 3474: 		omapi_value_dereference (&tv, MDL);
 3475: 
 3476: 		/* If we already have a lease, and it's not the same one,
 3477: 		   then the query was invalid. */
 3478: 		if (*sp && *sp != (omapi_object_t *)s) {
 3479: 			omapi_object_dereference (sp, MDL);
 3480: 			return ISC_R_KEYCONFLICT;
 3481: 		} else if (!s) {
 3482: 			if (*sp)
 3483: 				omapi_object_dereference (sp, MDL);
 3484: 			return ISC_R_NOTFOUND;
 3485: 		} else if (!*sp)
 3486: 			/* XXX fix so that hash lookup itself creates
 3487: 			   XXX the reference. */
 3488: 			omapi_object_reference (sp, (omapi_object_t *)s, MDL);
 3489: 	}
 3490: 
 3491: 	/* If we get to here without finding a lease, no valid key was
 3492: 	   specified. */
 3493: 	if (!*sp)
 3494: 		return ISC_R_NOKEYS;
 3495: 	return ISC_R_SUCCESS;
 3496: }
 3497: 
 3498: isc_result_t dhcp_failover_state_create (omapi_object_t **sp,
 3499: 					 omapi_object_t *id)
 3500: {
 3501: 	return ISC_R_NOTIMPLEMENTED;
 3502: }
 3503: 
 3504: isc_result_t dhcp_failover_state_remove (omapi_object_t *sp,
 3505: 					 omapi_object_t *id)
 3506: {
 3507: 	return ISC_R_NOTIMPLEMENTED;
 3508: }
 3509: 
 3510: int dhcp_failover_state_match (dhcp_failover_state_t *state,
 3511: 			       u_int8_t *addr, unsigned addrlen)
 3512: {
 3513: 	struct data_string ds;
 3514: 	int i;
 3515: 	
 3516: 	memset (&ds, 0, sizeof ds);
 3517: 	if (evaluate_option_cache (&ds, (struct packet *)0,
 3518: 				   (struct lease *)0,
 3519: 				   (struct client_state *)0,
 3520: 				   (struct option_state *)0,
 3521: 				   (struct option_state *)0,
 3522: 				   &global_scope,
 3523: 				   state -> partner.address, MDL)) {
 3524: 		for (i = 0; i + addrlen - 1 < ds.len; i += addrlen) {
 3525: 			if (!memcmp (&ds.data [i],
 3526: 				     addr, addrlen)) {
 3527: 				data_string_forget (&ds, MDL);
 3528: 				return 1;
 3529: 			}
 3530: 		}
 3531: 		data_string_forget (&ds, MDL);
 3532: 	}
 3533: 	return 0;
 3534: }
 3535: 
 3536: int
 3537: dhcp_failover_state_match_by_name(state, name)
 3538: 	dhcp_failover_state_t *state;
 3539: 	failover_option_t *name;
 3540: {
 3541: 	if ((strlen(state->name) == name->count) &&
 3542: 	    (memcmp(state->name, name->data, name->count) == 0))
 3543: 		return 1;
 3544: 
 3545: 	return 0;
 3546: }
 3547: 
 3548: const char *dhcp_failover_reject_reason_print (int reason)
 3549: {
 3550:     static char resbuf[sizeof("Undefined-255: This reason code is not defined "
 3551: 			      "in the protocol standard.")];
 3552: 
 3553:     if ((reason > 0xff) || (reason < 0))
 3554: 	return "Reason code out of range.";
 3555: 
 3556:     switch (reason) {
 3557:       case FTR_ILLEGAL_IP_ADDR:
 3558: 	return "Illegal IP address (not part of any address pool).";
 3559: 
 3560:       case FTR_FATAL_CONFLICT:
 3561: 	return "Fatal conflict exists: address in use by other client.";
 3562: 
 3563:       case FTR_MISSING_BINDINFO:
 3564: 	return "Missing binding information.";
 3565: 
 3566:       case FTR_TIMEMISMATCH:
 3567: 	return "Connection rejected, time mismatch too great.";
 3568: 
 3569:       case FTR_INVALID_MCLT:
 3570: 	return "Connection rejected, invalid MCLT.";
 3571: 
 3572:       case FTR_MISC_REJECT:
 3573: 	return "Connection rejected, unknown reason.";
 3574: 
 3575:       case FTR_DUP_CONNECTION:
 3576: 	return "Connection rejected, duplicate connection.";
 3577: 
 3578:       case FTR_INVALID_PARTNER:
 3579: 	return "Connection rejected, invalid failover partner.";
 3580: 
 3581:       case FTR_TLS_UNSUPPORTED:
 3582: 	return "TLS not supported.";
 3583: 
 3584:       case FTR_TLS_UNCONFIGURED:
 3585: 	return "TLS supported but not configured.";
 3586: 
 3587:       case FTR_TLS_REQUIRED:
 3588: 	return "TLS required but not supported by partner.";
 3589: 
 3590:       case FTR_DIGEST_UNSUPPORTED:
 3591: 	return "Message digest not supported.";
 3592: 
 3593:       case FTR_DIGEST_UNCONFIGURED:
 3594: 	return "Message digest not configured.";
 3595: 
 3596:       case FTR_VERSION_MISMATCH:
 3597: 	return "Protocol version mismatch.";
 3598: 
 3599:       case FTR_OUTDATED_BIND_INFO:
 3600: 	return "Outdated binding information.";
 3601: 
 3602:       case FTR_LESS_CRIT_BIND_INFO:
 3603: 	return "Less critical binding information.";
 3604: 
 3605:       case FTR_NO_TRAFFIC:
 3606: 	return "No traffic within sufficient time.";
 3607: 
 3608:       case FTR_HBA_CONFLICT:
 3609: 	return "Hash bucket assignment conflict.";
 3610: 
 3611:       case FTR_IP_NOT_RESERVED:
 3612: 	return "IP not reserved on this server.";
 3613: 
 3614:       case FTR_IP_DIGEST_FAILURE:
 3615: 	return "Message digest failed to compare.";
 3616: 
 3617:       case FTR_IP_MISSING_DIGEST:
 3618: 	return "Missing message digest.";
 3619: 
 3620:       case FTR_UNKNOWN:
 3621: 	return "Unknown Error.";
 3622: 
 3623:       default:
 3624: 	sprintf(resbuf, "Undefined-%d: This reason code is not defined in the "
 3625: 			"protocol standard.", reason);
 3626: 	return resbuf;
 3627:     }
 3628: }
 3629: 
 3630: const char *dhcp_failover_state_name_print (enum failover_state state)
 3631: {
 3632: 	switch (state) {
 3633: 	      default:
 3634: 	      case unknown_state:
 3635: 		return "unknown-state";
 3636: 
 3637: 	      case partner_down:
 3638: 		return "partner-down";
 3639: 
 3640: 	      case normal:
 3641: 		return "normal";
 3642: 
 3643: 	      case conflict_done:
 3644: 		return "conflict-done";
 3645: 
 3646: 	      case communications_interrupted:
 3647: 		return "communications-interrupted";
 3648: 
 3649: 	      case resolution_interrupted:
 3650: 		return "resolution-interrupted";
 3651: 
 3652: 	      case potential_conflict:
 3653: 		return "potential-conflict";
 3654: 
 3655: 	      case recover:
 3656: 		return "recover";
 3657: 
 3658: 	      case recover_done:
 3659: 		return "recover-done";
 3660: 
 3661: 	      case recover_wait:
 3662: 		return "recover-wait";
 3663: 
 3664: 	      case shut_down:
 3665: 		return "shutdown";
 3666: 
 3667: 	      case paused:
 3668: 		return "paused";
 3669: 
 3670: 	      case startup:
 3671: 		return "startup";
 3672: 	}
 3673: }
 3674: 
 3675: const char *dhcp_failover_message_name (unsigned type)
 3676: {
 3677: 	static char messbuf[sizeof("unknown-message-255")];
 3678: 
 3679: 	if (type > 0xff)
 3680: 		return "invalid-message";
 3681: 
 3682: 	switch (type) {
 3683: 	      case FTM_POOLREQ:
 3684: 		return "pool-request";
 3685: 		
 3686: 	      case FTM_POOLRESP:
 3687: 		return "pool-response";
 3688: 
 3689: 	      case FTM_BNDUPD:
 3690: 		return "bind-update";
 3691: 
 3692: 	      case FTM_BNDACK:
 3693: 		return "bind-ack";
 3694: 
 3695: 	      case FTM_CONNECT:
 3696: 		return "connect";
 3697: 
 3698: 	      case FTM_CONNECTACK:
 3699: 		return "connect-ack";
 3700: 
 3701: 	      case FTM_UPDREQ:
 3702: 		return "update-request";
 3703: 
 3704: 	      case FTM_UPDDONE:
 3705: 		return "update-done";
 3706: 
 3707: 	      case FTM_UPDREQALL:
 3708: 		return "update-request-all";
 3709: 
 3710: 	      case FTM_STATE:
 3711: 		return "state";
 3712: 
 3713: 	      case FTM_CONTACT:
 3714: 		return "contact";
 3715: 
 3716: 	      case FTM_DISCONNECT:
 3717: 		return "disconnect";
 3718: 
 3719: 	      default:
 3720: 		sprintf(messbuf, "unknown-message-%u", type);
 3721: 		return messbuf;
 3722: 	}
 3723: }
 3724: 
 3725: const char *dhcp_failover_option_name (unsigned type)
 3726: {
 3727: 	static char optbuf[sizeof("unknown-option-65535")];
 3728: 
 3729: 	if (type > 0xffff)
 3730: 		return "invalid-option";
 3731: 
 3732: 	switch (type) {
 3733: 	    case FTO_ADDRESSES_TRANSFERRED:
 3734: 		return "addresses-transferred";
 3735: 
 3736: 	    case FTO_ASSIGNED_IP_ADDRESS:
 3737: 		return "assigned-ip-address";
 3738: 
 3739: 	    case FTO_BINDING_STATUS:
 3740: 		return "binding-status";
 3741: 
 3742: 	    case FTO_CLIENT_IDENTIFIER:
 3743: 		return "client-identifier";
 3744: 
 3745: 	    case FTO_CHADDR:
 3746: 		return "chaddr";
 3747: 
 3748: 	    case FTO_CLTT:
 3749: 		return "cltt";
 3750: 
 3751: 	    case FTO_DDNS:
 3752: 		return "ddns";
 3753: 
 3754: 	    case FTO_DELAYED_SERVICE:
 3755: 		return "delayed-service";
 3756: 
 3757: 	    case FTO_HBA:
 3758: 		return "hba";
 3759: 
 3760: 	    case FTO_IP_FLAGS:
 3761: 		return "ip-flags";
 3762: 
 3763: 	    case FTO_LEASE_EXPIRY:
 3764: 		return "lease-expiry";
 3765: 
 3766: 	    case FTO_MAX_UNACKED:
 3767: 		return "max-unacked";
 3768: 
 3769: 	    case FTO_MCLT:
 3770: 		return "mclt";
 3771: 
 3772: 	    case FTO_MESSAGE:
 3773: 		return "message";
 3774: 
 3775: 	    case FTO_MESSAGE_DIGEST:
 3776: 		return "message-digest";
 3777: 
 3778: 	    case FTO_POTENTIAL_EXPIRY:
 3779: 		return "potential-expiry";
 3780: 
 3781: 	    case FTO_PROTOCOL_VERSION:
 3782: 		return "protocol-version";
 3783: 
 3784: 	    case FTO_RECEIVE_TIMER:
 3785: 		return "receive-timer";
 3786: 
 3787: 	    case FTO_REJECT_REASON:
 3788: 		return "reject-reason";
 3789: 
 3790: 	    case FTO_RELATIONSHIP_NAME:
 3791: 		return "relationship-name";
 3792: 
 3793: 	    case FTO_REPLY_OPTIONS:
 3794: 		return "reply-options";
 3795: 
 3796: 	    case FTO_REQUEST_OPTIONS:
 3797: 		return "request-options";
 3798: 
 3799: 	    case FTO_SERVER_FLAGS:
 3800: 		return "server-flags";
 3801: 
 3802: 	    case FTO_SERVER_STATE:
 3803: 		return "server-state";
 3804: 
 3805: 	    case FTO_STOS:
 3806: 		return "stos";
 3807: 
 3808: 	    case FTO_TLS_REPLY:
 3809: 		return "tls-reply";
 3810: 
 3811: 	    case FTO_TLS_REQUEST:
 3812: 		return "tls-request";
 3813: 
 3814: 	    case FTO_VENDOR_CLASS:
 3815: 		return "vendor-class";
 3816: 
 3817: 	    case FTO_VENDOR_OPTIONS:
 3818: 		return "vendor-options";
 3819: 
 3820: 	    default:
 3821: 		sprintf(optbuf, "unknown-option-%u", type);
 3822: 		return optbuf;
 3823: 	}
 3824: }
 3825: 
 3826: failover_option_t *dhcp_failover_option_printf (unsigned code,
 3827: 						char *obuf,
 3828: 						unsigned *obufix,
 3829: 						unsigned obufmax,
 3830: 						const char *fmt, ...)
 3831: {
 3832: 	va_list va;
 3833: 	char tbuf [256];
 3834: 
 3835: 	/* %Audit% Truncation causes panic. %2004.06.17,Revisit%
 3836: 	 * It is unclear what the effects of truncation here are, or
 3837: 	 * how that condition should be handled.  It seems that this
 3838: 	 * function is used for formatting messages in the failover
 3839: 	 * command channel.  For now the safest thing is for
 3840: 	 * overflow-truncation to cause a fatal log.
 3841: 	 */
 3842: 	va_start (va, fmt);
 3843: 	if (vsnprintf (tbuf, sizeof tbuf, fmt, va) >= sizeof tbuf)
 3844: 		log_fatal ("%s: vsnprintf would truncate",
 3845: 				"dhcp_failover_make_option");
 3846: 	va_end (va);
 3847: 
 3848: 	return dhcp_failover_make_option (code, obuf, obufix, obufmax,
 3849: 					  strlen (tbuf), tbuf);
 3850: }
 3851: 
 3852: failover_option_t *dhcp_failover_make_option (unsigned code,
 3853: 					      char *obuf, unsigned *obufix,
 3854: 					      unsigned obufmax, ...)
 3855: {
 3856: 	va_list va;
 3857: 	struct failover_option_info *info;
 3858: 	int i;
 3859: 	unsigned size, count;
 3860: 	unsigned val;
 3861: 	u_int8_t *iaddr;
 3862: 	unsigned ilen = 0;
 3863: 	u_int8_t *bval;
 3864: 	char *txt = NULL;
 3865: #if defined (DEBUG_FAILOVER_MESSAGES)
 3866: 	char tbuf [256];
 3867: #endif
 3868: 
 3869: 	/* Note that the failover_option structure is used differently on
 3870: 	   input than on output - on input, count is an element count, and
 3871: 	   on output it's the number of bytes total in the option, including
 3872: 	   the option code and option length. */
 3873: 	failover_option_t option, *op;
 3874: 
 3875: 
 3876: 	/* Bogus option code? */
 3877: 	if (code < 1 || code > FTO_MAX || ft_options [code].type == FT_UNDEF) {
 3878: 		return &null_failover_option;
 3879: 	}
 3880: 	info = &ft_options [code];
 3881: 		
 3882: 	va_start (va, obufmax);
 3883: 
 3884: 	/* Get the number of elements and the size of the buffer we need
 3885: 	   to allocate. */
 3886: 	if (info -> type == FT_DDNS || info -> type == FT_DDNS1) {
 3887: 		count = info -> type == FT_DDNS ? 1 : 2;
 3888: 		size = va_arg (va, int) + count;
 3889: 	} else {
 3890: 		/* Find out how many items in this list. */
 3891: 		if (info -> num_present)
 3892: 			count = info -> num_present;
 3893: 		else
 3894: 			count = va_arg (va, int);
 3895: 
 3896: 		/* Figure out size. */
 3897: 		switch (info -> type) {
 3898: 		      case FT_UINT8:
 3899: 		      case FT_BYTES:
 3900: 		      case FT_DIGEST:
 3901: 			size = count;
 3902: 			break;
 3903: 
 3904: 		      case FT_TEXT_OR_BYTES:
 3905: 		      case FT_TEXT:
 3906: 			txt = va_arg (va, char *);
 3907: 			size = count;
 3908: 			break;
 3909: 
 3910: 		      case FT_IPADDR:
 3911: 			ilen = va_arg (va, unsigned);
 3912: 			size = count * ilen;
 3913: 			break;
 3914: 
 3915: 		      case FT_UINT32:
 3916: 			size = count * 4;
 3917: 			break;
 3918: 
 3919: 		      case FT_UINT16:
 3920: 			size = count * 2;
 3921: 			break;
 3922: 
 3923: 		      default:
 3924: 			/* shouldn't get here. */
 3925: 			log_fatal ("bogus type in failover_make_option: %d",
 3926: 				   info -> type);
 3927: 			return &null_failover_option;
 3928: 		}
 3929: 	}
 3930: 	
 3931: 	size += 4;
 3932: 
 3933: 	/* Allocate a buffer for the option. */
 3934: 	option.count = size;
 3935: 	option.data = dmalloc (option.count, MDL);
 3936: 	if (!option.data) {
 3937: 		va_end (va);
 3938: 		return &null_failover_option;
 3939: 	}
 3940: 
 3941: 	/* Put in the option code and option length. */
 3942: 	putUShort (option.data, code);
 3943: 	putUShort (&option.data [2], size - 4);
 3944: 
 3945: #if defined (DEBUG_FAILOVER_MESSAGES)	
 3946: 	/* %Audit% Truncation causes panic. %2004.06.17,Revisit%
 3947: 	 * It is unclear what the effects of truncation here are, or
 3948: 	 * how that condition should be handled.  It seems that this
 3949: 	 * message may be sent over the failover command channel.
 3950: 	 * For now the safest thing is for overflow-truncation to cause
 3951: 	 * a fatal log.
 3952: 	 */
 3953: 	if (snprintf (tbuf, sizeof tbuf, " (%s<%d>", info -> name,
 3954: 			option.count) >= sizeof tbuf)
 3955: 		log_fatal ("dhcp_failover_make_option: tbuf overflow");
 3956: 	failover_print (obuf, obufix, obufmax, tbuf);
 3957: #endif
 3958: 
 3959: 	/* Now put in the data. */
 3960: 	switch (info -> type) {
 3961: 	      case FT_UINT8:
 3962: 		for (i = 0; i < count; i++) {
 3963: 			val = va_arg (va, unsigned);
 3964: #if defined (DEBUG_FAILOVER_MESSAGES)
 3965: 			/* %Audit% Cannot exceed 24 bytes. %2004.06.17,Safe% */
 3966: 			sprintf (tbuf, " %d", val);
 3967: 			failover_print (obuf, obufix, obufmax, tbuf);
 3968: #endif
 3969: 			option.data [i + 4] = val;
 3970: 		}
 3971: 		break;
 3972: 
 3973: 	      case FT_IPADDR:
 3974: 		for (i = 0; i < count; i++) {
 3975: 			iaddr = va_arg (va, u_int8_t *);
 3976: 			if (ilen != 4) {
 3977: 				dfree (option.data, MDL);
 3978: 				log_error ("IP addrlen=%d, should be 4.",
 3979: 					   ilen);
 3980: 				va_end (va);
 3981: 				return &null_failover_option;
 3982: 			}
 3983: 				
 3984: #if defined (DEBUG_FAILOVER_MESSAGES)
 3985: 			/*%Audit% Cannot exceed 17 bytes.  %2004.06.17,Safe%*/
 3986: 			sprintf (tbuf, " %u.%u.%u.%u",
 3987: 				  iaddr [0], iaddr [1], iaddr [2], iaddr [3]);
 3988: 			failover_print (obuf, obufix, obufmax, tbuf);
 3989: #endif
 3990: 			memcpy (&option.data [4 + i * ilen], iaddr, ilen);
 3991: 		}
 3992: 		break;
 3993: 
 3994: 	      case FT_UINT32:
 3995: 		for (i = 0; i < count; i++) {
 3996: 			val = va_arg (va, unsigned);
 3997: #if defined (DEBUG_FAILOVER_MESSAGES)
 3998: 			/*%Audit% Cannot exceed 24 bytes.  %2004.06.17,Safe%*/
 3999: 			sprintf (tbuf, " %d", val);
 4000: 			failover_print (obuf, obufix, obufmax, tbuf);
 4001: #endif
 4002: 			putULong (&option.data [4 + i * 4], val);
 4003: 		}
 4004: 		break;
 4005: 
 4006: 	      case FT_BYTES:
 4007: 	      case FT_DIGEST:
 4008: 		bval = va_arg (va, u_int8_t *);
 4009: #if defined (DEBUG_FAILOVER_MESSAGES)
 4010: 		for (i = 0; i < count; i++) {
 4011: 			/* 23 bytes plus nul, safe. */
 4012: 			sprintf (tbuf, " %d", bval [i]);
 4013: 			failover_print (obuf, obufix, obufmax, tbuf);
 4014: 		}
 4015: #endif
 4016: 		memcpy (&option.data [4], bval, count);
 4017: 		break;
 4018: 
 4019: 		/* On output, TEXT_OR_BYTES is _always_ text, and always NUL
 4020: 		   terminated.  Note that the caller should be careful not
 4021: 		   to provide a format and data that amount to more than 256
 4022: 		   bytes of data, since it will cause a fatal error. */
 4023: 	      case FT_TEXT_OR_BYTES:
 4024: 	      case FT_TEXT:
 4025: #if defined (DEBUG_FAILOVER_MESSAGES)
 4026: 		/* %Audit% Truncation causes panic. %2004.06.17,Revisit%
 4027: 		 * It is unclear what the effects of truncation here are, or
 4028: 		 * how that condition should be handled.  It seems that this
 4029: 		 * function is used for formatting messages in the failover
 4030: 		 * command channel.  For now the safest thing is for
 4031: 		 * overflow-truncation to cause a fatal log.
 4032: 		 */
 4033: 		if (snprintf (tbuf, sizeof tbuf, "\"%s\"", txt) >= sizeof tbuf)
 4034: 			log_fatal ("dhcp_failover_make_option: tbuf overflow");
 4035: 		failover_print (obuf, obufix, obufmax, tbuf);
 4036: #endif
 4037: 		memcpy (&option.data [4], txt, count);
 4038: 		break;
 4039: 
 4040: 	      case FT_DDNS:
 4041: 	      case FT_DDNS1:
 4042: 		option.data [4] = va_arg (va, unsigned);
 4043: 		if (count == 2)
 4044: 			option.data [5] = va_arg (va, unsigned);
 4045: 		bval = va_arg (va, u_int8_t *);
 4046: 		memcpy (&option.data [4 + count], bval, size - count - 4);
 4047: #if defined (DEBUG_FAILOVER_MESSAGES)
 4048: 		for (i = 4; i < size; i++) {
 4049: 			/*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
 4050: 			sprintf (tbuf, " %d", option.data [i]);
 4051: 			failover_print (obuf, obufix, obufmax, tbuf);
 4052: 		}
 4053: #endif
 4054: 		break;
 4055: 
 4056: 	      case FT_UINT16:
 4057: 		for (i = 0; i < count; i++) {
 4058: 			val = va_arg (va, u_int32_t);
 4059: #if defined (DEBUG_FAILOVER_MESSAGES)
 4060: 			/*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
 4061: 			sprintf (tbuf, " %d", val);
 4062: 			failover_print (obuf, obufix, obufmax, tbuf);
 4063: #endif
 4064: 			putUShort (&option.data [4 + i * 2], val);
 4065: 		}
 4066: 		break;
 4067: 
 4068: 	      case FT_UNDEF:
 4069: 	      default:
 4070: 		break;
 4071: 	}
 4072: 
 4073: #if defined DEBUG_FAILOVER_MESSAGES
 4074: 	failover_print (obuf, obufix, obufmax, ")");
 4075: #endif
 4076: 	va_end (va);
 4077: 
 4078: 	/* Now allocate a place to store what we just set up. */
 4079: 	op = dmalloc (sizeof (failover_option_t), MDL);
 4080: 	if (!op) {
 4081: 		dfree (option.data, MDL);
 4082: 		return &null_failover_option;
 4083: 	}
 4084: 
 4085: 	*op = option;
 4086: 	return op;
 4087: }
 4088: 
 4089: /* Send a failover message header. */
 4090: 
 4091: isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link,
 4092: 					omapi_object_t *connection,
 4093: 					int msg_type, u_int32_t xid, ...)
 4094: {
 4095: 	unsigned size = 0;
 4096: 	int bad_option = 0;
 4097: 	int opix = 0;
 4098: 	va_list list;
 4099: 	failover_option_t *option;
 4100: 	unsigned char *opbuf;
 4101: 	isc_result_t status = ISC_R_SUCCESS;
 4102: 	unsigned char cbuf;
 4103: 	struct timeval tv;
 4104: 
 4105: 	/* Run through the argument list once to compute the length of
 4106: 	   the option portion of the message. */
 4107: 	va_start (list, xid);
 4108: 	while ((option = va_arg (list, failover_option_t *))) {
 4109: 		if (option != &skip_failover_option)
 4110: 			size += option -> count;
 4111: 		if (option == &null_failover_option)
 4112: 			bad_option = 1;
 4113: 	}
 4114: 	va_end (list);
 4115: 
 4116: 	/* Allocate an option buffer, unless we got an error. */
 4117: 	if (!bad_option && size) {
 4118: 		opbuf = dmalloc (size, MDL);
 4119: 		if (!opbuf)
 4120: 			status = ISC_R_NOMEMORY;
 4121: 	} else
 4122: 		opbuf = (unsigned char *)0;
 4123: 
 4124: 	va_start (list, xid);
 4125: 	while ((option = va_arg (list, failover_option_t *))) {
 4126: 		if (option == &skip_failover_option)
 4127: 		    continue;
 4128: 		if (!bad_option && opbuf)
 4129: 			memcpy (&opbuf [opix],
 4130: 				option -> data, option -> count);
 4131: 		if (option != &null_failover_option &&
 4132: 		    option != &skip_failover_option) {
 4133: 			opix += option -> count;
 4134: 			dfree (option -> data, MDL);
 4135: 			dfree (option, MDL);
 4136: 		}
 4137: 	}
 4138: 	va_end(list);
 4139: 
 4140: 	if (bad_option)
 4141: 		return ISC_R_INVALIDARG;
 4142: 
 4143: 	/* Now send the message header. */
 4144: 
 4145: 	/* Message length. */
 4146: 	status = omapi_connection_put_uint16 (connection, size + 12);
 4147: 	if (status != ISC_R_SUCCESS)
 4148: 		goto err;
 4149: 
 4150: 	/* Message type. */
 4151: 	cbuf = msg_type;
 4152: 	status = omapi_connection_copyin (connection, &cbuf, 1);
 4153: 	if (status != ISC_R_SUCCESS)
 4154: 		goto err;
 4155: 
 4156: 	/* Payload offset. */
 4157: 	cbuf = 12;
 4158: 	status = omapi_connection_copyin (connection, &cbuf, 1);
 4159: 	if (status != ISC_R_SUCCESS)
 4160: 		goto err;
 4161: 
 4162: 	/* Current time. */
 4163: 	status = omapi_connection_put_uint32 (connection, (u_int32_t)cur_time);
 4164: 	if (status != ISC_R_SUCCESS)
 4165: 		goto err;
 4166: 
 4167: 	/* Transaction ID. */
 4168: 	status = omapi_connection_put_uint32(connection, xid);
 4169: 	if (status != ISC_R_SUCCESS)
 4170: 		goto err;
 4171: 
 4172: 	/* Payload. */
 4173: 	if (opbuf) {
 4174: 		status = omapi_connection_copyin (connection, opbuf, size);
 4175: 		if (status != ISC_R_SUCCESS)
 4176: 			goto err;
 4177: 		dfree (opbuf, MDL);
 4178: 	}
 4179: 	if (link -> state_object &&
 4180: 	    link -> state_object -> link_to_peer == link) {
 4181: #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
 4182: 		log_info ("add_timeout +%d %s",
 4183: 			  (int)(link -> state_object ->
 4184: 				partner.max_response_delay) / 3,
 4185: 			  "dhcp_failover_send_contact");
 4186: #endif
 4187: 		tv . tv_sec = cur_time +
 4188: 			(int)(link -> state_object ->
 4189: 			      partner.max_response_delay) / 3;
 4190: 		tv . tv_usec = 0;
 4191: 		add_timeout (&tv,
 4192: 			     dhcp_failover_send_contact, link -> state_object,
 4193: 			     (tvref_t)dhcp_failover_state_reference,
 4194: 			     (tvunref_t)dhcp_failover_state_dereference);
 4195: 	}
 4196: 	return status;
 4197: 
 4198:       err:
 4199: 	if (opbuf)
 4200: 		dfree (opbuf, MDL);
 4201: 	log_info ("dhcp_failover_put_message: something went wrong.");
 4202: 	omapi_disconnect (connection, 1);
 4203: 	return status;
 4204: }
 4205: 
 4206: void dhcp_failover_timeout (void *vstate)
 4207: {
 4208: 	dhcp_failover_state_t *state = vstate;
 4209: 	dhcp_failover_link_t *link;
 4210: 
 4211: #if defined (DEBUG_FAILOVER_TIMING)
 4212: 	log_info ("dhcp_failover_timeout");
 4213: #endif
 4214: 
 4215: 	if (!state || state -> type != dhcp_type_failover_state)
 4216: 		return;
 4217: 	link = state -> link_to_peer;
 4218: 	if (!link ||
 4219: 	    !link -> outer ||
 4220: 	    link -> outer -> type != omapi_type_connection)
 4221: 		return;
 4222: 
 4223: 	log_error ("timeout waiting for failover peer %s", state -> name);
 4224: 
 4225: 	/* If we haven't gotten a timely response, blow away the connection.
 4226: 	   This will cause the state to change automatically. */
 4227: 	omapi_disconnect (link -> outer, 1);
 4228: }
 4229: 
 4230: void dhcp_failover_send_contact (void *vstate)
 4231: {
 4232: 	dhcp_failover_state_t *state = vstate;
 4233: 	dhcp_failover_link_t *link;
 4234: 	isc_result_t status;
 4235: 
 4236: #if defined(DEBUG_FAILOVER_MESSAGES) && \
 4237:     defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
 4238: 	char obuf [64];
 4239: 	unsigned obufix = 0;
 4240: 
 4241: 	failover_print(obuf, &obufix, sizeof(obuf), "(contact");
 4242: #endif
 4243: 
 4244: #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
 4245: 	log_info ("dhcp_failover_send_contact");
 4246: #endif
 4247: 
 4248: 	if (!state || state -> type != dhcp_type_failover_state)
 4249: 		return;
 4250: 	link = state -> link_to_peer;
 4251: 	if (!link ||
 4252: 	    !link -> outer ||
 4253: 	    link -> outer -> type != omapi_type_connection)
 4254: 		return;
 4255: 
 4256: 	status = (dhcp_failover_put_message
 4257: 		  (link, link -> outer,
 4258: 		   FTM_CONTACT, link->xid++,
 4259: 		   (failover_option_t *)0));
 4260: 
 4261: #if defined(DEBUG_FAILOVER_MESSAGES) && \
 4262:     defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
 4263: 	if (status != ISC_R_SUCCESS)
 4264: 		failover_print(obuf, &obufix, sizeof(obuf), " (failed)");
 4265: 	failover_print(obuf, &obufix, sizeof(obuf), ")");
 4266: 	if (obufix) {
 4267: 		log_debug ("%s", obuf);
 4268: 	}
 4269: #endif
 4270: 	return;
 4271: }
 4272: 
 4273: isc_result_t dhcp_failover_send_state (dhcp_failover_state_t *state)
 4274: {
 4275: 	dhcp_failover_link_t *link;
 4276: 	isc_result_t status;
 4277: 
 4278: #if defined (DEBUG_FAILOVER_MESSAGES)	
 4279: 	char obuf [64];
 4280: 	unsigned obufix = 0;
 4281: 	
 4282: # define FMA obuf, &obufix, sizeof obuf
 4283: 	failover_print (FMA, "(state");
 4284: #else
 4285: # define FMA (char *)0, (unsigned *)0, 0
 4286: #endif
 4287: 
 4288: 	if (!state || state -> type != dhcp_type_failover_state)
 4289: 		return ISC_R_INVALIDARG;
 4290: 	link = state -> link_to_peer;
 4291: 	if (!link ||
 4292: 	    !link -> outer ||
 4293: 	    link -> outer -> type != omapi_type_connection)
 4294: 		return ISC_R_INVALIDARG;
 4295: 
 4296: 	status = (dhcp_failover_put_message
 4297: 		  (link, link -> outer,
 4298: 		   FTM_STATE, link->xid++,
 4299: 		   dhcp_failover_make_option (FTO_SERVER_STATE, FMA,
 4300: 					      (state -> me.state == startup
 4301: 					       ? state -> saved_state
 4302: 					       : state -> me.state)),
 4303: 		   dhcp_failover_make_option
 4304: 		   (FTO_SERVER_FLAGS, FMA,
 4305: 		    (state -> service_state == service_startup
 4306: 		     ? FTF_SERVER_STARTUP : 0)),
 4307: 		   dhcp_failover_make_option (FTO_STOS, FMA, state -> me.stos),
 4308: 		   (failover_option_t *)0));
 4309: 
 4310: #if defined (DEBUG_FAILOVER_MESSAGES)
 4311: 	if (status != ISC_R_SUCCESS)
 4312: 		failover_print (FMA, " (failed)");
 4313: 	failover_print (FMA, ")");
 4314: 	if (obufix) {
 4315: 		log_debug ("%s", obuf);
 4316: 	}
 4317: #endif
 4318: 	return ISC_R_SUCCESS;
 4319: }
 4320: 
 4321: /* Send a connect message. */
 4322: 
 4323: isc_result_t dhcp_failover_send_connect (omapi_object_t *l)
 4324: {
 4325: 	dhcp_failover_link_t *link;
 4326: 	dhcp_failover_state_t *state;
 4327: 	isc_result_t status;
 4328: #if defined (DEBUG_FAILOVER_MESSAGES)	
 4329: 	char obuf [64];
 4330: 	unsigned obufix = 0;
 4331: 	
 4332: # define FMA obuf, &obufix, sizeof obuf
 4333: 	failover_print (FMA, "(connect");
 4334: #else
 4335: # define FMA (char *)0, (unsigned *)0, 0
 4336: #endif
 4337: 
 4338: 	if (!l || l -> type != dhcp_type_failover_link)
 4339: 		return ISC_R_INVALIDARG;
 4340: 	link = (dhcp_failover_link_t *)l;
 4341: 	state = link -> state_object;
 4342: 	if (!l -> outer || l -> outer -> type != omapi_type_connection)
 4343: 		return ISC_R_INVALIDARG;
 4344: 
 4345: 	status =
 4346: 	    (dhcp_failover_put_message
 4347: 	     (link, l -> outer,
 4348: 	      FTM_CONNECT, link->xid++,
 4349: 	      dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA,
 4350: 					strlen(state->name), state->name),
 4351: 	      dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
 4352: 					 state -> me.max_flying_updates),
 4353: 	      dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
 4354: 					 state -> me.max_response_delay),
 4355: 	      dhcp_failover_option_printf(FTO_VENDOR_CLASS, FMA,
 4356: 					  "isc-%s", PACKAGE_VERSION),
 4357: 	      dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
 4358: 					 DHCP_FAILOVER_VERSION),
 4359: 	      dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
 4360: 					 0, 0),
 4361: 	      dhcp_failover_make_option (FTO_MCLT, FMA,
 4362: 					 state -> mclt),
 4363: 	      (state -> hba
 4364: 	       ? dhcp_failover_make_option (FTO_HBA, FMA, 32, state -> hba)
 4365: 	       : &skip_failover_option),
 4366: 	      (failover_option_t *)0));
 4367: 	
 4368: #if defined (DEBUG_FAILOVER_MESSAGES)
 4369: 	if (status != ISC_R_SUCCESS)
 4370: 		failover_print (FMA, " (failed)");
 4371: 	failover_print (FMA, ")");
 4372: 	if (obufix) {
 4373: 		log_debug ("%s", obuf);
 4374: 	}
 4375: #endif
 4376: 	return status;
 4377: }
 4378: 
 4379: isc_result_t dhcp_failover_send_connectack (omapi_object_t *l,
 4380: 					    dhcp_failover_state_t *state,
 4381: 					    int reason, const char *errmsg)
 4382: {
 4383: 	dhcp_failover_link_t *link;
 4384: 	isc_result_t status;
 4385: #if defined (DEBUG_FAILOVER_MESSAGES)	
 4386: 	char obuf [64];
 4387: 	unsigned obufix = 0;
 4388: 	
 4389: # define FMA obuf, &obufix, sizeof obuf
 4390: 	failover_print (FMA, "(connectack");
 4391: #else
 4392: # define FMA (char *)0, (unsigned *)0, 0
 4393: #endif
 4394: 
 4395: 	if (!l || l -> type != dhcp_type_failover_link)
 4396: 		return ISC_R_INVALIDARG;
 4397: 	link = (dhcp_failover_link_t *)l;
 4398: 	if (!l -> outer || l -> outer -> type != omapi_type_connection)
 4399: 		return ISC_R_INVALIDARG;
 4400: 
 4401: 	status =
 4402: 	    (dhcp_failover_put_message
 4403: 	     (link, l -> outer,
 4404: 	      FTM_CONNECTACK, link->imsg->xid,
 4405: 	      state
 4406: 	       ? dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA,
 4407: 					   strlen(state->name), state->name)
 4408: 	       : (link->imsg->options_present & FTB_RELATIONSHIP_NAME)
 4409: 		  ? &link->imsg->relationship_name
 4410: 		  : &skip_failover_option,
 4411: 	      state
 4412: 	       ? dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
 4413: 					    state -> me.max_flying_updates)
 4414: 	       : &skip_failover_option,
 4415: 	      state
 4416: 	       ? dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
 4417: 					    state -> me.max_response_delay)
 4418: 	       : &skip_failover_option,
 4419: 	      dhcp_failover_option_printf(FTO_VENDOR_CLASS, FMA,
 4420: 					  "isc-%s", PACKAGE_VERSION),
 4421: 	      dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
 4422: 					 DHCP_FAILOVER_VERSION),
 4423: 	      (link->imsg->options_present & FTB_TLS_REQUEST)
 4424: 	       ? dhcp_failover_make_option(FTO_TLS_REPLY, FMA,
 4425: 					   0, 0)
 4426: 	       : &skip_failover_option,
 4427: 	      reason
 4428: 	       ? dhcp_failover_make_option (FTO_REJECT_REASON,
 4429: 					    FMA, reason)
 4430: 	       : &skip_failover_option,
 4431: 	      (reason && errmsg)
 4432: 	       ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
 4433: 					    strlen (errmsg), errmsg)
 4434: 	       : &skip_failover_option,
 4435: 	      (failover_option_t *)0));
 4436: 
 4437: #if defined (DEBUG_FAILOVER_MESSAGES)
 4438: 	if (status != ISC_R_SUCCESS)
 4439: 		failover_print (FMA, " (failed)");
 4440: 	failover_print (FMA, ")");
 4441: 	if (obufix) {
 4442: 		log_debug ("%s", obuf);
 4443: 	}
 4444: #endif
 4445: 	return status;
 4446: }
 4447: 
 4448: isc_result_t dhcp_failover_send_disconnect (omapi_object_t *l,
 4449: 					    int reason,
 4450: 					    const char *message)
 4451: {
 4452: 	dhcp_failover_link_t *link;
 4453: 	dhcp_failover_state_t *state;
 4454: 	isc_result_t status;
 4455: #if defined (DEBUG_FAILOVER_MESSAGES)	
 4456: 	char obuf [64];
 4457: 	unsigned obufix = 0;
 4458: 	
 4459: # define FMA obuf, &obufix, sizeof obuf
 4460: 	failover_print (FMA, "(disconnect");
 4461: #else
 4462: # define FMA (char *)0, (unsigned *)0, 0
 4463: #endif
 4464: 
 4465: 	if (!l || l -> type != dhcp_type_failover_link)
 4466: 		return ISC_R_INVALIDARG;
 4467: 	link = (dhcp_failover_link_t *)l;
 4468: 	state = link -> state_object;
 4469: 	if (!l -> outer || l -> outer -> type != omapi_type_connection)
 4470: 		return ISC_R_INVALIDARG;
 4471: 
 4472: 	if (!message && reason)
 4473: 		message = dhcp_failover_reject_reason_print (reason);
 4474: 
 4475: 	status = (dhcp_failover_put_message
 4476: 		  (link, l -> outer,
 4477: 		   FTM_DISCONNECT, link->xid++,
 4478: 		   dhcp_failover_make_option (FTO_REJECT_REASON,
 4479: 					      FMA, reason),
 4480: 		   (message
 4481: 		    ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
 4482: 						 strlen (message), message)
 4483: 		    : &skip_failover_option),
 4484: 		   (failover_option_t *)0));
 4485: 
 4486: #if defined (DEBUG_FAILOVER_MESSAGES)
 4487: 	if (status != ISC_R_SUCCESS)
 4488: 		failover_print (FMA, " (failed)");
 4489: 	failover_print (FMA, ")");
 4490: 	if (obufix) {
 4491: 		log_debug ("%s", obuf);
 4492: 	}
 4493: #endif
 4494: 	return status;
 4495: }
 4496: 
 4497: /* Send a Bind Update message. */
 4498: 
 4499: isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state,
 4500: 					     struct lease *lease)
 4501: {
 4502: 	dhcp_failover_link_t *link;
 4503: 	isc_result_t status;
 4504: 	int flags = 0;
 4505: 	binding_state_t transmit_state;
 4506: #if defined (DEBUG_FAILOVER_MESSAGES)	
 4507: 	char obuf [64];
 4508: 	unsigned obufix = 0;
 4509: 	
 4510: # define FMA obuf, &obufix, sizeof obuf
 4511: 	failover_print (FMA, "(bndupd");
 4512: #else
 4513: # define FMA (char *)0, (unsigned *)0, 0
 4514: #endif
 4515: 
 4516: 	if (!state -> link_to_peer ||
 4517: 	    state -> link_to_peer -> type != dhcp_type_failover_link)
 4518: 		return ISC_R_INVALIDARG;
 4519: 	link = (dhcp_failover_link_t *)state -> link_to_peer;
 4520: 
 4521: 	if (!link -> outer || link -> outer -> type != omapi_type_connection)
 4522: 		return ISC_R_INVALIDARG;
 4523: 
 4524: 	transmit_state = lease->desired_binding_state;
 4525: 	if (lease->flags & RESERVED_LEASE) {
 4526: 		/* If we are listing an allocable (not yet ACTIVE etc) lease
 4527: 		 * as reserved, toggle to the peer's 'free state', per the
 4528: 		 * draft.  This gives the peer permission to alloc it to the
 4529: 		 * chaddr/uid-named client.
 4530: 		 */
 4531: 		if ((state->i_am == primary) && (transmit_state == FTS_FREE))
 4532: 			transmit_state = FTS_BACKUP;
 4533: 		else if ((state->i_am == secondary) &&
 4534: 			 (transmit_state == FTS_BACKUP))
 4535: 			transmit_state = FTS_FREE;
 4536: 
 4537: 		flags |= FTF_IP_FLAG_RESERVE;
 4538: 	}
 4539: 	if (lease->flags & BOOTP_LEASE)
 4540: 		flags |= FTF_IP_FLAG_BOOTP;
 4541: 
 4542: 	/* last_xid == 0 is illegal, seek past zero if we hit it. */
 4543: 	if (link->xid == 0)
 4544: 		link->xid = 1;
 4545: 
 4546: 	lease->last_xid = link->xid++;
 4547: 
 4548: 	/* Send the update. */
 4549: 	status = (dhcp_failover_put_message
 4550: 		  (link, link -> outer,
 4551: 		   FTM_BNDUPD, lease->last_xid,
 4552: 		   dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
 4553: 					      lease -> ip_addr.len,
 4554: 					      lease -> ip_addr.iabuf),
 4555: 		   dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
 4556: 					      lease -> desired_binding_state),
 4557: 		   lease -> uid_len
 4558: 		   ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
 4559: 						lease -> uid_len,
 4560: 						lease -> uid)
 4561: 		   : &skip_failover_option,
 4562: 		   lease -> hardware_addr.hlen
 4563: 		   ? dhcp_failover_make_option (FTO_CHADDR, FMA,
 4564: 						lease -> hardware_addr.hlen,
 4565: 						lease -> hardware_addr.hbuf)
 4566: 		   : &skip_failover_option,
 4567: 		   dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
 4568: 					      lease -> ends),
 4569: 		   dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
 4570: 					      lease -> tstp),
 4571: 		   dhcp_failover_make_option (FTO_STOS, FMA,
 4572: 					      lease -> starts),
 4573: 		   (lease->cltt != 0) ? 
 4574: 			dhcp_failover_make_option(FTO_CLTT, FMA, lease->cltt) :
 4575: 			&skip_failover_option, /* No CLTT */
 4576: 		   flags ? dhcp_failover_make_option(FTO_IP_FLAGS, FMA,
 4577: 						     flags) :
 4578: 			   &skip_failover_option, /* No IP_FLAGS */
 4579: 		   &skip_failover_option,	/* XXX DDNS */
 4580: 		   &skip_failover_option,	/* XXX request options */
 4581: 		   &skip_failover_option,	/* XXX reply options */
 4582: 		   (failover_option_t *)0));
 4583: 
 4584: #if defined (DEBUG_FAILOVER_MESSAGES)
 4585: 	if (status != ISC_R_SUCCESS)
 4586: 		failover_print (FMA, " (failed)");
 4587: 	failover_print (FMA, ")");
 4588: 	if (obufix) {
 4589: 		log_debug ("%s", obuf);
 4590: 	}
 4591: #endif
 4592: 	return status;
 4593: }
 4594: 
 4595: /* Send a Bind ACK message. */
 4596: 
 4597: isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *state,
 4598: 					  failover_message_t *msg,
 4599: 					  int reason, const char *message)
 4600: {
 4601: 	dhcp_failover_link_t *link;
 4602: 	isc_result_t status;
 4603: #if defined (DEBUG_FAILOVER_MESSAGES)	
 4604: 	char obuf [64];
 4605: 	unsigned obufix = 0;
 4606: 	
 4607: # define FMA obuf, &obufix, sizeof obuf
 4608: 	failover_print (FMA, "(bndack");
 4609: #else
 4610: # define FMA (char *)0, (unsigned *)0, 0
 4611: #endif
 4612: 
 4613: 	if (!state -> link_to_peer ||
 4614: 	    state -> link_to_peer -> type != dhcp_type_failover_link)
 4615: 		return ISC_R_INVALIDARG;
 4616: 	link = (dhcp_failover_link_t *)state -> link_to_peer;
 4617: 
 4618: 	if (!link -> outer || link -> outer -> type != omapi_type_connection)
 4619: 		return ISC_R_INVALIDARG;
 4620: 
 4621: 	if (!message && reason)
 4622: 		message = dhcp_failover_reject_reason_print (reason);
 4623: 
 4624: 	/* Send the update. */
 4625: 	status = (dhcp_failover_put_message
 4626: 		  (link, link -> outer,
 4627: 		   FTM_BNDACK, msg->xid,
 4628: 		   dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
 4629: 					      sizeof msg -> assigned_addr,
 4630: 					      &msg -> assigned_addr),
 4631: #ifdef DO_BNDACK_SHOULD_NOT
 4632: 		   dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
 4633: 					      msg -> binding_status),
 4634: 		   (msg -> options_present & FTB_CLIENT_IDENTIFIER)
 4635: 		   ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
 4636: 						msg -> client_identifier.count,
 4637: 						msg -> client_identifier.data)
 4638: 		   : &skip_failover_option,
 4639: 		   (msg -> options_present & FTB_CHADDR)
 4640: 		   ? dhcp_failover_make_option (FTO_CHADDR, FMA,
 4641: 						msg -> chaddr.count,
 4642: 						msg -> chaddr.data)
 4643: 		   : &skip_failover_option,
 4644: 		   dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
 4645: 					      msg -> expiry),
 4646: 		   dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
 4647: 					      msg -> potential_expiry),
 4648: 		   dhcp_failover_make_option (FTO_STOS, FMA,
 4649: 					      msg -> stos),
 4650: 		   (msg->options_present & FTB_CLTT) ?
 4651: 			dhcp_failover_make_option(FTO_CLTT, FMA, msg->cltt) :
 4652: 			&skip_failover_option, /* No CLTT in the msg to ack. */
 4653: 		   ((msg->options_present & FTB_IP_FLAGS) && msg->ip_flags) ? 
 4654: 			dhcp_failover_make_option(FTO_IP_FLAGS, FMA,
 4655: 						  msg->ip_flags)
 4656: 			: &skip_failover_option,
 4657: #endif /* DO_BNDACK_SHOULD_NOT */
 4658: 		   reason
 4659: 		    ? dhcp_failover_make_option(FTO_REJECT_REASON, FMA, reason)
 4660: 		    : &skip_failover_option,
 4661: 		   (reason && message)
 4662: 		    ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
 4663: 						 strlen (message), message)
 4664: 		    : &skip_failover_option,
 4665: #ifdef DO_BNDACK_SHOULD_NOT
 4666: 		   &skip_failover_option,	/* XXX DDNS */
 4667: 		   &skip_failover_option,	/* XXX request options */
 4668: 		   &skip_failover_option,	/* XXX reply options */
 4669: #endif /* DO_BNDACK_SHOULD_NOT */
 4670: 		   (failover_option_t *)0));
 4671: 
 4672: #if defined (DEBUG_FAILOVER_MESSAGES)
 4673: 	if (status != ISC_R_SUCCESS)
 4674: 		failover_print (FMA, " (failed)");
 4675: 	failover_print (FMA, ")");
 4676: 	if (obufix) {
 4677: 		log_debug ("%s", obuf);
 4678: 	}
 4679: #endif
 4680: 	return status;
 4681: }
 4682: 
 4683: isc_result_t dhcp_failover_send_poolreq (dhcp_failover_state_t *state)
 4684: {
 4685: 	dhcp_failover_link_t *link;
 4686: 	isc_result_t status;
 4687: #if defined (DEBUG_FAILOVER_MESSAGES)	
 4688: 	char obuf [64];
 4689: 	unsigned obufix = 0;
 4690: 	
 4691: # define FMA obuf, &obufix, sizeof obuf
 4692: 	failover_print (FMA, "(poolreq");
 4693: #else
 4694: # define FMA (char *)0, (unsigned *)0, 0
 4695: #endif
 4696: 
 4697: 	if (!state -> link_to_peer ||
 4698: 	    state -> link_to_peer -> type != dhcp_type_failover_link)
 4699: 		return ISC_R_INVALIDARG;
 4700: 	link = (dhcp_failover_link_t *)state -> link_to_peer;
 4701: 
 4702: 	if (!link -> outer || link -> outer -> type != omapi_type_connection)
 4703: 		return ISC_R_INVALIDARG;
 4704: 
 4705: 	status = (dhcp_failover_put_message
 4706: 		  (link, link -> outer,
 4707: 		   FTM_POOLREQ, link->xid++,
 4708: 		   (failover_option_t *)0));
 4709: 
 4710: #if defined (DEBUG_FAILOVER_MESSAGES)
 4711: 	if (status != ISC_R_SUCCESS)
 4712: 		failover_print (FMA, " (failed)");
 4713: 	failover_print (FMA, ")");
 4714: 	if (obufix) {
 4715: 		log_debug ("%s", obuf);
 4716: 	}
 4717: #endif
 4718: 	return status;
 4719: }
 4720: 
 4721: isc_result_t dhcp_failover_send_poolresp (dhcp_failover_state_t *state,
 4722: 					  int leases)
 4723: {
 4724: 	dhcp_failover_link_t *link;
 4725: 	isc_result_t status;
 4726: #if defined (DEBUG_FAILOVER_MESSAGES)	
 4727: 	char obuf [64];
 4728: 	unsigned obufix = 0;
 4729: 	
 4730: # define FMA obuf, &obufix, sizeof obuf
 4731: 	failover_print (FMA, "(poolresp");
 4732: #else
 4733: # define FMA (char *)0, (unsigned *)0, 0
 4734: #endif
 4735: 
 4736: 	if (!state -> link_to_peer ||
 4737: 	    state -> link_to_peer -> type != dhcp_type_failover_link)
 4738: 		return ISC_R_INVALIDARG;
 4739: 	link = (dhcp_failover_link_t *)state -> link_to_peer;
 4740: 
 4741: 	if (!link -> outer || link -> outer -> type != omapi_type_connection)
 4742: 		return ISC_R_INVALIDARG;
 4743: 
 4744: 	status = (dhcp_failover_put_message
 4745: 		  (link, link -> outer,
 4746: 		   FTM_POOLRESP, link->imsg->xid,
 4747: 		   dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED, FMA,
 4748: 					      leases),
 4749: 		   (failover_option_t *)0));
 4750: 
 4751: #if defined (DEBUG_FAILOVER_MESSAGES)
 4752: 	if (status != ISC_R_SUCCESS)
 4753: 		failover_print (FMA, " (failed)");
 4754: 	failover_print (FMA, ")");
 4755: 	if (obufix) {
 4756: 		log_debug ("%s", obuf);
 4757: 	}
 4758: #endif
 4759: 	return status;
 4760: }
 4761: 
 4762: isc_result_t dhcp_failover_send_update_request (dhcp_failover_state_t *state)
 4763: {
 4764: 	dhcp_failover_link_t *link;
 4765: 	isc_result_t status;
 4766: #if defined (DEBUG_FAILOVER_MESSAGES)	
 4767: 	char obuf [64];
 4768: 	unsigned obufix = 0;
 4769: 
 4770: # define FMA obuf, &obufix, sizeof obuf
 4771: 	failover_print (FMA, "(updreq");
 4772: #else
 4773: # define FMA (char *)0, (unsigned *)0, 0
 4774: #endif
 4775: 
 4776: 	if (!state -> link_to_peer ||
 4777: 	    state -> link_to_peer -> type != dhcp_type_failover_link)
 4778: 		return ISC_R_INVALIDARG;
 4779: 	link = (dhcp_failover_link_t *)state -> link_to_peer;
 4780: 
 4781: 	if (!link -> outer || link -> outer -> type != omapi_type_connection)
 4782: 		return ISC_R_INVALIDARG;
 4783: 
 4784: 	if (state -> curUPD)
 4785: 		return ISC_R_ALREADYRUNNING;
 4786: 
 4787: 	status = (dhcp_failover_put_message
 4788: 		  (link, link -> outer,
 4789: 		   FTM_UPDREQ, link->xid++,
 4790: 		   (failover_option_t *)0));
 4791: 
 4792: 	if (status == ISC_R_SUCCESS)
 4793: 		state -> curUPD = FTM_UPDREQ;
 4794: 
 4795: #if defined (DEBUG_FAILOVER_MESSAGES)
 4796: 	if (status != ISC_R_SUCCESS)
 4797: 		failover_print (FMA, " (failed)");
 4798: 	failover_print (FMA, ")");
 4799: 	if (obufix) {
 4800: 		log_debug ("%s", obuf);
 4801: 	}
 4802: #endif
 4803: 	log_info ("Sent update request message to %s", state -> name);
 4804: 	return status;
 4805: }
 4806: 
 4807: isc_result_t dhcp_failover_send_update_request_all (dhcp_failover_state_t
 4808: 						    *state)
 4809: {
 4810: 	dhcp_failover_link_t *link;
 4811: 	isc_result_t status;
 4812: #if defined (DEBUG_FAILOVER_MESSAGES)	
 4813: 	char obuf [64];
 4814: 	unsigned obufix = 0;
 4815: 	
 4816: # define FMA obuf, &obufix, sizeof obuf
 4817: 	failover_print (FMA, "(updreqall");
 4818: #else
 4819: # define FMA (char *)0, (unsigned *)0, 0
 4820: #endif
 4821: 
 4822: 	if (!state -> link_to_peer ||
 4823: 	    state -> link_to_peer -> type != dhcp_type_failover_link)
 4824: 		return ISC_R_INVALIDARG;
 4825: 	link = (dhcp_failover_link_t *)state -> link_to_peer;
 4826: 
 4827: 	if (!link -> outer || link -> outer -> type != omapi_type_connection)
 4828: 		return ISC_R_INVALIDARG;
 4829: 
 4830: 	/* If there is an UPDREQ in progress, then upgrade to UPDREQALL. */
 4831: 	if (state -> curUPD && (state -> curUPD != FTM_UPDREQ))
 4832: 		return ISC_R_ALREADYRUNNING;
 4833: 
 4834: 	status = (dhcp_failover_put_message
 4835: 		  (link, link -> outer,
 4836: 		   FTM_UPDREQALL, link->xid++,
 4837: 		   (failover_option_t *)0));
 4838: 
 4839: 	if (status == ISC_R_SUCCESS)
 4840: 		state -> curUPD = FTM_UPDREQALL;
 4841: 
 4842: #if defined (DEBUG_FAILOVER_MESSAGES)
 4843: 	if (status != ISC_R_SUCCESS)
 4844: 		failover_print (FMA, " (failed)");
 4845: 	failover_print (FMA, ")");
 4846: 	if (obufix) {
 4847: 		log_debug ("%s", obuf);
 4848: 	}
 4849: #endif
 4850: 	log_info ("Sent update request all message to %s", state -> name);
 4851: 	return status;
 4852: }
 4853: 
 4854: isc_result_t dhcp_failover_send_update_done (dhcp_failover_state_t *state)
 4855: {
 4856: 	dhcp_failover_link_t *link;
 4857: 	isc_result_t status;
 4858: #if defined (DEBUG_FAILOVER_MESSAGES)	
 4859: 	char obuf [64];
 4860: 	unsigned obufix = 0;
 4861: 	
 4862: # define FMA obuf, &obufix, sizeof obuf
 4863: 	failover_print (FMA, "(upddone");
 4864: #else
 4865: # define FMA (char *)0, (unsigned *)0, 0
 4866: #endif
 4867: 
 4868: 	if (!state -> link_to_peer ||
 4869: 	    state -> link_to_peer -> type != dhcp_type_failover_link)
 4870: 		return ISC_R_INVALIDARG;
 4871: 	link = (dhcp_failover_link_t *)state -> link_to_peer;
 4872: 
 4873: 	if (!link -> outer || link -> outer -> type != omapi_type_connection)
 4874: 		return ISC_R_INVALIDARG;
 4875: 
 4876: 	status = (dhcp_failover_put_message
 4877: 		  (link, link -> outer,
 4878: 		   FTM_UPDDONE, state->updxid,
 4879: 		   (failover_option_t *)0));
 4880: 
 4881: #if defined (DEBUG_FAILOVER_MESSAGES)
 4882: 	if (status != ISC_R_SUCCESS)
 4883: 		failover_print (FMA, " (failed)");
 4884: 	failover_print (FMA, ")");
 4885: 	if (obufix) {
 4886: 		log_debug ("%s", obuf);
 4887: 	}
 4888: #endif
 4889: 
 4890: 	log_info ("Sent update done message to %s", state -> name);
 4891: 
 4892: 	state->updxid--; /* Paranoia, just so it mismatches. */
 4893: 
 4894: 	/* There may be uncommitted leases at this point (since
 4895: 	   dhcp_failover_process_bind_ack() doesn't commit leases);
 4896: 	   commit the lease file. */
 4897: 	commit_leases();
 4898: 
 4899: 	return status;
 4900: }
 4901: 
 4902: /*
 4903:  * failover_lease_is_better() compares the binding update in 'msg' with
 4904:  * the current lease in 'lease'.  If the determination is that the binding
 4905:  * update shouldn't be allowed to update/crush more critical binding info
 4906:  * on the lease, the lease is preferred.  A value of true is returned if the
 4907:  * local lease is preferred, or false if the remote binding update is
 4908:  * preferred.
 4909:  *
 4910:  * For now this function is hopefully simplistic and trivial.  It may be that
 4911:  * a more detailed system of preferences is required, so this is something we
 4912:  * should monitor as we gain experience with these dueling events.
 4913:  */
 4914: static isc_boolean_t
 4915: failover_lease_is_better(dhcp_failover_state_t *state, struct lease *lease,
 4916: 			 failover_message_t *msg)
 4917: {
 4918: 	binding_state_t local_state;
 4919: 	TIME msg_cltt;
 4920: 
 4921: 	if (lease->binding_state != lease->desired_binding_state)
 4922: 		local_state = lease->desired_binding_state;
 4923: 	else
 4924: 		local_state = lease->binding_state;
 4925: 
 4926: 	if ((msg->options_present & FTB_CLTT) != 0)
 4927: 		msg_cltt = msg->cltt;
 4928: 	else
 4929: 		msg_cltt = 0;
 4930: 
 4931: 	switch(local_state) {
 4932: 	      case FTS_ACTIVE:
 4933: 		if (msg->binding_status == FTS_ACTIVE) {
 4934: 			if (msg_cltt < lease->cltt)
 4935: 				return ISC_TRUE;
 4936: 			else if (msg_cltt > lease->cltt)
 4937: 				return ISC_FALSE;
 4938: 			else if (state->i_am == primary)
 4939: 				return ISC_TRUE;
 4940: 			else
 4941: 				return ISC_FALSE;
 4942: 		} else if (msg->binding_status == FTS_EXPIRED) {
 4943: 			return ISC_FALSE;
 4944: 		}
 4945: 		/* FALL THROUGH */
 4946: 
 4947: 	      case FTS_FREE:
 4948: 	      case FTS_BACKUP:
 4949: 	      case FTS_EXPIRED:
 4950: 	      case FTS_RELEASED:
 4951: 	      case FTS_ABANDONED:
 4952: 	      case FTS_RESET:
 4953: 		if (msg->binding_status == FTS_ACTIVE)
 4954: 			return ISC_FALSE;
 4955: 		else if (state->i_am == primary)
 4956: 			return ISC_TRUE;
 4957: 		else
 4958: 			return ISC_FALSE;
 4959: 		/* FALL THROUGH to impossible condition */
 4960: 
 4961: 	      default:
 4962: 		log_fatal("Impossible condition at %s:%d.", MDL);
 4963: 	}
 4964: 
 4965: 	log_fatal("Impossible condition at %s:%d.", MDL);
 4966: 	/* Silence compiler warning. */
 4967: 	return ISC_FALSE;
 4968: }
 4969: 
 4970: isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *state,
 4971: 					       failover_message_t *msg)
 4972: {
 4973: 	struct lease *lt, *lease;
 4974: 	struct iaddr ia;
 4975: 	int reason = FTR_MISC_REJECT;
 4976: 	const char *message;
 4977: 	int new_binding_state;
 4978: 	int send_to_backup = 0;
 4979: 	int required_options;
 4980: 	isc_boolean_t chaddr_changed = ISC_FALSE;
 4981: 	isc_boolean_t ident_changed = ISC_FALSE;
 4982: 
 4983: 	/* Validate the binding update. */
 4984: 	required_options = FTB_ASSIGNED_IP_ADDRESS | FTB_BINDING_STATUS;
 4985: 	if ((msg->options_present & required_options) != required_options) {
 4986: 		message = "binding update lacks required options";
 4987: 		reason = FTR_MISSING_BINDINFO;
 4988: 		goto bad;
 4989: 	}
 4990: 
 4991: 	ia.len = sizeof msg -> assigned_addr;
 4992: 	memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
 4993: 
 4994: 	lease = (struct lease *)0;
 4995: 	lt = (struct lease *)0;
 4996: 	if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
 4997: 		message = "unknown IP address";
 4998: 		reason = FTR_ILLEGAL_IP_ADDR;
 4999: 		goto bad;
 5000: 	}
 5001: 
 5002: 	/*
 5003: 	 * If this lease is covered by a different failover peering
 5004: 	 * relationship, assert an error.
 5005: 	 */
 5006: 	if ((lease->pool == NULL) || (lease->pool->failover_peer == NULL) ||
 5007: 	    (lease->pool->failover_peer != state)) {
 5008: 		message = "IP address is covered by a different failover "
 5009: 			  "relationship state";
 5010: 		reason = FTR_ILLEGAL_IP_ADDR;
 5011: 		goto bad;
 5012: 	}
 5013: 
 5014: 	/*
 5015: 	 * Dueling updates:  This happens when both servers send a BNDUPD
 5016: 	 * at the same time.  We want the best update to win, which means
 5017: 	 * we reject if we think ours is better, or cancel if we think the
 5018: 	 * peer's is better.  We only assert a problem if the lease is on
 5019: 	 * the ACK queue, not on the UPDATE queue.  This means that after
 5020: 	 * accepting this server's BNDUPD, we will send our own BNDUPD
 5021: 	 * /after/ sending the BNDACK (this order was recently enforced in
 5022: 	 * queue processing).
 5023: 	 */
 5024: 	if ((lease->flags & ON_ACK_QUEUE) != 0) {
 5025: 		if (failover_lease_is_better(state, lease, msg)) {
 5026: 			message = "incoming update is less critical than "
 5027: 				  "outgoing update";
 5028: 			reason = FTR_LESS_CRIT_BIND_INFO;
 5029: 			goto bad;
 5030: 		} else {
 5031: 			/* This makes it so we ignore any spurious ACKs. */
 5032: 			dhcp_failover_ack_queue_remove(state, lease);
 5033: 		}
 5034: 	}
 5035: 
 5036: 	/* Install the new info.  Start by taking a copy to markup. */
 5037: 	if (!lease_copy (&lt, lease, MDL)) {
 5038: 		message = "no memory";
 5039: 		goto bad;
 5040: 	}
 5041: 
 5042: 	if (msg -> options_present & FTB_CHADDR) {
 5043: 		if (msg->binding_status == FTS_ABANDONED) {
 5044: 			message = "BNDUPD to ABANDONED with a CHADDR";
 5045: 			goto bad;
 5046: 		}
 5047: 		if (msg -> chaddr.count > sizeof lt -> hardware_addr.hbuf) {
 5048: 			message = "chaddr too long";
 5049: 			goto bad;
 5050: 		}
 5051: 
 5052: 		if ((lt->hardware_addr.hlen != msg->chaddr.count) ||
 5053: 		    (memcmp(lt->hardware_addr.hbuf, msg->chaddr.data,
 5054: 			    msg->chaddr.count) != 0))
 5055: 			chaddr_changed = ISC_TRUE;
 5056: 
 5057: 		lt -> hardware_addr.hlen = msg -> chaddr.count;
 5058: 		memcpy (lt -> hardware_addr.hbuf, msg -> chaddr.data,
 5059: 			msg -> chaddr.count);
 5060: 	} else if (msg->binding_status == FTS_ACTIVE ||
 5061: 		   msg->binding_status == FTS_EXPIRED ||
 5062: 		   msg->binding_status == FTS_RELEASED) {
 5063: 		message = "BNDUPD without CHADDR";
 5064: 		reason = FTR_MISSING_BINDINFO;
 5065: 		goto bad;
 5066: 	} else if (msg->binding_status == FTS_ABANDONED) {
 5067: 		chaddr_changed = ISC_TRUE;
 5068: 		lt->hardware_addr.hlen = 0;
 5069: 		if (lt->scope)
 5070: 			binding_scope_dereference(&lt->scope, MDL);
 5071: 	}
 5072: 
 5073: 	/* There is no explicit message content to indicate that the client
 5074: 	 * supplied no client-identifier.  So if we don't hear of a value,
 5075: 	 * we discard the last one.
 5076: 	 */
 5077: 	if (msg->options_present & FTB_CLIENT_IDENTIFIER) {
 5078: 		if (msg->binding_status == FTS_ABANDONED) {
 5079: 			message = "BNDUPD to ABANDONED with client-id";
 5080: 			goto bad;
 5081: 		}
 5082: 
 5083: 		if ((lt->uid_len != msg->client_identifier.count) ||
 5084: 		    (lt->uid == NULL) || /* Sanity; should never happen. */
 5085: 		    (memcmp(lt->uid, msg->client_identifier.data,
 5086: 			    lt->uid_len) != 0))
 5087: 			ident_changed = ISC_TRUE;
 5088: 
 5089: 		lt->uid_len = msg->client_identifier.count;
 5090: 
 5091: 		/* Allocate the lt->uid buffer if we haven't already, or
 5092: 		 * re-allocate the lt-uid buffer if we have one that is not
 5093: 		 * large enough.  Otherwise, just use the extant buffer.
 5094: 		 */
 5095: 		if (!lt->uid || lt->uid == lt->uid_buf ||
 5096: 		    lt->uid_len > lt->uid_max) {
 5097: 			if (lt->uid && lt->uid != lt->uid_buf)
 5098: 				dfree(lt->uid, MDL);
 5099: 
 5100: 			if (lt->uid_len > sizeof(lt->uid_buf)) {
 5101: 				lt->uid_max = lt->uid_len;
 5102: 				lt->uid = dmalloc(lt->uid_len, MDL);
 5103: 				if (!lt->uid) {
 5104: 					message = "no memory";
 5105: 					goto bad;
 5106: 				}
 5107: 			} else {
 5108: 				lt->uid_max = sizeof(lt->uid_buf);
 5109: 				lt->uid = lt->uid_buf;
 5110: 			}
 5111: 		}
 5112: 		memcpy (lt -> uid,
 5113: 			msg -> client_identifier.data, lt -> uid_len);
 5114: 	} else if (lt->uid && msg->binding_status != FTS_RESET &&
 5115: 		   msg->binding_status != FTS_FREE &&
 5116: 		   msg->binding_status != FTS_BACKUP) {
 5117: 		ident_changed = ISC_TRUE;
 5118: 		if (lt->uid != lt->uid_buf)
 5119: 			dfree (lt->uid, MDL);
 5120: 		lt->uid = NULL;
 5121: 		lt->uid_max = lt->uid_len = 0;
 5122: 	}
 5123: 
 5124: 	/*
 5125: 	 * A server's configuration can assign a 'binding scope';
 5126: 	 *
 5127: 	 *	set var = "value";
 5128: 	 *
 5129: 	 * The problem with these binding scopes is that they are refreshed
 5130: 	 * when the server processes a client's DHCP packet.  A local binding
 5131: 	 * scope is trash, then, when the lease has been assigned by the
 5132: 	 * partner server.  There is no real way to detect this, a peer may
 5133: 	 * be updating us (as through potential conflict) with a binding we
 5134: 	 * sent them, but we can trivially detect the /problematic/ case;
 5135: 	 *
 5136: 	 *	lease is free.
 5137: 	 *	primary allocates lease to client A, assigns ddns name A.
 5138: 	 *	primary fails.
 5139: 	 *	secondary enters partner down.
 5140: 	 *	lease expires, and is set free.
 5141: 	 *	lease is allocated to client B and given ddns name B.
 5142: 	 *	primary recovers.
 5143: 	 *
 5144: 	 * The binding update in this case will be active->active, but the
 5145: 	 * client identification on the lease will have changed.  The ddns
 5146: 	 * update on client A will have leaked if we just remove the binding
 5147: 	 * scope blindly.
 5148: 	 */
 5149: 	if (msg->binding_status == FTS_ACTIVE &&
 5150: 	    (chaddr_changed || ident_changed)) {
 5151: 		ddns_removals(lease, NULL);
 5152: 
 5153: 		if (lease->scope != NULL)
 5154: 			binding_scope_dereference(&lease->scope, MDL);
 5155: 	}
 5156: 
 5157: 	/* XXX Times may need to be adjusted based on clock skew! */
 5158: 	if (msg -> options_present & FTB_STOS) {
 5159: 		lt -> starts = msg -> stos;
 5160: 	}
 5161: 	if (msg -> options_present & FTB_LEASE_EXPIRY) {
 5162: 		lt -> ends = msg -> expiry;
 5163: 	}
 5164: 	if (msg->options_present & FTB_POTENTIAL_EXPIRY) {
 5165: 		lt->atsfp = lt->tsfp = msg->potential_expiry;
 5166: 	}
 5167: 	if (msg->options_present & FTB_IP_FLAGS) {
 5168: 		if (msg->ip_flags & FTF_IP_FLAG_RESERVE) {
 5169: 			if ((((state->i_am == primary) &&
 5170: 			      (lease->binding_state == FTS_FREE)) ||
 5171: 			     ((state->i_am == secondary) &&
 5172: 			      (lease->binding_state == FTS_BACKUP))) &&
 5173: 			    !(lease->flags & RESERVED_LEASE)) {
 5174: 				message = "Address is not reserved.";
 5175: 				reason = FTR_IP_NOT_RESERVED;
 5176: 				goto bad;
 5177: 			}
 5178: 
 5179: 			lt->flags |= RESERVED_LEASE;
 5180: 		} else
 5181: 			lt->flags &= ~RESERVED_LEASE;
 5182: 
 5183: 		if (msg->ip_flags & FTF_IP_FLAG_BOOTP) {
 5184: 			if ((((state->i_am == primary) &&
 5185: 			      (lease->binding_state == FTS_FREE)) ||
 5186: 			     ((state->i_am == secondary) &&
 5187: 			      (lease->binding_state == FTS_BACKUP))) &&
 5188: 			    !(lease->flags & BOOTP_LEASE)) {
 5189: 				message = "Address is not allocated to BOOTP.";
 5190: 				goto bad;
 5191: 			}
 5192: 			lt->flags |= BOOTP_LEASE;
 5193: 		} else
 5194: 			lt->flags &= ~BOOTP_LEASE;
 5195: 
 5196: 		if (msg->ip_flags & ~(FTF_IP_FLAG_RESERVE | FTF_IP_FLAG_BOOTP))
 5197: 			log_info("Unknown IP-flags set in BNDUPD (0x%x).",
 5198: 				 msg->ip_flags);
 5199: 	} else /* Flags may only not appear if the values are zero. */
 5200: 		lt->flags &= ~(RESERVED_LEASE | BOOTP_LEASE);
 5201: 
 5202: #if defined (DEBUG_LEASE_STATE_TRANSITIONS)
 5203: 	log_info ("processing state transition for %s: %s to %s",
 5204: 		  piaddr (lease -> ip_addr),
 5205: 		  binding_state_print (lease -> binding_state),
 5206: 		  binding_state_print (msg -> binding_status));
 5207: #endif
 5208: 
 5209: 	/* If we're in normal state, make sure the state transition
 5210: 	   we got is valid. */
 5211: 	if (state -> me.state == normal) {
 5212: 		new_binding_state =
 5213: 			(normal_binding_state_transition_check
 5214: 			 (lease, state, msg -> binding_status,
 5215: 			  msg -> potential_expiry));
 5216: 		/* XXX if the transition the peer asked for isn't
 5217: 		   XXX allowed, maybe we should make the transition
 5218: 		   XXX into potential-conflict at this point. */
 5219: 	} else {
 5220: 		new_binding_state =
 5221: 			(conflict_binding_state_transition_check
 5222: 			 (lease, state, msg -> binding_status,
 5223: 			  msg -> potential_expiry));
 5224: 	}
 5225: 	if (new_binding_state != msg -> binding_status) {
 5226: 		char outbuf [100];
 5227: 
 5228: 		if (snprintf (outbuf, sizeof outbuf,
 5229: 			  "%s: invalid state transition: %s to %s",
 5230: 			  piaddr (lease -> ip_addr),
 5231: 			  binding_state_print (lease -> binding_state),
 5232: 			  binding_state_print (msg -> binding_status))
 5233: 					>= sizeof outbuf)
 5234: 			log_fatal ("%s: impossible outbuf overflow",
 5235: 				"dhcp_failover_process_bind_update");
 5236: 
 5237: 		dhcp_failover_send_bind_ack (state, msg,
 5238: 					     FTR_FATAL_CONFLICT,
 5239: 					     outbuf);
 5240: 		goto out;
 5241: 	}
 5242: 	if (new_binding_state == FTS_EXPIRED ||
 5243: 	    new_binding_state == FTS_RELEASED ||
 5244: 	    new_binding_state == FTS_RESET) {
 5245: 		lt -> next_binding_state = FTS_FREE;
 5246: 
 5247: 		/* Mac address affinity.  Assign the lease to
 5248: 		 * BACKUP state if we are the primary and the
 5249: 		 * peer is more likely to reallocate this lease
 5250: 		 * to a returning client.
 5251: 		 */
 5252: 		if ((state->i_am == primary) &&
 5253: 		    !(lt->flags & (RESERVED_LEASE | BOOTP_LEASE)))
 5254: 			send_to_backup = peer_wants_lease(lt);
 5255: 	} else {
 5256: 		lt -> next_binding_state = new_binding_state;
 5257: 	}
 5258: 	msg -> binding_status = lt -> next_binding_state;
 5259: 
 5260: 	/* Try to install the new information. */
 5261: 	if (!supersede_lease (lease, lt, 0, 0, 0) ||
 5262: 	    !write_lease (lease)) {
 5263: 		message = "database update failed";
 5264: 	      bad:
 5265: 		dhcp_failover_send_bind_ack (state, msg, reason, message);
 5266: 		goto out;
 5267: 	} else {
 5268: 		dhcp_failover_queue_ack (state, msg);
 5269: 	}
 5270: 
 5271: 	/* If it is probably wise, assign lease to backup state if the peer
 5272: 	 * is not already hoarding leases.
 5273: 	 */
 5274: 	if (send_to_backup && secondary_not_hoarding(state, lease->pool)) {
 5275: 		lease->next_binding_state = FTS_BACKUP;
 5276: 		lease->tstp = cur_time;
 5277: 		lease->starts = cur_time;
 5278: 
 5279: 		if (!supersede_lease(lease, NULL, 0, 1, 0) ||
 5280: 		    !write_lease(lease))
 5281: 			log_error("can't commit lease %s for mac addr "
 5282: 				  "affinity", piaddr(lease->ip_addr));
 5283: 
 5284: 		dhcp_failover_send_updates(state);
 5285: 	}
 5286: 
 5287:       out:
 5288: 	if (lt)
 5289: 		lease_dereference (&lt, MDL);
 5290: 	if (lease)
 5291: 		lease_dereference (&lease, MDL);
 5292: 
 5293: 	return ISC_R_SUCCESS;
 5294: }
 5295: 
 5296: /* This was hairy enough I didn't want to do it all in an if statement.
 5297:  *
 5298:  * Returns: Truth is the secondary is allowed to get more leases based upon
 5299:  * MAC address affinity.  False otherwise.
 5300:  */
 5301: static inline int
 5302: secondary_not_hoarding(dhcp_failover_state_t *state, struct pool *p) {
 5303: 	int total;
 5304: 	int hold;
 5305: 	int lts;
 5306: 
 5307: 	total = p->free_leases + p->backup_leases;
 5308: 
 5309: 	/* How many leases is one side or the other allowed to "hold"? */
 5310: 	hold = ((total * state->max_lease_ownership) + 50) / 100;
 5311: 
 5312: 	/* If we were to send leases (or if the secondary were to send us
 5313: 	 * leases in the negative direction), how many would that be?
 5314: 	 */
 5315: 	lts = (p->free_leases - p->backup_leases) / 2;
 5316: 
 5317: 	/* The peer is not hoarding leases if we would send them more leases
 5318: 	 * (or they would take fewer leases) than the maximum they are allowed
 5319: 	 * to hold (the negative hold).
 5320: 	 */
 5321: 	return(lts > -hold);
 5322: }
 5323: 
 5324: isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *state,
 5325: 					     failover_message_t *msg)
 5326: {
 5327: 	struct lease *lt = (struct lease *)0;
 5328: 	struct lease *lease = (struct lease *)0;
 5329: 	struct iaddr ia;
 5330: 	const char *message = "no memory";
 5331: 	u_int32_t pot_expire;
 5332: 	int send_to_backup = ISC_FALSE;
 5333: 	struct timeval tv;
 5334: 
 5335: 	ia.len = sizeof msg -> assigned_addr;
 5336: 	memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
 5337: 
 5338: 	if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
 5339: 		message = "no such lease";
 5340: 		goto bad;
 5341: 	}
 5342: 
 5343: 	/* XXX check for conflicts. */
 5344: 	if (msg -> options_present & FTB_REJECT_REASON) {
 5345: 		log_error ("bind update on %s from %s rejected: %.*s",
 5346: 			   piaddr (ia), state -> name,
 5347: 			   (int)((msg -> options_present & FTB_MESSAGE)
 5348: 				 ? msg -> message.count
 5349: 				 : strlen (dhcp_failover_reject_reason_print
 5350: 					   (msg -> reject_reason))),
 5351: 			   (msg -> options_present & FTB_MESSAGE)
 5352: 			   ? (const char *)(msg -> message.data)
 5353: 			   : (dhcp_failover_reject_reason_print
 5354: 			      (msg -> reject_reason)));
 5355: 		goto unqueue;
 5356: 	}
 5357: 
 5358: 	/* Silently discard acks for leases we did not update (or multiple
 5359: 	 * acks).
 5360: 	 */
 5361: 	if (!lease->last_xid)
 5362: 		goto unqueue;
 5363: 
 5364: 	if (lease->last_xid != msg->xid) {
 5365: 		message = "xid mismatch";
 5366: 		goto bad;
 5367: 	}
 5368: 
 5369: 	/* XXX Times may need to be adjusted based on clock skew! */
 5370: 	if (msg->options_present & FTO_POTENTIAL_EXPIRY)
 5371: 		pot_expire = msg->potential_expiry;
 5372: 	else
 5373: 		pot_expire = lease->tstp;
 5374: 
 5375: 	/* If the lease was desired to enter a binding state, we set
 5376: 	 * such a value upon transmitting a bndupd.  We do not clear it
 5377: 	 * if we receive a bndupd in the meantime (or change the state
 5378: 	 * of the lease again ourselves), but we do set binding_state
 5379: 	 * if we get a bndupd.
 5380: 	 *
 5381: 	 * So desired_binding_state tells us what we sent a bndupd for,
 5382: 	 * and binding_state tells us what we have since determined in
 5383: 	 * the meantime.
 5384: 	 */
 5385: 	if (lease->desired_binding_state == FTS_EXPIRED ||
 5386: 	    lease->desired_binding_state == FTS_RESET ||
 5387: 	    lease->desired_binding_state == FTS_RELEASED)
 5388: 	{
 5389: 		/* It is not a problem to do this directly as we call
 5390: 		 * supersede_lease immediately after: the lease is requeued
 5391: 		 * even if its sort order (tsfp) has changed.
 5392: 		 */
 5393: 		lease->atsfp = lease->tsfp = pot_expire;
 5394: 		if ((state->i_am == secondary) &&
 5395: 		    (lease->flags & RESERVED_LEASE))
 5396: 			lease->next_binding_state = FTS_BACKUP;
 5397: 		else
 5398: 			lease->next_binding_state = FTS_FREE;
 5399: 		/* Clear this condition for the next go-round. */
 5400: 		lease->desired_binding_state = lease->next_binding_state;
 5401: 		supersede_lease(lease, (struct lease *)0, 0, 0, 0);
 5402: 		write_lease(lease);
 5403: 
 5404: 		/* Lease has returned to FREE state from the
 5405: 		 * transitional states.  If the lease 'belongs'
 5406: 		 * to a client that would be served by the
 5407: 		 * peer, process a binding update now to send
 5408: 		 * the lease to backup state.  But not if we
 5409: 		 * think we already have.
 5410: 		 */
 5411: 		if (state->i_am == primary &&
 5412: 		    !(lease->flags & (RESERVED_LEASE | BOOTP_LEASE)) &&
 5413: 		    peer_wants_lease(lease))
 5414: 			send_to_backup = ISC_TRUE;
 5415: 
 5416: 		if (!send_to_backup && state->me.state == normal)
 5417: 			commit_leases();
 5418: 	} else {
 5419: 		/* XXX It could be a problem to do this directly if the lease
 5420: 		 * XXX is sorted by tsfp.
 5421: 		 */
 5422: 		lease->atsfp = lease->tsfp = pot_expire;
 5423: 		if (lease->desired_binding_state != lease->binding_state) {
 5424: 			lease->next_binding_state =
 5425: 				lease->desired_binding_state;
 5426: 			supersede_lease(lease,
 5427: 					(struct lease *)0, 0, 0, 0);
 5428: 		}
 5429: 		write_lease(lease);
 5430: 		/* Commit the lease only after a two-second timeout,
 5431: 		   so that if we get a bunch of acks in quick
 5432: 		   succession (e.g., when stealing leases from the
 5433: 		   secondary), we do not do an immediate commit for
 5434: 		   each one. */
 5435: 		tv.tv_sec = cur_time + 2;
 5436: 		tv.tv_usec = 0;
 5437: 		add_timeout(&tv, commit_leases_timeout, (void *)0, 0, 0);
 5438: 	}
 5439: 
 5440:       unqueue:
 5441: 	dhcp_failover_ack_queue_remove (state, lease);
 5442: 
 5443: 	/* If we are supposed to send an update done after we send
 5444: 	   this lease, go ahead and send it. */
 5445: 	if (state -> send_update_done == lease) {
 5446: 		lease_dereference (&state -> send_update_done, MDL);
 5447: 		dhcp_failover_send_update_done (state);
 5448: 	}
 5449: 
 5450: 	/* Now that the lease is off the ack queue, consider putting it
 5451: 	 * back on the update queue for mac address affinity.
 5452: 	 */
 5453: 	if (send_to_backup && secondary_not_hoarding(state, lease->pool)) {
 5454: 		lease->next_binding_state = FTS_BACKUP;
 5455: 		lease->tstp = lease->starts = cur_time;
 5456: 
 5457: 		if (!supersede_lease(lease, NULL, 0, 1, 0) ||
 5458: 		    !write_lease(lease))
 5459: 			log_error("can't commit lease %s for "
 5460: 				  "client affinity", piaddr(lease->ip_addr));
 5461: 
 5462: 		if (state->me.state == normal)
 5463: 			commit_leases();
 5464: 	}
 5465: 
 5466: 	/* If there are updates pending, we've created space to send at
 5467: 	   least one. */
 5468: 	dhcp_failover_send_updates (state);
 5469: 
 5470:       out:
 5471: 	lease_dereference (&lease, MDL);
 5472: 	if (lt)
 5473: 		lease_dereference (&lt, MDL);
 5474: 
 5475: 	return ISC_R_SUCCESS;
 5476: 
 5477:       bad:
 5478: 	log_info ("bind update on %s got ack from %s: %s.",
 5479: 		  piaddr (ia), state -> name, message);
 5480: 	goto out;
 5481: }
 5482: 
 5483: isc_result_t dhcp_failover_generate_update_queue (dhcp_failover_state_t *state,
 5484: 						  int everythingp)
 5485: {
 5486: 	struct shared_network *s;
 5487: 	struct pool *p;
 5488: 	struct lease *l;
 5489: 	int i;
 5490: #define FREE_LEASES 0
 5491: #define ACTIVE_LEASES 1
 5492: #define EXPIRED_LEASES 2
 5493: #define ABANDONED_LEASES 3
 5494: #define BACKUP_LEASES 4
 5495: #define RESERVED_LEASES 5
 5496: 	struct lease **lptr[RESERVED_LEASES+1];
 5497: 
 5498: 	/* Loop through each pool in each shared network and call the
 5499: 	   expiry routine on the pool. */
 5500: 	for (s = shared_networks; s; s = s -> next) {
 5501: 	    for (p = s -> pools; p; p = p -> next) {
 5502: 		if (p->failover_peer != state)
 5503: 			continue;
 5504: 
 5505: 		lptr[FREE_LEASES] = &p->free;
 5506: 		lptr[ACTIVE_LEASES] = &p->active;
 5507: 		lptr[EXPIRED_LEASES] = &p->expired;
 5508: 		lptr[ABANDONED_LEASES] = &p->abandoned;
 5509: 		lptr[BACKUP_LEASES] = &p->backup;
 5510: 		lptr[RESERVED_LEASES] = &p->reserved;
 5511: 
 5512: 		for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
 5513: 		    for (l = *(lptr [i]); l; l = l -> next) {
 5514: 			if ((l->flags & ON_QUEUE) == 0 &&
 5515: 			    (everythingp ||
 5516: 			     (l->tstp > l->atsfp) ||
 5517: 			     (i == EXPIRED_LEASES))) {
 5518: 				l -> desired_binding_state = l -> binding_state;
 5519: 				dhcp_failover_queue_update (l, 0);
 5520: 			}
 5521: 		    }
 5522: 		}
 5523: 	    }
 5524: 	}
 5525: 	return ISC_R_SUCCESS;
 5526: }
 5527: 
 5528: isc_result_t
 5529: dhcp_failover_process_update_request (dhcp_failover_state_t *state,
 5530: 				      failover_message_t *msg)
 5531: {
 5532: 	if (state->send_update_done) {
 5533: 		log_info("Received update request while old update still "
 5534: 			 "flying!  Silently discarding old request.");
 5535: 		lease_dereference(&state->send_update_done, MDL);
 5536: 	}
 5537: 
 5538: 	/* Generate a fresh update queue. */
 5539: 	dhcp_failover_generate_update_queue (state, 0);
 5540: 
 5541: 	state->updxid = msg->xid;
 5542: 
 5543: 	/* If there's anything on the update queue (there shouldn't be
 5544: 	   anything on the ack queue), trigger an update done message
 5545: 	   when we get an ack for that lease. */
 5546: 	if (state -> update_queue_tail) {
 5547: 		lease_reference (&state -> send_update_done,
 5548: 				 state -> update_queue_tail, MDL);
 5549: 		dhcp_failover_send_updates (state);
 5550: 		log_info ("Update request from %s: sending update",
 5551: 			   state -> name);
 5552: 	} else {
 5553: 		/* Otherwise, there are no updates to send, so we can
 5554: 		   just send an UPDDONE message immediately. */
 5555: 		dhcp_failover_send_update_done (state);
 5556: 		log_info ("Update request from %s: nothing pending",
 5557: 			   state -> name);
 5558: 	}
 5559: 
 5560: 	return ISC_R_SUCCESS;
 5561: }
 5562: 
 5563: isc_result_t
 5564: dhcp_failover_process_update_request_all (dhcp_failover_state_t *state,
 5565: 					  failover_message_t *msg)
 5566: {
 5567: 	if (state->send_update_done) {
 5568: 		log_info("Received update request while old update still "
 5569: 			 "flying!  Silently discarding old request.");
 5570: 		lease_dereference(&state->send_update_done, MDL);
 5571: 	}
 5572: 
 5573: 	/* Generate a fresh update queue that includes every lease. */
 5574: 	dhcp_failover_generate_update_queue (state, 1);
 5575: 
 5576: 	state->updxid = msg->xid;
 5577: 
 5578: 	if (state -> update_queue_tail) {
 5579: 		lease_reference (&state -> send_update_done,
 5580: 				 state -> update_queue_tail, MDL);
 5581: 		dhcp_failover_send_updates (state);
 5582: 		log_info ("Update request all from %s: sending update",
 5583: 			   state -> name);
 5584: 	} else {
 5585: 		/* This should really never happen, but it could happen
 5586: 		   on a server that currently has no leases configured. */
 5587: 		dhcp_failover_send_update_done (state);
 5588: 		log_info ("Update request all from %s: nothing pending",
 5589: 			   state -> name);
 5590: 	}
 5591: 
 5592: 	return ISC_R_SUCCESS;
 5593: }
 5594: 
 5595: isc_result_t
 5596: dhcp_failover_process_update_done (dhcp_failover_state_t *state,
 5597: 				   failover_message_t *msg)
 5598: {
 5599: 	struct timeval tv;
 5600: 
 5601: 	log_info ("failover peer %s: peer update completed.",
 5602: 		  state -> name);
 5603: 
 5604: 	state -> curUPD = 0;
 5605: 
 5606: 	switch (state -> me.state) {
 5607: 	      case unknown_state:
 5608: 	      case partner_down:
 5609: 	      case normal:
 5610: 	      case communications_interrupted:
 5611: 	      case resolution_interrupted:
 5612: 	      case shut_down:
 5613: 	      case paused:
 5614: 	      case recover_done:
 5615: 	      case startup:
 5616: 	      case recover_wait:
 5617: 		break;	/* shouldn't happen. */
 5618: 
 5619: 		/* We got the UPDDONE, so we can go into normal state! */
 5620: 	      case potential_conflict:
 5621: 		if (state->partner.state == conflict_done) {
 5622: 			if (state->i_am == secondary) {
 5623: 				dhcp_failover_set_state (state, normal);
 5624: 			} else {
 5625: 				log_error("Secondary is in conflict_done "
 5626: 					  "state after conflict resolution, "
 5627: 					  "this is illegal.");
 5628: 				dhcp_failover_set_state (state, shut_down);
 5629: 			}
 5630: 		} else {
 5631: 			if (state->i_am == primary)
 5632: 				dhcp_failover_set_state (state, conflict_done);
 5633: 			else
 5634: 				log_error("Spurious update-done message.");
 5635: 		}
 5636: 
 5637: 		break;
 5638: 
 5639: 	      case conflict_done:
 5640: 		log_error("Spurious update-done message.");
 5641: 		break;
 5642: 
 5643: 	      case recover:
 5644: 		/* Wait for MCLT to expire before moving to recover_done,
 5645: 		   except that if both peers come up in recover, there is
 5646: 		   no point in waiting for MCLT to expire - this probably
 5647: 		   indicates the initial startup of a newly-configured
 5648: 		   failover pair. */
 5649: 		if (state -> me.stos + state -> mclt > cur_time &&
 5650: 		    state -> partner.state != recover &&
 5651: 		    state -> partner.state != recover_done) {
 5652: 			dhcp_failover_set_state (state, recover_wait);
 5653: #if defined (DEBUG_FAILOVER_TIMING)
 5654: 			log_info ("add_timeout +%d %s",
 5655: 				  (int)(cur_time -
 5656: 					state -> me.stos + state -> mclt),
 5657: 				  "dhcp_failover_recover_done");
 5658: #endif
 5659: 			tv . tv_sec = (int)(state -> me.stos + state -> mclt);
 5660: 			tv . tv_usec = 0;
 5661: 			add_timeout (&tv,
 5662: 				     dhcp_failover_recover_done,
 5663: 				     state,
 5664: 				     (tvref_t)omapi_object_reference,
 5665: 				     (tvunref_t)
 5666: 				     omapi_object_dereference);
 5667: 		} else
 5668: 			dhcp_failover_recover_done (state);
 5669: 	}
 5670: 
 5671: 	return ISC_R_SUCCESS;
 5672: }
 5673: 
 5674: void dhcp_failover_recover_done (void *sp)
 5675: {
 5676: 	dhcp_failover_state_t *state = sp;
 5677: 
 5678: #if defined (DEBUG_FAILOVER_TIMING)
 5679: 	log_info ("dhcp_failover_recover_done");
 5680: #endif
 5681: 
 5682: 	dhcp_failover_set_state (state, recover_done);
 5683: }
 5684: 
 5685: #if defined (DEBUG_FAILOVER_MESSAGES)
 5686: /* Print hunks of failover messages, doing line breaks as appropriate.
 5687:    Note that this assumes syslog is being used, rather than, e.g., the
 5688:    Windows NT logging facility, where just dumping the whole message in
 5689:    one hunk would be more appropriate. */
 5690: 
 5691: void failover_print (char *obuf,
 5692: 		     unsigned *obufix, unsigned obufmax, const char *s)
 5693: {
 5694: 	int len = strlen (s);
 5695: 
 5696: 	while (len + *obufix + 1 >= obufmax) {
 5697: 		log_debug ("%s", obuf);
 5698: 		if (!*obufix) {
 5699: 			log_debug ("%s", s);
 5700: 			*obufix = 0;
 5701: 			return;
 5702: 		}
 5703: 		*obufix = 0;
 5704: 	}
 5705: 	strcpy (&obuf [*obufix], s);
 5706: 	*obufix += len;
 5707: }	
 5708: #endif /* defined (DEBUG_FAILOVER_MESSAGES) */
 5709: 
 5710: /* Taken from draft-ietf-dhc-loadb-01.txt: */
 5711: /* A "mixing table" of 256 distinct values, in pseudo-random order. */
 5712: unsigned char loadb_mx_tbl[256] = {
 5713:     251, 175, 119, 215,  81,  14,  79, 191, 103,  49,
 5714:     181, 143, 186, 157,   0, 232,  31,  32,  55,  60,
 5715:     152,  58,  17, 237, 174,  70, 160, 144, 220,  90,
 5716:     57,  223,  59,   3,  18, 140, 111, 166, 203, 196,
 5717:     134, 243, 124,  95, 222, 179, 197,  65, 180,  48,
 5718:      36,  15, 107,  46, 233, 130, 165,  30, 123, 161,
 5719:     209,  23,  97,  16,  40,  91, 219,  61, 100,  10,
 5720:     210, 109, 250, 127,  22, 138,  29, 108, 244,  67,
 5721:     207,   9, 178, 204,  74,  98, 126, 249, 167, 116,
 5722:     34,   77, 193, 200, 121,   5,  20, 113,  71,  35,
 5723:     128,  13, 182,  94,  25, 226, 227, 199,  75,  27,
 5724:      41, 245, 230, 224,  43, 225, 177,  26, 155, 150,
 5725:     212, 142, 218, 115, 241,  73,  88, 105,  39, 114,
 5726:      62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
 5727:     154, 122,  12,  84,  82, 163,  44, 139, 228, 236,
 5728:     205, 242, 217,  11, 187, 146, 159,  64,  86, 239,
 5729:     195,  42, 106, 198, 118, 112, 184, 172,  87,   2,
 5730:     173, 117, 176, 229, 247, 253, 137, 185,  99, 164,
 5731:     102, 147,  45,  66, 231,  52, 141, 211, 194, 206,
 5732:     246, 238,  56, 110,  78, 248,  63, 240, 189,  93,
 5733:      92,  51,  53, 183,  19, 171,  72,  50,  33, 104,
 5734:     101,  69,   8, 252,  83, 120,  76, 135,  85,  54,
 5735:     202, 125, 188, 213,  96, 235, 136, 208, 162, 129,
 5736:     190, 132, 156,  38,  47,   1,   7, 254,  24,   4,
 5737:     216, 131,  89,  21,  28, 133,  37, 153, 149,  80,
 5738:     170,  68,   6, 169, 234, 151 };
 5739: 
 5740: static unsigned char loadb_p_hash (const unsigned char *, unsigned);
 5741: static unsigned char loadb_p_hash (const unsigned char *key, unsigned len)
 5742: {
 5743:         unsigned char hash = len;
 5744:         int i;
 5745:         for(i = len; i > 0;  )
 5746: 		hash = loadb_mx_tbl [hash ^ (key [--i])];
 5747:         return hash;
 5748: }
 5749: 
 5750: int load_balance_mine (struct packet *packet, dhcp_failover_state_t *state)
 5751: {
 5752: 	struct option_cache *oc;
 5753: 	struct data_string ds;
 5754: 	unsigned char hbaix;
 5755: 	int hm;
 5756: 
 5757: 	if (state -> load_balance_max_secs < ntohs (packet -> raw -> secs)) {
 5758: 		return 1;
 5759: 	}
 5760: 
 5761: 	/* If we don't have a hash bucket array, we can't tell if this
 5762: 	   one's ours, so we assume it's not. */
 5763: 	if (!state -> hba)
 5764: 		return 0;
 5765: 
 5766: 	oc = lookup_option (&dhcp_universe, packet -> options,
 5767: 			    DHO_DHCP_CLIENT_IDENTIFIER);
 5768: 	memset (&ds, 0, sizeof ds);
 5769: 	if (oc &&
 5770: 	    evaluate_option_cache (&ds, packet, (struct lease *)0,
 5771: 				   (struct client_state *)0,
 5772: 				   packet -> options, (struct option_state *)0,
 5773: 				   &global_scope, oc, MDL)) {
 5774: 		hbaix = loadb_p_hash (ds.data, ds.len);
 5775: 
 5776: 		data_string_forget(&ds, MDL);
 5777: 	} else {
 5778: 		hbaix = loadb_p_hash (packet -> raw -> chaddr,
 5779: 				      packet -> raw -> hlen);
 5780: 	}
 5781: 
 5782: 	hm = state->hba[(hbaix >> 3) & 0x1F] & (1 << (hbaix & 0x07));
 5783: 
 5784: 	if (state -> i_am == primary)
 5785: 		return hm;
 5786: 	else
 5787: 		return !hm;
 5788: }
 5789: 
 5790: /* The inverse of load_balance_mine ("load balance theirs").  We can't
 5791:  * use the regular load_balance_mine() and invert it because of the case
 5792:  * where there might not be an HBA, and we want to indicate false here
 5793:  * in this case only.
 5794:  */
 5795: int
 5796: peer_wants_lease(struct lease *lp)
 5797: {
 5798: 	dhcp_failover_state_t *state;
 5799: 	unsigned char hbaix;
 5800: 	int hm;
 5801: 
 5802: 	if (!lp->pool)
 5803: 		return 0;
 5804: 
 5805: 	state = lp->pool->failover_peer;
 5806: 
 5807: 	if (!state || !state->hba)
 5808: 		return 0;
 5809: 
 5810: 	if (lp->uid_len)
 5811: 		hbaix = loadb_p_hash(lp->uid, lp->uid_len);
 5812: 	else if (lp->hardware_addr.hlen > 1)
 5813: 		/* Skip the first byte, which is the hardware type, and is
 5814: 		 * not included during actual load balancing checks above
 5815: 		 * since it is separate from the packet header chaddr field.
 5816: 		 * The remainder of the hardware address should be identical
 5817: 		 * to the chaddr contents.
 5818: 		 */
 5819: 		hbaix = loadb_p_hash(lp->hardware_addr.hbuf + 1,
 5820: 				     lp->hardware_addr.hlen - 1);
 5821: 	else /* impossible to categorize into LBA */
 5822: 		return 0;
 5823: 
 5824: 	hm = state->hba[(hbaix >> 3) & 0x1F] & (1 << (hbaix & 0x07));
 5825: 
 5826: 	if (state->i_am == primary)
 5827: 		return !hm;
 5828: 	else
 5829: 		return hm;
 5830: }
 5831: 
 5832: /* This deals with what to do with bind updates when
 5833:    we're in the normal state 
 5834: 
 5835:    Note that tsfp had better be set from the latest bind update
 5836:    _before_ this function is called! */
 5837: 
 5838: binding_state_t
 5839: normal_binding_state_transition_check (struct lease *lease,
 5840: 				       dhcp_failover_state_t *state,
 5841: 				       binding_state_t binding_state,
 5842: 				       u_int32_t tsfp)
 5843: {
 5844: 	binding_state_t new_state;
 5845: 
 5846: 	/* If there is no transition, it's no problem. */
 5847: 	if (binding_state == lease -> binding_state)
 5848: 		return binding_state;
 5849: 
 5850: 	switch (lease -> binding_state) {
 5851: 	      case FTS_FREE:
 5852: 	      case FTS_ABANDONED:
 5853: 		switch (binding_state) {
 5854: 		      case FTS_ACTIVE:
 5855: 		      case FTS_ABANDONED:
 5856: 		      case FTS_BACKUP:
 5857: 		      case FTS_EXPIRED:
 5858: 		      case FTS_RELEASED:
 5859: 		      case FTS_RESET:
 5860: 			/* If the lease was free, and our peer is primary,
 5861: 			   then it can make it active, or abandoned, or
 5862: 			   backup.    Abandoned is treated like free in
 5863: 			   this case. */
 5864: 			if (state -> i_am == secondary)
 5865: 				return binding_state;
 5866: 
 5867: 			/* Otherwise, it can't legitimately do any sort of
 5868: 			   state transition.   Because the lease was free,
 5869: 			   and the error has already been made, we allow the
 5870: 			   peer to change its state anyway, but log a warning
 5871: 			   message in hopes that the error will be fixed. */
 5872: 		      case FTS_FREE: /* for compiler */
 5873: 			new_state = binding_state;
 5874: 			goto out;
 5875: 
 5876: 		      default:
 5877: 			log_fatal ("Impossible case at %s:%d.", MDL);
 5878: 			return FTS_RESET;
 5879: 		}
 5880: 	      case FTS_ACTIVE:
 5881: 		/* The secondary can't change the state of an active
 5882: 		   lease. */
 5883: 		if (state -> i_am == primary) {
 5884: 			/* Except that the client may send the DHCPRELEASE
 5885: 			   to the secondary, and we have to accept that. */
 5886: 			if (binding_state == FTS_RELEASED)
 5887: 				return binding_state;
 5888: 			new_state = lease -> binding_state;
 5889: 			goto out;
 5890: 		}
 5891: 
 5892: 		/* So this is only for transitions made by the primary: */
 5893: 		switch (binding_state) {
 5894: 		      case FTS_FREE:
 5895: 		      case FTS_BACKUP:
 5896: 			/* Can't set a lease to free or backup until the
 5897: 			   peer agrees that it's expired. */
 5898: 			if (tsfp > cur_time) {
 5899: 				new_state = lease -> binding_state;
 5900: 				goto out;
 5901: 			}
 5902: 			return binding_state;
 5903: 
 5904: 		      case FTS_EXPIRED:
 5905: 			/* XXX 65 should be the clock skew between the peers
 5906: 			   XXX plus a fudge factor.   This code will result
 5907: 			   XXX in problems if MCLT is really short or the
 5908: 			   XXX max-lease-time is really short (less than the
 5909: 			   XXX fudge factor. */
 5910: 			if (lease -> ends - 65 > cur_time) {
 5911: 				new_state = lease -> binding_state;
 5912: 				goto out;
 5913: 			}
 5914: 
 5915: 		      case FTS_RELEASED:
 5916: 		      case FTS_ABANDONED:
 5917: 		      case FTS_RESET:
 5918: 		      case FTS_ACTIVE:
 5919: 			return binding_state;
 5920: 
 5921: 		      default:
 5922: 			log_fatal ("Impossible case at %s:%d.", MDL);
 5923: 			return FTS_RESET;
 5924: 		}
 5925: 		break;
 5926: 	      case FTS_EXPIRED:
 5927: 		switch (binding_state) {
 5928: 		      case FTS_BACKUP:
 5929: 		      case FTS_FREE:
 5930: 			/* Can't set a lease to free or backup until the
 5931: 			   peer agrees that it's expired. */
 5932: 			if (tsfp > cur_time) {
 5933: 				new_state = lease -> binding_state;
 5934: 				goto out;
 5935: 			}
 5936: 			return binding_state;
 5937: 
 5938: 		      case FTS_ACTIVE:
 5939: 		      case FTS_RELEASED:
 5940: 		      case FTS_ABANDONED:
 5941: 		      case FTS_RESET:
 5942: 		      case FTS_EXPIRED:
 5943: 			return binding_state;
 5944: 
 5945: 		      default:
 5946: 			log_fatal ("Impossible case at %s:%d.", MDL);
 5947: 			return FTS_RESET;
 5948: 		}
 5949: 	      case FTS_RELEASED:
 5950: 		switch (binding_state) {
 5951: 		      case FTS_FREE:
 5952: 		      case FTS_BACKUP:
 5953: 
 5954: 			/* These are invalid state transitions - should we
 5955: 			   prevent them? */
 5956: 		      case FTS_EXPIRED:
 5957: 		      case FTS_ABANDONED:
 5958: 		      case FTS_RESET:
 5959: 		      case FTS_ACTIVE:
 5960: 		      case FTS_RELEASED:
 5961: 			return binding_state;
 5962: 
 5963: 		      default:
 5964: 			log_fatal ("Impossible case at %s:%d.", MDL);
 5965: 			return FTS_RESET;
 5966: 		}
 5967: 	      case FTS_RESET:
 5968: 		switch (binding_state) {
 5969: 		      case FTS_FREE:
 5970: 		      case FTS_BACKUP:
 5971: 			/* Can't set a lease to free or backup until the
 5972: 			   peer agrees that it's expired. */
 5973: 			if (tsfp > cur_time) {
 5974: 				new_state = lease -> binding_state;
 5975: 				goto out;
 5976: 			}
 5977: 			return binding_state;
 5978: 
 5979: 		      case FTS_ACTIVE:
 5980: 		      case FTS_EXPIRED:
 5981: 		      case FTS_RELEASED:
 5982: 		      case FTS_ABANDONED:
 5983: 		      case FTS_RESET:
 5984: 			return binding_state;
 5985: 
 5986: 		      default:
 5987: 			log_fatal ("Impossible case at %s:%d.", MDL);
 5988: 			return FTS_RESET;
 5989: 		}
 5990: 	      case FTS_BACKUP:
 5991: 		switch (binding_state) {
 5992: 		      case FTS_ACTIVE:
 5993: 		      case FTS_ABANDONED:
 5994: 		      case FTS_EXPIRED:
 5995: 		      case FTS_RELEASED:
 5996: 		      case FTS_RESET:
 5997: 			/* If the lease was in backup, and our peer
 5998: 			   is secondary, then it can make it active
 5999: 			   or abandoned. */
 6000: 			if (state -> i_am == primary)
 6001: 				return binding_state;
 6002: 
 6003: 			/* Either the primary or the secondary can
 6004: 			   reasonably move a lease from the backup
 6005: 			   state to the free state. */
 6006: 		      case FTS_FREE:
 6007: 			return binding_state;
 6008: 
 6009: 		      case FTS_BACKUP:
 6010: 			new_state = lease -> binding_state;
 6011: 			goto out;
 6012: 
 6013: 		      default:
 6014: 			log_fatal ("Impossible case at %s:%d.", MDL);
 6015: 			return FTS_RESET;
 6016: 		}
 6017: 
 6018: 	      default:
 6019: 		log_fatal ("Impossible case at %s:%d.", MDL);
 6020: 		return FTS_RESET;
 6021: 	}
 6022:       out:
 6023: 	return new_state;
 6024: }
 6025: 
 6026: /* Determine whether the state transition is okay when we're potentially
 6027:    in conflict with the peer. */
 6028: binding_state_t
 6029: conflict_binding_state_transition_check (struct lease *lease,
 6030: 					 dhcp_failover_state_t *state,
 6031: 					 binding_state_t binding_state,
 6032: 					 u_int32_t tsfp)
 6033: {
 6034: 	binding_state_t new_state;
 6035: 
 6036: 	/* If there is no transition, it's no problem. */
 6037: 	if (binding_state == lease -> binding_state)
 6038: 		new_state = binding_state;
 6039: 	else {
 6040: 		switch (lease -> binding_state) {
 6041: 			/* If we think the lease is not in use, then the
 6042: 			   state into which the partner put it is just fine,
 6043: 			   whatever it is. */
 6044: 		      case FTS_FREE:
 6045: 		      case FTS_ABANDONED:
 6046: 		      case FTS_EXPIRED:
 6047: 		      case FTS_RELEASED:
 6048: 		      case FTS_RESET:
 6049: 		      case FTS_BACKUP:
 6050: 			new_state = binding_state;
 6051: 			break;
 6052: 
 6053: 			/* If we think the lease *is* in use, then we're not
 6054: 			   going to take the partner's change if the partner
 6055: 			   thinks it's free. */
 6056: 		      case FTS_ACTIVE:
 6057: 			switch (binding_state) {
 6058: 			      case FTS_FREE:
 6059: 			      case FTS_BACKUP:
 6060: 				new_state = lease -> binding_state;
 6061: 				break;
 6062: 
 6063: 			      case FTS_EXPIRED:
 6064: 				/* If we don't agree about expiry, it's
 6065: 				 * invalid.  65 should allow for max
 6066: 				 * clock skew (60) plus some fudge.
 6067: 				 * XXX: should we refetch cur_time?
 6068: 				 */
 6069: 				if ((lease->ends - 65) > cur_time)
 6070: 					new_state = lease->binding_state;
 6071: 				else
 6072: 					new_state = binding_state;
 6073: 				break;
 6074: 
 6075: 				/* RELEASED, RESET, and ABANDONED indicate
 6076: 				 * that our partner has information about
 6077: 				 * this lease that we did not witness.  Our
 6078: 				 * partner wins.
 6079: 				 */
 6080: 			      case FTS_RELEASED:
 6081: 			      case FTS_RESET:
 6082: 			      case FTS_ABANDONED:
 6083: 				new_state = binding_state;
 6084: 				break;
 6085: 
 6086: 			      default:
 6087: 				log_fatal ("Impossible case at %s:%d.", MDL);
 6088: 				return FTS_RESET;
 6089: 			}
 6090: 			break;
 6091: 
 6092: 		      default:
 6093: 			log_fatal ("Impossible case at %s:%d.", MDL);
 6094: 			return FTS_RESET;
 6095: 		}
 6096: 	}
 6097: 	return new_state;
 6098: }
 6099: 
 6100: /* We can reallocate a lease under the following circumstances:
 6101: 
 6102:    (1) It belongs to us - it's FTS_FREE, and we're primary, or it's
 6103:        FTS_BACKUP, and we're secondary.
 6104:    (2) We're in partner_down, and the lease is not active, and we
 6105:        can be sure that the other server didn't make it active.
 6106:        We can only be sure that the server didn't make it active
 6107:        when we are in the partner_down state and one of the following
 6108:        two conditions holds:
 6109:        (a) in the case that the time sent from the peer is earlier than
 6110:            the time we entered the partner_down state, at least MCLT has
 6111: 	   gone by since we entered partner_down, or
 6112:        (b) in the case that the time sent from the peer is later than
 6113:            the time when we entered partner_down, the current time is
 6114: 	   later than the time sent from the peer by at least MCLT. */
 6115: 
 6116: int lease_mine_to_reallocate (struct lease *lease)
 6117: {
 6118: 	dhcp_failover_state_t *peer;
 6119: 
 6120: 	if (lease && lease->pool &&
 6121: 	    (peer = lease->pool->failover_peer)) {
 6122: 		switch (lease->binding_state) {
 6123: 		      case FTS_ACTIVE:
 6124: 			/* ACTIVE leases may not be reallocated. */
 6125: 			return 0;
 6126: 
 6127: 		      case FTS_FREE:
 6128: 		      case FTS_ABANDONED:
 6129: 			/* FREE leases may only be allocated by the primary,
 6130: 			 * unless the secondary is acting in partner_down
 6131: 			 * state and stos+mclt or tsfp+mclt has expired,
 6132: 			 * whichever is greater.
 6133: 			 *
 6134: 			 * ABANDONED are treated the same as FREE for all
 6135: 			 * purposes here.  Note that servers will only try
 6136: 			 * for ABANDONED leases as a last resort anyway.
 6137: 			 */
 6138: 			if (peer -> i_am == primary)
 6139: 				return 1;
 6140: 
 6141: 			return(peer->service_state == service_partner_down &&
 6142: 			       ((lease->tsfp < peer->me.stos) ?
 6143: 				(peer->me.stos + peer->mclt < cur_time) :
 6144: 				(lease->tsfp + peer->mclt < cur_time)));
 6145: 
 6146: 		      case FTS_RESET:
 6147: 		      case FTS_RELEASED:
 6148: 		      case FTS_EXPIRED:
 6149: 			/* These three lease states go onto the 'expired'
 6150: 			 * queue.  Upon entry into partner-down state, this
 6151: 			 * queue of leases has their tsfp values modified
 6152: 			 * to equal stos+mclt, the point at which the server
 6153: 			 * is allowed to remove them from these transitional
 6154: 			 * states without an acknowledgement.
 6155: 			 *
 6156: 			 * Note that although tsfp has been possibly extended
 6157: 			 * past the actual tsfp we received from the peer, we
 6158: 			 * don't have to take any special action.  Since tsfp
 6159: 			 * is now in the past (or now), we can guarantee that
 6160: 			 * this server will only allocate a lease time equal
 6161: 			 * to MCLT, rather than a TSFP-optimal lease, which is
 6162: 			 * the only danger for a lease in one of these states.
 6163: 			 */
 6164: 			return((peer->service_state == service_partner_down) &&
 6165: 			       (lease->tsfp < cur_time));
 6166: 
 6167: 		      case FTS_BACKUP:
 6168: 			/* Only the secondary may allocate BACKUP leases,
 6169: 			 * unless in partner_down state in which case at
 6170: 			 * least TSFP+MCLT or STOS+MCLT must have expired,
 6171: 			 * whichever is greater.
 6172: 			 */
 6173: 			if (peer->i_am == secondary)
 6174: 				return 1;
 6175: 
 6176: 			return((peer->service_state == service_partner_down) &&
 6177: 			       ((lease->tsfp < peer->me.stos) ?
 6178: 				(peer->me.stos + peer->mclt < cur_time) :
 6179: 				(lease->tsfp + peer->mclt < cur_time)));
 6180: 
 6181: 		      default:
 6182: 			/* All lease states appear above. */
 6183: 			log_fatal("Impossible case at %s:%d.", MDL);
 6184: 			break;
 6185: 		}
 6186: 		return 0;
 6187: 	}
 6188: 	if (lease)
 6189: 		return(lease->binding_state == FTS_FREE ||
 6190: 		       lease->binding_state == FTS_BACKUP);
 6191: 	else
 6192: 		return 0;
 6193: }
 6194: 
 6195: static isc_result_t failover_message_reference (failover_message_t **mp,
 6196: 						failover_message_t *m,
 6197: 						const char *file, int line)
 6198: {
 6199: 	*mp = m;
 6200: 	m -> refcnt++;
 6201: 	return ISC_R_SUCCESS;
 6202: }
 6203: 
 6204: static isc_result_t failover_message_dereference (failover_message_t **mp,
 6205: 						  const char *file, int line)
 6206: {
 6207: 	failover_message_t *m;
 6208: 	m = (*mp);
 6209: 	m -> refcnt--;
 6210: 	if (m -> refcnt == 0) {
 6211: 		if (m -> next)
 6212: 			failover_message_dereference (&m -> next,
 6213: 						      file, line);
 6214: 		if (m -> chaddr.data)
 6215: 			dfree (m -> chaddr.data, file, line);
 6216: 		if (m -> client_identifier.data)
 6217: 			dfree (m -> client_identifier.data, file, line);
 6218: 		if (m -> hba.data)
 6219: 			dfree (m -> hba.data, file, line);
 6220: 		if (m -> message.data)
 6221: 			dfree (m -> message.data, file, line);
 6222: 		if (m -> reply_options.data)
 6223: 			dfree (m -> reply_options.data, file, line);
 6224: 		if (m -> request_options.data)
 6225: 			dfree (m -> request_options.data, file, line);
 6226: 		if (m -> vendor_class.data)
 6227: 			dfree (m -> vendor_class.data, file, line);
 6228: 		if (m -> vendor_options.data)
 6229: 			dfree (m -> vendor_options.data, file, line);
 6230: 		if (m -> ddns.data)
 6231: 			dfree (m -> ddns.data, file, line);
 6232: 		dfree (*mp, file, line);
 6233: 	}
 6234: 	*mp = 0;
 6235: 	return ISC_R_SUCCESS;
 6236: }
 6237: 
 6238: OMAPI_OBJECT_ALLOC (dhcp_failover_state, dhcp_failover_state_t,
 6239: 		    dhcp_type_failover_state)
 6240: OMAPI_OBJECT_ALLOC (dhcp_failover_listener, dhcp_failover_listener_t,
 6241: 		    dhcp_type_failover_listener)
 6242: OMAPI_OBJECT_ALLOC (dhcp_failover_link, dhcp_failover_link_t,
 6243: 		    dhcp_type_failover_link)
 6244: #endif /* defined (FAILOVER_PROTOCOL) */
 6245: 
 6246: const char *binding_state_print (enum failover_state state)
 6247: {
 6248: 	switch (state) {
 6249: 	      case FTS_FREE:
 6250: 		return "free";
 6251: 		break;
 6252: 
 6253: 	      case FTS_ACTIVE:
 6254: 		return "active";
 6255: 		break;
 6256: 
 6257: 	      case FTS_EXPIRED:
 6258: 		return "expired";
 6259: 		break;
 6260: 
 6261: 	      case FTS_RELEASED:
 6262: 		return "released";
 6263: 		break;
 6264: 
 6265: 	      case FTS_ABANDONED:
 6266: 		return "abandoned";
 6267: 		break;
 6268: 
 6269: 	      case FTS_RESET:
 6270: 		return "reset";
 6271: 		break;
 6272: 
 6273: 	      case FTS_BACKUP:
 6274: 		return "backup";
 6275: 		break;
 6276: 
 6277: 	      default:
 6278: 		return "unknown";
 6279: 		break;
 6280: 	}
 6281: }

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