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