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

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

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