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