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