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