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