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