Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / dhcp / server / failover.c
blob32fa037bf8e2a87516327bc7d523764c0cc60884
1 /* failover.c
3 Failover protocol support code... */
5 /*
6 * Copyright (c) 2004-2005 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1999-2003 by Internet Software Consortium
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
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.
21 * Internet Systems Consortium, Inc.
22 * 950 Charter Street
23 * Redwood City, CA 94063
24 * <info@isc.org>
25 * http://www.isc.org/
27 * This software has been written for Internet Systems Consortium
28 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29 * To learn more about Internet Systems Consortium, see
30 * ``http://www.isc.org/''. To learn more about Vixie Enterprises,
31 * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
32 * ``http://www.nominum.com''.
35 #ifndef lint
36 static char copyright[] =
37 "$Id: failover.c,v 1.10 2005/08/11 17:13:30 drochner Exp $ Copyright (c) 2004-2005 Internet Systems Consortium. All rights reserved.\n";
38 #endif /* not lint */
40 #include "dhcpd.h"
41 #include "version.h"
42 #include <omapip/omapip_p.h>
44 #if defined (FAILOVER_PROTOCOL)
45 dhcp_failover_state_t *failover_states;
46 static isc_result_t do_a_failover_option (omapi_object_t *,
47 dhcp_failover_link_t *);
48 dhcp_failover_listener_t *failover_listeners;
50 static isc_result_t failover_message_reference (failover_message_t **,
51 failover_message_t *,
52 const char *file, int line);
53 static isc_result_t failover_message_dereference (failover_message_t **,
54 const char *file, int line);
56 void dhcp_failover_startup ()
58 dhcp_failover_state_t *state;
59 isc_result_t status;
61 for (state = failover_states; state; state = state -> next) {
62 dhcp_failover_state_transition (state, "startup");
64 if (state -> pool_count == 0) {
65 log_error ("failover peer declaration with no %s",
66 "referring pools.");
67 log_error ("In order to use failover, you MUST %s",
68 "refer to your main failover declaration");
69 log_error ("in each pool declaration. You MUST %s",
70 "NOT use range declarations outside");
71 log_fatal ("of pool declarations.");
73 /* In case the peer is already running, immediately try
74 to establish a connection with it. */
75 status = dhcp_failover_link_initiate ((omapi_object_t *)state);
76 if (status != ISC_R_SUCCESS && status != ISC_R_INCOMPLETE) {
77 #if defined (DEBUG_FAILOVER_TIMING)
78 log_info ("add_timeout +90 dhcp_failover_reconnect");
79 #endif
80 add_timeout (cur_time + 90,
81 dhcp_failover_reconnect, state,
82 (tvref_t)
83 dhcp_failover_state_reference,
84 (tvunref_t)
85 dhcp_failover_state_dereference);
86 log_error ("failover peer %s: %s", state -> name,
87 isc_result_totext (status));
90 status = (dhcp_failover_listen
91 ((omapi_object_t *)state));
92 if (status != ISC_R_SUCCESS) {
93 #if defined (DEBUG_FAILOVER_TIMING)
94 log_info ("add_timeout +90 %s",
95 "dhcp_failover_listener_restart");
96 #endif
97 add_timeout (cur_time + 90,
98 dhcp_failover_listener_restart,
99 state,
100 (tvref_t)omapi_object_reference,
101 (tvunref_t)omapi_object_dereference);
106 int dhcp_failover_write_all_states ()
108 dhcp_failover_state_t *state;
110 for (state = failover_states; state; state = state -> next) {
111 if (!write_failover_state (state))
112 return 0;
114 return 1;
117 isc_result_t enter_failover_peer (peer)
118 dhcp_failover_state_t *peer;
120 dhcp_failover_state_t *dup = (dhcp_failover_state_t *)0;
121 isc_result_t status;
123 status = find_failover_peer (&dup, peer -> name, MDL);
124 if (status == ISC_R_NOTFOUND) {
125 if (failover_states) {
126 dhcp_failover_state_reference (&peer -> next,
127 failover_states, MDL);
128 dhcp_failover_state_dereference (&failover_states,
129 MDL);
131 dhcp_failover_state_reference (&failover_states, peer, MDL);
132 return ISC_R_SUCCESS;
134 dhcp_failover_state_dereference (&dup, MDL);
135 if (status == ISC_R_SUCCESS)
136 return ISC_R_EXISTS;
137 return status;
140 isc_result_t find_failover_peer (peer, name, file, line)
141 dhcp_failover_state_t **peer;
142 const char *name;
143 const char *file;
144 int line;
146 dhcp_failover_state_t *p;
148 for (p = failover_states; p; p = p -> next)
149 if (!strcmp (name, p -> name))
150 break;
151 if (p)
152 return dhcp_failover_state_reference (peer, p, file, line);
153 return ISC_R_NOTFOUND;
156 /* The failover protocol has three objects associated with it. For
157 each failover partner declaration in the dhcpd.conf file, primary
158 or secondary, there is a failover_state object. For any primary or
159 secondary state object that has a connection to its peer, there is
160 also a failover_link object, which has its own input state seperate
161 from the failover protocol state for managing the actual bytes
162 coming in off the wire. Finally, there will be one listener object
163 for every distinct port number associated with a secondary
164 failover_state object. Normally all secondary failover_state
165 objects are expected to listen on the same port number, so there
166 need be only one listener object, but if different port numbers are
167 specified for each failover object, there could be as many as one
168 listener object for each secondary failover_state object. */
170 /* This, then, is the implemention of the failover link object. */
172 isc_result_t dhcp_failover_link_initiate (omapi_object_t *h)
174 isc_result_t status;
175 dhcp_failover_link_t *obj;
176 dhcp_failover_state_t *state;
177 omapi_object_t *o;
178 int i;
179 struct data_string ds;
180 omapi_addr_list_t *addrs = (omapi_addr_list_t *)0;
181 omapi_addr_t local_addr;
183 /* Find the failover state in the object chain. */
184 for (o = h; o -> outer; o = o -> outer)
186 for (; o; o = o -> inner) {
187 if (o -> type == dhcp_type_failover_state)
188 break;
190 if (!o)
191 return ISC_R_INVALIDARG;
192 state = (dhcp_failover_state_t *)o;
194 obj = (dhcp_failover_link_t *)0;
195 status = dhcp_failover_link_allocate (&obj, MDL);
196 if (status != ISC_R_SUCCESS)
197 return status;
198 option_cache_reference (&obj -> peer_address,
199 state -> partner.address, MDL);
200 obj -> peer_port = state -> partner.port;
201 dhcp_failover_state_reference (&obj -> state_object, state, MDL);
203 memset (&ds, 0, sizeof ds);
204 if (!evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0,
205 (struct client_state *)0,
206 (struct option_state *)0,
207 (struct option_state *)0,
208 &global_scope, obj -> peer_address, MDL)) {
209 dhcp_failover_link_dereference (&obj, MDL);
210 return ISC_R_UNEXPECTED;
213 /* Make an omapi address list out of a buffer containing zero or more
214 IPv4 addresses. */
215 status = omapi_addr_list_new (&addrs, ds.len / 4, MDL);
216 if (status != ISC_R_SUCCESS) {
217 dhcp_failover_link_dereference (&obj, MDL);
218 return status;
221 for (i = 0; i < addrs -> count; i++) {
222 addrs -> addresses [i].addrtype = AF_INET;
223 addrs -> addresses [i].addrlen = sizeof (struct in_addr);
224 memcpy (addrs -> addresses [i].address,
225 &ds.data [i * 4], sizeof (struct in_addr));
226 addrs -> addresses [i].port = obj -> peer_port;
228 data_string_forget (&ds, MDL);
230 /* Now figure out the local address that we're supposed to use. */
231 if (!state -> me.address ||
232 !evaluate_option_cache (&ds, (struct packet *)0,
233 (struct lease *)0,
234 (struct client_state *)0,
235 (struct option_state *)0,
236 (struct option_state *)0,
237 &global_scope, state -> me.address,
238 MDL)) {
239 memset (&local_addr, 0, sizeof local_addr);
240 local_addr.addrtype = AF_INET;
241 local_addr.addrlen = sizeof (struct in_addr);
242 if (!state -> server_identifier.len) {
243 log_fatal ("failover peer %s: no local address.",
244 state -> name);
246 } else {
247 if (ds.len != sizeof (struct in_addr)) {
248 data_string_forget (&ds, MDL);
249 dhcp_failover_link_dereference (&obj, MDL);
250 omapi_addr_list_dereference (&addrs, MDL);
251 return ISC_R_INVALIDARG;
253 local_addr.addrtype = AF_INET;
254 local_addr.addrlen = ds.len;
255 memcpy (local_addr.address, ds.data, ds.len);
256 if (!state -> server_identifier.len)
257 data_string_copy (&state -> server_identifier,
258 &ds, MDL);
259 data_string_forget (&ds, MDL);
260 local_addr.port = 0; /* Let the O.S. choose. */
263 status = omapi_connect_list ((omapi_object_t *)obj,
264 addrs, &local_addr);
265 omapi_addr_list_dereference (&addrs, MDL);
267 dhcp_failover_link_dereference (&obj, MDL);
268 return status;
271 isc_result_t dhcp_failover_link_signal (omapi_object_t *h,
272 const char *name, va_list ap)
274 isc_result_t status;
275 dhcp_failover_link_t *link;
276 omapi_object_t *c;
277 dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
279 if (h -> type != dhcp_type_failover_link) {
280 /* XXX shouldn't happen. Put an assert here? */
281 return ISC_R_UNEXPECTED;
283 link = (dhcp_failover_link_t *)h;
285 if (!strcmp (name, "connect")) {
286 if (link -> state_object -> i_am == primary) {
287 status = dhcp_failover_send_connect (h);
288 if (status != ISC_R_SUCCESS) {
289 log_info ("dhcp_failover_send_connect: %s",
290 isc_result_totext (status));
291 omapi_disconnect (h -> outer, 1);
293 } else
294 status = ISC_R_SUCCESS;
295 /* Allow the peer fifteen seconds to send us a
296 startup message. */
297 #if defined (DEBUG_FAILOVER_TIMING)
298 log_info ("add_timeout +15 %s",
299 "dhcp_failover_link_startup_timeout");
300 #endif
301 add_timeout (cur_time + 15,
302 dhcp_failover_link_startup_timeout,
303 link,
304 (tvref_t)dhcp_failover_link_reference,
305 (tvunref_t)dhcp_failover_link_dereference);
306 return status;
309 if (!strcmp (name, "disconnect")) {
310 if (link -> state_object) {
311 dhcp_failover_state_reference (&state,
312 link -> state_object, MDL);
313 link -> state = dhcp_flink_disconnected;
315 /* Make the transition. */
316 if (state -> link_to_peer == link) {
317 dhcp_failover_state_transition (link -> state_object,
318 name);
320 /* Start trying to reconnect. */
321 #if defined (DEBUG_FAILOVER_TIMING)
322 log_info ("add_timeout +5 %s",
323 "dhcp_failover_reconnect");
324 #endif
325 add_timeout (cur_time + 5, dhcp_failover_reconnect,
326 state,
327 (tvref_t)dhcp_failover_state_reference,
328 (tvunref_t)dhcp_failover_state_dereference);
330 dhcp_failover_state_dereference (&state, MDL);
332 return ISC_R_SUCCESS;
335 if (!strcmp (name, "status")) {
336 if (link -> state_object) {
337 isc_result_t status;
339 status = va_arg(ap, isc_result_t);
341 if ((status == ISC_R_HOSTUNREACH) || (status == ISC_R_TIMEDOUT)) {
342 dhcp_failover_state_reference (&state,
343 link -> state_object, MDL);
344 link -> state = dhcp_flink_disconnected;
346 /* Make the transition. */
347 dhcp_failover_state_transition (link -> state_object,
348 "disconnect");
350 /* Start trying to reconnect. */
351 #if defined (DEBUG_FAILOVER_TIMING)
352 log_info ("add_timeout +5 %s",
353 "dhcp_failover_reconnect");
354 #endif
355 add_timeout (cur_time + 5, dhcp_failover_reconnect,
356 state,
357 (tvref_t)dhcp_failover_state_reference,
358 (tvunref_t)dhcp_failover_state_dereference);
360 dhcp_failover_state_dereference (&state, MDL);
362 return ISC_R_SUCCESS;
365 /* Not a signal we recognize? */
366 if (strcmp (name, "ready")) {
367 if (h -> inner && h -> inner -> type -> signal_handler)
368 return (*(h -> inner -> type -> signal_handler))
369 (h -> inner, name, ap);
370 return ISC_R_NOTFOUND;
373 if (!h -> outer || h -> outer -> type != omapi_type_connection)
374 return ISC_R_INVALIDARG;
375 c = h -> outer;
377 /* We get here because we requested that we be woken up after
378 some number of bytes were read, and that number of bytes
379 has in fact been read. */
380 switch (link -> state) {
381 case dhcp_flink_start:
382 link -> state = dhcp_flink_message_length_wait;
383 if ((omapi_connection_require (c, 2)) != ISC_R_SUCCESS)
384 break;
385 case dhcp_flink_message_length_wait:
386 next_message:
387 link -> state = dhcp_flink_message_wait;
388 link -> imsg = dmalloc (sizeof (failover_message_t), MDL);
389 if (!link -> imsg) {
390 status = ISC_R_NOMEMORY;
391 dhcp_flink_fail:
392 if (link -> imsg) {
393 failover_message_dereference (&link->imsg,
394 MDL);
396 link -> state = dhcp_flink_disconnected;
397 log_info ("message length wait: %s",
398 isc_result_totext (status));
399 omapi_disconnect (c, 1);
400 /* XXX just blow away the protocol state now?
401 XXX or will disconnect blow it away? */
402 return ISC_R_UNEXPECTED;
404 memset (link -> imsg, 0, sizeof (failover_message_t));
405 link -> imsg -> refcnt = 1;
406 /* Get the length: */
407 omapi_connection_get_uint16 (c, &link -> imsg_len);
408 link -> imsg_count = 0; /* Bytes read. */
410 /* Maximum of 2048 bytes in any failover message. */
411 if (link -> imsg_len > DHCP_FAILOVER_MAX_MESSAGE_SIZE) {
412 status = ISC_R_UNEXPECTED;
413 goto dhcp_flink_fail;
416 if ((omapi_connection_require (c, link -> imsg_len - 2U)) !=
417 ISC_R_SUCCESS)
418 break;
419 case dhcp_flink_message_wait:
420 /* Read in the message. At this point we have the
421 entire message in the input buffer. For each
422 incoming value ID, set a bit in the bitmask
423 indicating that we've gotten it. Maybe flag an
424 error message if the bit is already set. Once
425 we're done reading, we can check the bitmask to
426 make sure that the required fields for each message
427 have been included. */
429 link -> imsg_count += 2; /* Count the length as read. */
431 /* Get message type. */
432 omapi_connection_copyout (&link -> imsg -> type, c, 1);
433 link -> imsg_count++;
435 /* Get message payload offset. */
436 omapi_connection_copyout (&link -> imsg_payoff, c, 1);
437 link -> imsg_count++;
439 /* Get message time. */
440 omapi_connection_get_uint32 (c, &link -> imsg -> time);
441 link -> imsg_count += 4;
443 /* Get transaction ID. */
444 omapi_connection_get_uint32 (c, &link -> imsg -> xid);
445 link -> imsg_count += 4;
447 #if defined (DEBUG_FAILOVER_MESSAGES)
448 log_info ("link: message %s payoff %d time %ld xid %ld",
449 dhcp_failover_message_name (link -> imsg -> type),
450 link -> imsg_payoff,
451 (unsigned long)link -> imsg -> time,
452 (unsigned long)link -> imsg -> xid);
453 #endif
454 /* Skip over any portions of the message header that we
455 don't understand. */
456 if (link -> imsg_payoff - link -> imsg_count) {
457 omapi_connection_copyout ((unsigned char *)0, c,
458 (link -> imsg_payoff -
459 link -> imsg_count));
460 link -> imsg_count = link -> imsg_payoff;
463 /* Now start sucking options off the wire. */
464 while (link -> imsg_count < link -> imsg_len) {
465 status = do_a_failover_option (c, link);
466 if (status != ISC_R_SUCCESS)
467 goto dhcp_flink_fail;
470 /* If it's a connect message, try to associate it with
471 a state object. */
472 /* XXX this should be authenticated! */
473 if (link -> imsg -> type == FTM_CONNECT) {
474 const char *errmsg;
475 int reason;
476 /* See if we can find a failover_state object that
477 matches this connection. This message should only
478 be received by a secondary from a primary. */
479 for (s = failover_states; s; s = s -> next) {
480 if (dhcp_failover_state_match
481 (s, (u_int8_t *)&link -> imsg -> server_addr,
482 sizeof link -> imsg -> server_addr))
483 state = s;
486 /* If we can't find a failover protocol state
487 for this remote host, drop the connection */
488 if (!state) {
489 errmsg = "unknown server";
490 reason = FTR_INVALID_PARTNER;
492 badconnect:
493 /* XXX Send a refusal message first?
494 XXX Look in protocol spec for guidance. */
495 log_error ("Failover CONNECT from %u.%u.%u.%u: %s",
496 ((u_int8_t *)
497 (&link -> imsg -> server_addr)) [0],
498 ((u_int8_t *)
499 (&link -> imsg -> server_addr)) [1],
500 ((u_int8_t *)
501 (&link -> imsg -> server_addr)) [2],
502 ((u_int8_t *)
503 (&link -> imsg -> server_addr)) [3],
504 errmsg);
505 dhcp_failover_send_connectack
506 ((omapi_object_t *)link, state,
507 reason, errmsg);
508 log_info ("failover: disconnect: %s", errmsg);
509 omapi_disconnect (c, 0);
510 link -> state = dhcp_flink_disconnected;
511 return ISC_R_SUCCESS;
514 if ((cur_time > link -> imsg -> time &&
515 cur_time - link -> imsg -> time > 60) ||
516 (cur_time < link -> imsg -> time &&
517 link -> imsg -> time - cur_time > 60)) {
518 errmsg = "time offset too large";
519 reason = FTR_TIMEMISMATCH;
520 goto badconnect;
523 if (!(link -> imsg -> options_present & FTB_HBA) ||
524 link -> imsg -> hba.count != 32) {
525 errmsg = "invalid HBA";
526 reason = FTR_HBA_CONFLICT; /* XXX */
527 goto badconnect;
529 if (state -> hba)
530 dfree (state -> hba, MDL);
531 state -> hba = dmalloc (32, MDL);
532 if (!state -> hba) {
533 errmsg = "no memory";
534 reason = FTR_MISC_REJECT;
535 goto badconnect;
537 memcpy (state -> hba, link -> imsg -> hba.data, 32);
539 if (!link -> state_object)
540 dhcp_failover_state_reference
541 (&link -> state_object, state, MDL);
542 if (!link -> peer_address)
543 option_cache_reference
544 (&link -> peer_address,
545 state -> partner.address, MDL);
548 /* If we don't have a state object at this point, it's
549 some kind of bogus situation, so just drop the
550 connection. */
551 if (!link -> state_object) {
552 log_info ("failover: connect: no matching state.");
553 omapi_disconnect (c, 1);
554 link -> state = dhcp_flink_disconnected;
555 return ISC_R_INVALIDARG;
558 /* Once we have the entire message, and we've validated
559 it as best we can here, pass it to the parent. */
560 omapi_signal ((omapi_object_t *)link -> state_object,
561 "message", link);
562 link -> state = dhcp_flink_message_length_wait;
563 failover_message_dereference (&link -> imsg, MDL);
564 /* XXX This is dangerous because we could get into a tight
565 XXX loop reading input without servicing any other stuff.
566 XXX There needs to be a way to relinquish control but
567 XXX get it back immediately if there's no other work to
568 XXX do. */
569 if ((omapi_connection_require (c, 2)) == ISC_R_SUCCESS)
570 goto next_message;
571 break;
573 default:
574 /* XXX should never get here. Assertion? */
575 break;
577 return ISC_R_SUCCESS;
580 static isc_result_t do_a_failover_option (c, link)
581 omapi_object_t *c;
582 dhcp_failover_link_t *link;
584 u_int16_t option_code;
585 u_int16_t option_len;
586 unsigned char *op;
587 unsigned op_size;
588 unsigned op_count;
589 int i;
591 if (link -> imsg_count + 2 > link -> imsg_len) {
592 log_error ("FAILOVER: message overflow at option code.");
593 return ISC_R_PROTOCOLERROR;
596 /* Get option code. */
597 omapi_connection_get_uint16 (c, &option_code);
598 link -> imsg_count += 2;
600 if (link -> imsg_count + 2 > link -> imsg_len) {
601 log_error ("FAILOVER: message overflow at length.");
602 return ISC_R_PROTOCOLERROR;
605 /* Get option length. */
606 omapi_connection_get_uint16 (c, &option_len);
607 link -> imsg_count += 2;
609 if (link -> imsg_count + option_len > link -> imsg_len) {
610 log_error ("FAILOVER: message overflow at data.");
611 return ISC_R_PROTOCOLERROR;
614 /* If it's an unknown code, skip over it. */
615 if (option_code > FTO_MAX) {
616 #if defined (DEBUG_FAILOVER_MESSAGES)
617 log_debug (" option code %d (%s) len %d (not recognized)",
618 option_code,
619 dhcp_failover_option_name (option_code),
620 option_len);
621 #endif
622 omapi_connection_copyout ((unsigned char *)0, c, option_len);
623 link -> imsg_count += option_len;
624 return ISC_R_SUCCESS;
627 /* If it's the digest, do it now. */
628 if (ft_options [option_code].type == FT_DIGEST) {
629 link -> imsg_count += option_len;
630 if (link -> imsg_count != link -> imsg_len) {
631 log_error ("FAILOVER: digest not at end of message");
632 return ISC_R_PROTOCOLERROR;
634 #if defined (DEBUG_FAILOVER_MESSAGES)
635 log_debug (" option %s len %d",
636 ft_options [option_code].name, option_len);
637 #endif
638 /* For now, just dump it. */
639 omapi_connection_copyout ((unsigned char *)0, c, option_len);
640 return ISC_R_SUCCESS;
643 /* Only accept an option once. */
644 if (link -> imsg -> options_present & ft_options [option_code].bit) {
645 log_error ("FAILOVER: duplicate option %s",
646 ft_options [option_code].name);
647 return ISC_R_PROTOCOLERROR;
650 /* Make sure the option is appropriate for this type of message.
651 Really, any option is generally allowed for any message, and the
652 cases where this is not true are too complicated to represent in
653 this way - what this code is doing is to just avoid saving the
654 value of an option we don't have any way to use, which allows
655 us to make the failover_message structure smaller. */
656 if (ft_options [option_code].bit &&
657 !(fto_allowed [link -> imsg -> type] &
658 ft_options [option_code].bit)) {
659 omapi_connection_copyout ((unsigned char *)0, c, option_len);
660 link -> imsg_count += option_len;
661 return ISC_R_SUCCESS;
664 /* Figure out how many elements, how big they are, and where
665 to store them. */
666 if (ft_options [option_code].num_present) {
667 /* If this option takes a fixed number of elements,
668 we expect the space for them to be preallocated,
669 and we can just read the data in. */
671 op = ((unsigned char *)link -> imsg) +
672 ft_options [option_code].offset;
673 op_size = ft_sizes [ft_options [option_code].type];
674 op_count = ft_options [option_code].num_present;
676 if (option_len != op_size * op_count) {
677 log_error ("FAILOVER: option size (%d:%d), option %s",
678 option_len,
679 (ft_sizes [ft_options [option_code].type] *
680 ft_options [option_code].num_present),
681 ft_options [option_code].name);
682 return ISC_R_PROTOCOLERROR;
684 } else {
685 failover_option_t *fo;
687 /* FT_DDNS* are special - one or two bytes of status
688 followed by the client FQDN. */
689 if (ft_options [option_code].type == FT_DDNS1 ||
690 ft_options [option_code].type == FT_DDNS1) {
691 ddns_fqdn_t *ddns =
692 ((ddns_fqdn_t *)
693 (((char *)link -> imsg) +
694 ft_options [option_code].offset));
696 op_count = (ft_options [option_code].type == FT_DDNS1
697 ? 1 : 2);
699 omapi_connection_copyout (&ddns -> codes [0],
700 c, op_count);
701 link -> imsg_count += op_count;
702 if (op_count == 1)
703 ddns -> codes [1] = 0;
704 op_size = 1;
705 op_count = option_len - op_count;
707 ddns -> length = op_count;
708 ddns -> data = dmalloc (op_count, MDL);
709 if (!ddns -> data) {
710 log_error ("FAILOVER: no memory getting%s(%d)",
711 " DNS data ", op_count);
713 /* Actually, NO_MEMORY, but if we lose here
714 we have to drop the connection. */
715 return ISC_R_PROTOCOLERROR;
717 omapi_connection_copyout (ddns -> data, c, op_count);
718 goto out;
721 /* A zero for num_present means that any number of
722 elements can appear, so we have to figure out how
723 many we got from the length of the option, and then
724 fill out a failover_option structure describing the
725 data. */
726 op_size = ft_sizes [ft_options [option_code].type];
728 /* Make sure that option data length is a multiple of the
729 size of the data type being sent. */
730 if (op_size > 1 && option_len % op_size) {
731 log_error ("FAILOVER: option_len %d not %s%d",
732 option_len, "multiple of ", op_size);
733 return ISC_R_PROTOCOLERROR;
736 op_count = option_len / op_size;
738 fo = ((failover_option_t *)
739 (((char *)link -> imsg) +
740 ft_options [option_code].offset));
742 fo -> count = op_count;
743 fo -> data = dmalloc (option_len, MDL);
744 if (!fo -> data) {
745 log_error ("FAILOVER: no memory getting %s (%d)",
746 "option data", op_count);
748 return ISC_R_PROTOCOLERROR;
750 op = fo -> data;
753 /* For single-byte message values and multi-byte values that
754 don't need swapping, just read them in all at once. */
755 if (op_size == 1 || ft_options [option_code].type == FT_IPADDR) {
756 omapi_connection_copyout ((unsigned char *)op, c, option_len);
757 link -> imsg_count += option_len;
758 goto out;
761 /* For values that require swapping, read them in one at a time
762 using routines that swap bytes. */
763 for (i = 0; i < op_count; i++) {
764 switch (ft_options [option_code].type) {
765 case FT_UINT32:
766 omapi_connection_get_uint32 (c, (u_int32_t *)op);
767 op += 4;
768 link -> imsg_count += 4;
769 break;
771 case FT_UINT16:
772 omapi_connection_get_uint16 (c, (u_int16_t *)op);
773 op += 2;
774 link -> imsg_count += 2;
775 break;
777 default:
778 /* Everything else should have been handled
779 already. */
780 log_error ("FAILOVER: option %s: bad type %d",
781 ft_options [option_code].name,
782 ft_options [option_code].type);
783 return ISC_R_PROTOCOLERROR;
786 out:
787 /* Remember that we got this option. */
788 link -> imsg -> options_present |= ft_options [option_code].bit;
789 return ISC_R_SUCCESS;
792 isc_result_t dhcp_failover_link_set_value (omapi_object_t *h,
793 omapi_object_t *id,
794 omapi_data_string_t *name,
795 omapi_typed_data_t *value)
797 if (h -> type != omapi_type_protocol)
798 return ISC_R_INVALIDARG;
800 /* Never valid to set these. */
801 if (!omapi_ds_strcmp (name, "link-port") ||
802 !omapi_ds_strcmp (name, "link-name") ||
803 !omapi_ds_strcmp (name, "link-state"))
804 return ISC_R_NOPERM;
806 if (h -> inner && h -> inner -> type -> set_value)
807 return (*(h -> inner -> type -> set_value))
808 (h -> inner, id, name, value);
809 return ISC_R_NOTFOUND;
812 isc_result_t dhcp_failover_link_get_value (omapi_object_t *h,
813 omapi_object_t *id,
814 omapi_data_string_t *name,
815 omapi_value_t **value)
817 dhcp_failover_link_t *link;
818 int tmp; /* XXX used to force a signed comparison below */
820 if (h -> type != omapi_type_protocol)
821 return ISC_R_INVALIDARG;
822 link = (dhcp_failover_link_t *)h;
824 if (!omapi_ds_strcmp (name, "link-port")) {
825 return omapi_make_int_value (value, name,
826 (int)link -> peer_port, MDL);
827 } else if (!omapi_ds_strcmp (name, "link-state")) {
828 if ((tmp = link -> state) < 0 ||
829 link -> state >= dhcp_flink_state_max)
830 return omapi_make_string_value (value, name,
831 "invalid link state",
832 MDL);
833 return omapi_make_string_value
834 (value, name,
835 dhcp_flink_state_names [link -> state], MDL);
838 if (h -> inner && h -> inner -> type -> get_value)
839 return (*(h -> inner -> type -> get_value))
840 (h -> inner, id, name, value);
841 return ISC_R_NOTFOUND;
844 isc_result_t dhcp_failover_link_destroy (omapi_object_t *h,
845 const char *file, int line)
847 dhcp_failover_link_t *link;
848 if (h -> type != dhcp_type_failover_link)
849 return ISC_R_INVALIDARG;
850 link = (dhcp_failover_link_t *)h;
852 if (link -> peer_address)
853 option_cache_dereference (&link -> peer_address, file, line);
854 if (link -> imsg)
855 failover_message_dereference (&link -> imsg, file, line);
856 if (link -> state_object)
857 dhcp_failover_state_dereference (&link -> state_object,
858 file, line);
859 return ISC_R_SUCCESS;
862 /* Write all the published values associated with the object through the
863 specified connection. */
865 isc_result_t dhcp_failover_link_stuff_values (omapi_object_t *c,
866 omapi_object_t *id,
867 omapi_object_t *l)
869 dhcp_failover_link_t *link;
870 isc_result_t status;
871 int tmp; /* XXX used to force a signed comparison below */
873 if (l -> type != dhcp_type_failover_link)
874 return ISC_R_INVALIDARG;
875 link = (dhcp_failover_link_t *)l;
877 status = omapi_connection_put_name (c, "link-port");
878 if (status != ISC_R_SUCCESS)
879 return status;
880 status = omapi_connection_put_uint32 (c, sizeof (int));
881 if (status != ISC_R_SUCCESS)
882 return status;
883 status = omapi_connection_put_uint32 (c, link -> peer_port);
884 if (status != ISC_R_SUCCESS)
885 return status;
887 status = omapi_connection_put_name (c, "link-state");
888 if (status != ISC_R_SUCCESS)
889 return status;
890 if ((tmp = link -> state) < 0 ||
891 link -> state >= dhcp_flink_state_max)
892 status = omapi_connection_put_string (c, "invalid link state");
893 else
894 status = (omapi_connection_put_string
895 (c, dhcp_flink_state_names [link -> state]));
896 if (status != ISC_R_SUCCESS)
897 return status;
899 if (link -> inner && link -> inner -> type -> stuff_values)
900 return (*(link -> inner -> type -> stuff_values)) (c, id,
901 link -> inner);
902 return ISC_R_SUCCESS;
905 /* Set up a listener for the omapi protocol. The handle stored points to
906 a listener object, not a protocol object. */
908 isc_result_t dhcp_failover_listen (omapi_object_t *h)
910 isc_result_t status;
911 dhcp_failover_listener_t *obj, *l;
912 omapi_value_t *value = (omapi_value_t *)0;
913 omapi_addr_t local_addr;
914 unsigned long port;
916 status = omapi_get_value_str (h, (omapi_object_t *)0,
917 "local-port", &value);
918 if (status != ISC_R_SUCCESS)
919 return status;
920 if (!value -> value) {
921 omapi_value_dereference (&value, MDL);
922 return ISC_R_INVALIDARG;
925 status = omapi_get_int_value (&port, value -> value);
926 omapi_value_dereference (&value, MDL);
927 if (status != ISC_R_SUCCESS)
928 return status;
929 local_addr.port = port;
931 status = omapi_get_value_str (h, (omapi_object_t *)0,
932 "local-address", &value);
933 if (status != ISC_R_SUCCESS)
934 return status;
935 if (!value -> value) {
936 nogood:
937 omapi_value_dereference (&value, MDL);
938 return ISC_R_INVALIDARG;
941 if (value -> value -> type != omapi_datatype_data ||
942 value -> value -> u.buffer.len != sizeof (struct in_addr))
943 goto nogood;
945 memcpy (local_addr.address, value -> value -> u.buffer.value,
946 value -> value -> u.buffer.len);
947 local_addr.addrlen = value -> value -> u.buffer.len;
948 local_addr.addrtype = AF_INET;
950 omapi_value_dereference (&value, MDL);
952 /* Are we already listening on this port and address? */
953 for (l = failover_listeners; l; l = l -> next) {
954 if (l -> address.port == local_addr.port &&
955 l -> address.addrtype == local_addr.addrtype &&
956 l -> address.addrlen == local_addr.addrlen &&
957 !memcmp (l -> address.address, local_addr.address,
958 local_addr.addrlen))
959 break;
961 /* Already listening. */
962 if (l)
963 return ISC_R_SUCCESS;
965 obj = (dhcp_failover_listener_t *)0;
966 status = dhcp_failover_listener_allocate (&obj, MDL);
967 if (status != ISC_R_SUCCESS)
968 return status;
969 obj -> address = local_addr;
971 status = omapi_listen_addr ((omapi_object_t *)obj, &obj -> address, 1);
972 if (status != ISC_R_SUCCESS)
973 return status;
975 status = omapi_object_reference (&h -> outer,
976 (omapi_object_t *)obj, MDL);
977 if (status != ISC_R_SUCCESS) {
978 dhcp_failover_listener_dereference (&obj, MDL);
979 return status;
981 status = omapi_object_reference (&obj -> inner, h, MDL);
982 if (status != ISC_R_SUCCESS) {
983 dhcp_failover_listener_dereference (&obj, MDL);
984 return status;
987 /* Put this listener on the list. */
988 if (failover_listeners) {
989 dhcp_failover_listener_reference (&obj -> next,
990 failover_listeners, MDL);
991 dhcp_failover_listener_dereference (&failover_listeners, MDL);
993 dhcp_failover_listener_reference (&failover_listeners, obj, MDL);
995 return dhcp_failover_listener_dereference (&obj, MDL);
998 /* Signal handler for protocol listener - if we get a connect signal,
999 create a new protocol connection, otherwise pass the signal down. */
1001 isc_result_t dhcp_failover_listener_signal (omapi_object_t *o,
1002 const char *name, va_list ap)
1004 isc_result_t status;
1005 omapi_connection_object_t *c;
1006 dhcp_failover_link_t *obj;
1007 dhcp_failover_listener_t *p;
1008 dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
1010 if (!o || o -> type != dhcp_type_failover_listener)
1011 return ISC_R_INVALIDARG;
1012 p = (dhcp_failover_listener_t *)o;
1014 /* Not a signal we recognize? */
1015 if (strcmp (name, "connect")) {
1016 if (p -> inner && p -> inner -> type -> signal_handler)
1017 return (*(p -> inner -> type -> signal_handler))
1018 (p -> inner, name, ap);
1019 return ISC_R_NOTFOUND;
1022 c = va_arg (ap, omapi_connection_object_t *);
1023 if (!c || c -> type != omapi_type_connection)
1024 return ISC_R_INVALIDARG;
1026 /* See if we can find a failover_state object that
1027 matches this connection. */
1028 for (s = failover_states; s; s = s -> next) {
1029 if (dhcp_failover_state_match
1030 (s, (u_int8_t *)&c -> remote_addr.sin_addr,
1031 sizeof c -> remote_addr.sin_addr)) {
1032 state = s;
1033 break;
1036 if (!state) {
1037 log_info ("failover: listener: no matching state");
1038 return omapi_disconnect ((omapi_object_t *)c, 1);
1041 obj = (dhcp_failover_link_t *)0;
1042 status = dhcp_failover_link_allocate (&obj, MDL);
1043 if (status != ISC_R_SUCCESS)
1044 return status;
1045 obj -> peer_port = ntohs (c -> remote_addr.sin_port);
1047 status = omapi_object_reference (&obj -> outer,
1048 (omapi_object_t *)c, MDL);
1049 if (status != ISC_R_SUCCESS) {
1050 lose:
1051 dhcp_failover_link_dereference (&obj, MDL);
1052 log_info ("failover: listener: picayune failure.");
1053 omapi_disconnect ((omapi_object_t *)c, 1);
1054 return status;
1057 status = omapi_object_reference (&c -> inner,
1058 (omapi_object_t *)obj, MDL);
1059 if (status != ISC_R_SUCCESS)
1060 goto lose;
1062 status = dhcp_failover_state_reference (&obj -> state_object,
1063 state, MDL);
1064 if (status != ISC_R_SUCCESS)
1065 goto lose;
1067 omapi_signal_in ((omapi_object_t *)obj, "connect");
1069 return dhcp_failover_link_dereference (&obj, MDL);
1072 isc_result_t dhcp_failover_listener_set_value (omapi_object_t *h,
1073 omapi_object_t *id,
1074 omapi_data_string_t *name,
1075 omapi_typed_data_t *value)
1077 if (h -> type != dhcp_type_failover_listener)
1078 return ISC_R_INVALIDARG;
1080 if (h -> inner && h -> inner -> type -> set_value)
1081 return (*(h -> inner -> type -> set_value))
1082 (h -> inner, id, name, value);
1083 return ISC_R_NOTFOUND;
1086 isc_result_t dhcp_failover_listener_get_value (omapi_object_t *h,
1087 omapi_object_t *id,
1088 omapi_data_string_t *name,
1089 omapi_value_t **value)
1091 if (h -> type != dhcp_type_failover_listener)
1092 return ISC_R_INVALIDARG;
1094 if (h -> inner && h -> inner -> type -> get_value)
1095 return (*(h -> inner -> type -> get_value))
1096 (h -> inner, id, name, value);
1097 return ISC_R_NOTFOUND;
1100 isc_result_t dhcp_failover_listener_destroy (omapi_object_t *h,
1101 const char *file, int line)
1103 dhcp_failover_listener_t *l;
1105 if (h -> type != dhcp_type_failover_listener)
1106 return ISC_R_INVALIDARG;
1107 l = (dhcp_failover_listener_t *)h;
1108 if (l -> next)
1109 dhcp_failover_listener_dereference (&l -> next, file, line);
1111 return ISC_R_SUCCESS;
1114 /* Write all the published values associated with the object through the
1115 specified connection. */
1117 isc_result_t dhcp_failover_listener_stuff (omapi_object_t *c,
1118 omapi_object_t *id,
1119 omapi_object_t *p)
1122 if (p -> type != dhcp_type_failover_listener)
1123 return ISC_R_INVALIDARG;
1125 if (p -> inner && p -> inner -> type -> stuff_values)
1126 return (*(p -> inner -> type -> stuff_values)) (c, id,
1127 p -> inner);
1128 return ISC_R_SUCCESS;
1131 /* Set up master state machine for the failover protocol. */
1133 isc_result_t dhcp_failover_register (omapi_object_t *h)
1135 isc_result_t status;
1136 dhcp_failover_state_t *obj;
1137 unsigned long port;
1138 omapi_value_t *value = (omapi_value_t *)0;
1140 status = omapi_get_value_str (h, (omapi_object_t *)0,
1141 "local-port", &value);
1142 if (status != ISC_R_SUCCESS)
1143 return status;
1144 if (!value -> value) {
1145 omapi_value_dereference (&value, MDL);
1146 return ISC_R_INVALIDARG;
1149 status = omapi_get_int_value (&port, value -> value);
1150 omapi_value_dereference (&value, MDL);
1151 if (status != ISC_R_SUCCESS)
1152 return status;
1154 obj = (dhcp_failover_state_t *)0;
1155 dhcp_failover_state_allocate (&obj, MDL);
1156 obj -> me.port = port;
1158 status = omapi_listen ((omapi_object_t *)obj, port, 1);
1159 if (status != ISC_R_SUCCESS) {
1160 dhcp_failover_state_dereference (&obj, MDL);
1161 return status;
1164 status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj,
1165 MDL);
1166 if (status != ISC_R_SUCCESS) {
1167 dhcp_failover_state_dereference (&obj, MDL);
1168 return status;
1170 status = omapi_object_reference (&obj -> inner, h, MDL);
1171 dhcp_failover_state_dereference (&obj, MDL);
1172 return status;
1175 /* Signal handler for protocol state machine. */
1177 isc_result_t dhcp_failover_state_signal (omapi_object_t *o,
1178 const char *name, va_list ap)
1180 isc_result_t status;
1181 dhcp_failover_state_t *state;
1182 dhcp_failover_link_t *link;
1184 if (!o || o -> type != dhcp_type_failover_state)
1185 return ISC_R_INVALIDARG;
1186 state = (dhcp_failover_state_t *)o;
1188 /* Not a signal we recognize? */
1189 if (strcmp (name, "disconnect") &&
1190 strcmp (name, "message")) {
1191 if (state -> inner && state -> inner -> type -> signal_handler)
1192 return (*(state -> inner -> type -> signal_handler))
1193 (state -> inner, name, ap);
1194 return ISC_R_NOTFOUND;
1197 /* Handle connect signals by seeing what state we're in
1198 and potentially doing a state transition. */
1199 if (!strcmp (name, "disconnect")) {
1200 link = va_arg (ap, dhcp_failover_link_t *);
1202 dhcp_failover_link_dereference (&state -> link_to_peer, MDL);
1203 dhcp_failover_state_transition (state, "disconnect");
1204 if (state -> i_am == primary) {
1205 #if defined (DEBUG_FAILOVER_TIMING)
1206 log_info ("add_timeout +90 %s",
1207 "dhcp_failover_reconnect");
1208 #endif
1209 add_timeout (cur_time + 90, dhcp_failover_reconnect,
1210 state,
1211 (tvref_t)dhcp_failover_state_reference,
1212 (tvunref_t)
1213 dhcp_failover_state_dereference);
1215 } else if (!strcmp (name, "message")) {
1216 link = va_arg (ap, dhcp_failover_link_t *);
1218 if (link -> imsg -> type == FTM_CONNECT) {
1219 /* If we already have a link to the peer, it must be
1220 dead, so drop it.
1221 XXX Is this the right thing to do?
1222 XXX Probably not - what if both peers start at
1223 XXX the same time? */
1224 if (state -> link_to_peer) {
1225 dhcp_failover_send_connectack
1226 ((omapi_object_t *)link, state,
1227 FTR_DUP_CONNECTION,
1228 "already connected");
1229 omapi_disconnect (link -> outer, 1);
1230 return ISC_R_SUCCESS;
1232 if (!(link -> imsg -> options_present & FTB_MCLT)) {
1233 dhcp_failover_send_connectack
1234 ((omapi_object_t *)link, state,
1235 FTR_INVALID_MCLT,
1236 "no MCLT provided");
1237 omapi_disconnect (link -> outer, 1);
1238 return ISC_R_SUCCESS;
1241 dhcp_failover_link_reference (&state -> link_to_peer,
1242 link, MDL);
1243 status = (dhcp_failover_send_connectack
1244 ((omapi_object_t *)link, state, 0, 0));
1245 if (status != ISC_R_SUCCESS) {
1246 dhcp_failover_link_dereference
1247 (&state -> link_to_peer, MDL);
1248 log_info ("dhcp_failover_send_connectack: %s",
1249 isc_result_totext (status));
1250 omapi_disconnect (link -> outer, 1);
1251 return ISC_R_SUCCESS;
1253 if (link -> imsg -> options_present & FTB_MAX_UNACKED)
1254 state -> partner.max_flying_updates =
1255 link -> imsg -> max_unacked;
1256 if (link -> imsg -> options_present &
1257 FTB_RECEIVE_TIMER)
1258 state -> partner.max_response_delay =
1259 link -> imsg -> receive_timer;
1260 state -> mclt = link -> imsg -> mclt;
1261 dhcp_failover_send_state (state);
1262 cancel_timeout (dhcp_failover_link_startup_timeout,
1263 link);
1264 } else if (link -> imsg -> type == FTM_CONNECTACK) {
1265 const char *errmsg;
1266 int reason;
1268 cancel_timeout (dhcp_failover_link_startup_timeout,
1269 link);
1271 if (link -> imsg -> reject_reason) {
1272 log_error ("Failover CONNECT to %u.%u.%u.%u%s%s",
1273 ((u_int8_t *)
1274 (&link -> imsg -> server_addr)) [0],
1275 ((u_int8_t *)
1276 (&link -> imsg -> server_addr)) [1],
1277 ((u_int8_t *)
1278 (&link -> imsg -> server_addr)) [2],
1279 ((u_int8_t *)
1280 (&link -> imsg -> server_addr)) [3],
1281 " rejected: ",
1282 (dhcp_failover_reject_reason_print
1283 (link -> imsg -> reject_reason)));
1284 /* XXX print message from peer if peer sent message. */
1285 omapi_disconnect (link -> outer, 1);
1286 return ISC_R_SUCCESS;
1289 if (!dhcp_failover_state_match
1290 (state,
1291 (u_int8_t *)&link -> imsg -> server_addr,
1292 sizeof link -> imsg -> server_addr)) {
1293 errmsg = "unknown server";
1294 reason = FTR_INVALID_PARTNER;
1295 badconnectack:
1296 log_error ("Failover CONNECTACK from %u.%u.%u.%u: %s",
1297 ((u_int8_t *)
1298 (&link -> imsg -> server_addr)) [0],
1299 ((u_int8_t *)
1300 (&link -> imsg -> server_addr)) [1],
1301 ((u_int8_t *)
1302 (&link -> imsg -> server_addr)) [2],
1303 ((u_int8_t *)
1304 (&link -> imsg -> server_addr)) [3],
1305 errmsg);
1306 dhcp_failover_send_disconnect ((omapi_object_t *)link,
1307 reason, errmsg);
1308 omapi_disconnect (link -> outer, 0);
1309 return ISC_R_SUCCESS;
1312 if (state -> link_to_peer) {
1313 errmsg = "already connected";
1314 reason = FTR_DUP_CONNECTION;
1315 goto badconnectack;
1318 if ((cur_time > link -> imsg -> time &&
1319 cur_time - link -> imsg -> time > 60) ||
1320 (cur_time < link -> imsg -> time &&
1321 link -> imsg -> time - cur_time > 60)) {
1322 errmsg = "time offset too large";
1323 reason = FTR_TIMEMISMATCH;
1324 goto badconnectack;
1327 dhcp_failover_link_reference (&state -> link_to_peer,
1328 link, MDL);
1329 #if 0
1330 /* XXX This is probably the right thing to do, but
1331 XXX for release three, to make the smallest possible
1332 XXX change, we are doing this when the peer state
1333 XXX changes instead. */
1334 if (state -> me.state == startup)
1335 dhcp_failover_set_state (state,
1336 state -> saved_state);
1337 else
1338 #endif
1339 dhcp_failover_send_state (state);
1341 if (link -> imsg -> options_present & FTB_MAX_UNACKED)
1342 state -> partner.max_flying_updates =
1343 link -> imsg -> max_unacked;
1344 if (link -> imsg -> options_present & FTB_RECEIVE_TIMER)
1345 state -> partner.max_response_delay =
1346 link -> imsg -> receive_timer;
1347 #if defined (DEBUG_FAILOVER_TIMING)
1348 log_info ("add_timeout +%d %s",
1349 (int)state -> partner.max_response_delay / 3,
1350 "dhcp_failover_send_contact");
1351 #endif
1352 add_timeout (cur_time +
1353 (int)state -> partner.max_response_delay / 3,
1354 dhcp_failover_send_contact, state,
1355 (tvref_t)dhcp_failover_state_reference,
1356 (tvunref_t)dhcp_failover_state_dereference);
1357 #if defined (DEBUG_FAILOVER_TIMING)
1358 log_info ("add_timeout +%d %s",
1359 (int)state -> me.max_response_delay,
1360 "dhcp_failover_timeout");
1361 #endif
1362 add_timeout (cur_time +
1363 (int)state -> me.max_response_delay,
1364 dhcp_failover_timeout, state,
1365 (tvref_t)dhcp_failover_state_reference,
1366 (tvunref_t)dhcp_failover_state_dereference);
1367 } else if (link -> imsg -> type == FTM_DISCONNECT) {
1368 if (link -> imsg -> reject_reason) {
1369 log_error ("Failover DISCONNECT from %u.%u.%u.%u%s%s",
1370 ((u_int8_t *)
1371 (&link -> imsg -> server_addr)) [0],
1372 ((u_int8_t *)
1373 (&link -> imsg -> server_addr)) [1],
1374 ((u_int8_t *)
1375 (&link -> imsg -> server_addr)) [2],
1376 ((u_int8_t *)
1377 (&link -> imsg -> server_addr)) [3],
1378 ": ",
1379 (dhcp_failover_reject_reason_print
1380 (link -> imsg -> reject_reason)));
1382 omapi_disconnect (link -> outer, 1);
1383 } else if (link -> imsg -> type == FTM_BNDUPD) {
1384 dhcp_failover_process_bind_update (state,
1385 link -> imsg);
1386 } else if (link -> imsg -> type == FTM_BNDACK) {
1387 dhcp_failover_process_bind_ack (state, link -> imsg);
1388 } else if (link -> imsg -> type == FTM_UPDREQ) {
1389 dhcp_failover_process_update_request (state,
1390 link -> imsg);
1391 } else if (link -> imsg -> type == FTM_UPDREQALL) {
1392 dhcp_failover_process_update_request_all
1393 (state, link -> imsg);
1394 } else if (link -> imsg -> type == FTM_UPDDONE) {
1395 dhcp_failover_process_update_done (state,
1396 link -> imsg);
1397 } else if (link -> imsg -> type == FTM_POOLREQ) {
1398 dhcp_failover_pool_rebalance (state);
1399 } else if (link -> imsg -> type == FTM_POOLRESP) {
1400 log_info ("pool response: %ld leases",
1401 (unsigned long)
1402 link -> imsg -> addresses_transferred);
1403 } else if (link -> imsg -> type == FTM_STATE) {
1404 dhcp_failover_peer_state_changed (state,
1405 link -> imsg);
1408 /* Add a timeout so that if the partner doesn't send
1409 another message for the maximum transmit idle time
1410 plus a grace of one second, we close the
1411 connection. */
1412 if (state -> link_to_peer &&
1413 state -> link_to_peer == link &&
1414 state -> link_to_peer -> state != dhcp_flink_disconnected)
1416 #if defined (DEBUG_FAILOVER_TIMING)
1417 log_info ("add_timeout +%d %s",
1418 (int)state -> me.max_response_delay,
1419 "dhcp_failover_timeout");
1420 #endif
1421 add_timeout (cur_time +
1422 (int)state -> me.max_response_delay,
1423 dhcp_failover_timeout, state,
1424 (tvref_t)dhcp_failover_state_reference,
1425 (tvunref_t)dhcp_failover_state_dereference);
1430 /* Handle all the events we care about... */
1431 return ISC_R_SUCCESS;
1434 isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *state,
1435 const char *name)
1437 isc_result_t status;
1439 /* XXX Check these state transitions against the spec! */
1440 if (!strcmp (name, "disconnect")) {
1441 if (state -> link_to_peer) {
1442 log_info ("peer %s: disconnected", state -> name);
1443 if (state -> link_to_peer -> state_object)
1444 dhcp_failover_state_dereference
1445 (&state -> link_to_peer -> state_object, MDL);
1446 dhcp_failover_link_dereference (&state -> link_to_peer,
1447 MDL);
1449 cancel_timeout (dhcp_failover_send_contact, state);
1450 cancel_timeout (dhcp_failover_timeout, state);
1451 cancel_timeout (dhcp_failover_startup_timeout, state);
1453 switch (state -> me.state == startup ?
1454 state -> saved_state : state -> me.state) {
1455 case resolution_interrupted:
1456 case partner_down:
1457 case communications_interrupted:
1458 case recover:
1459 /* Already in the right state? */
1460 if (state -> me.state == startup)
1461 return (dhcp_failover_set_state
1462 (state, state -> saved_state));
1463 return ISC_R_SUCCESS;
1465 case potential_conflict:
1466 return dhcp_failover_set_state
1467 (state, resolution_interrupted);
1469 case normal:
1470 return dhcp_failover_set_state
1471 (state, communications_interrupted);
1473 case unknown_state:
1474 return dhcp_failover_set_state
1475 (state, resolution_interrupted);
1476 case startup:
1477 break; /* can't happen. */
1479 } else if (!strcmp (name, "connect")) {
1480 switch (state -> me.state) {
1481 case communications_interrupted:
1482 status = dhcp_failover_set_state (state, normal);
1483 dhcp_failover_send_updates (state);
1484 return status;
1486 case resolution_interrupted:
1487 return dhcp_failover_set_state (state,
1488 potential_conflict);
1490 case partner_down:
1491 case potential_conflict:
1492 case normal:
1493 case recover:
1494 case shut_down:
1495 case paused:
1496 case unknown_state:
1497 case recover_done:
1498 case startup:
1499 case recover_wait:
1500 return dhcp_failover_send_state (state);
1502 } else if (!strcmp (name, "startup")) {
1503 dhcp_failover_set_state (state, startup);
1504 return ISC_R_SUCCESS;
1505 } else if (!strcmp (name, "connect-timeout")) {
1506 switch (state -> me.state) {
1507 case communications_interrupted:
1508 case partner_down:
1509 case resolution_interrupted:
1510 return ISC_R_SUCCESS;
1512 case normal:
1513 case recover:
1514 return dhcp_failover_set_state
1515 (state, communications_interrupted);
1517 case potential_conflict:
1518 return dhcp_failover_set_state
1519 (state, resolution_interrupted);
1521 case unknown_state:
1522 return dhcp_failover_set_state
1523 (state, communications_interrupted);
1525 default:
1526 return dhcp_failover_set_state
1527 (state, resolution_interrupted);
1530 return ISC_R_INVALIDARG;
1533 isc_result_t dhcp_failover_set_service_state (dhcp_failover_state_t *state)
1535 switch (state -> me.state) {
1536 case unknown_state:
1537 state -> service_state = not_responding;
1538 state -> nrr = " (my state unknown)";
1539 break;
1541 case partner_down:
1542 state -> service_state = service_partner_down;
1543 state -> nrr = "";
1544 break;
1546 case normal:
1547 state -> service_state = cooperating;
1548 state -> nrr = "";
1549 break;
1551 case communications_interrupted:
1552 state -> service_state = not_cooperating;
1553 state -> nrr = "";
1554 break;
1556 case resolution_interrupted:
1557 case potential_conflict:
1558 state -> service_state = not_responding;
1559 state -> nrr = " (resolving conflicts)";
1560 break;
1562 case recover:
1563 state -> service_state = not_responding;
1564 state -> nrr = " (recovering)";
1565 break;
1567 case shut_down:
1568 state -> service_state = not_responding;
1569 state -> nrr = " (shut down)";
1570 break;
1572 case paused:
1573 state -> service_state = not_responding;
1574 state -> nrr = " (paused)";
1575 break;
1577 case recover_wait:
1578 state -> service_state = not_responding;
1579 state -> nrr = " (recover wait)";
1580 break;
1582 case recover_done:
1583 state -> service_state = not_responding;
1584 state -> nrr = " (recover done)";
1585 break;
1587 case startup:
1588 state -> service_state = service_startup;
1589 state -> nrr = " (startup)";
1590 break;
1593 /* Some peer states can require us not to respond, even if our
1594 state doesn't. */
1595 /* XXX hm. I suspect this isn't true anymore. */
1596 if (state -> service_state != not_responding) {
1597 switch (state -> partner.state) {
1598 case partner_down:
1599 state -> service_state = not_responding;
1600 state -> nrr = " (recovering)";
1601 break;
1603 case potential_conflict:
1604 state -> service_state = not_responding;
1605 state -> nrr = " (resolving conflicts)";
1606 break;
1608 /* Other peer states don't affect our behaviour. */
1609 default:
1610 break;
1614 return ISC_R_SUCCESS;
1617 isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *state,
1618 enum failover_state new_state)
1620 enum failover_state saved_state;
1621 TIME saved_stos;
1622 struct pool *p;
1623 struct shared_network *s;
1624 struct lease *l;
1626 /* First make the transition out of the current state. */
1627 switch (state -> me.state) {
1628 case normal:
1629 /* Any updates that haven't been acked yet, we have to
1630 resend, just in case. */
1631 if (state -> ack_queue_tail) {
1632 struct lease *lp;
1634 /* Zap the flags. */
1635 for (lp = state -> ack_queue_head; lp; lp = lp -> next_pending)
1636 lp -> flags = ((lp -> flags & ~ON_ACK_QUEUE) |
1637 ON_UPDATE_QUEUE);
1639 /* Now hook the ack queue to the beginning of the update
1640 queue. */
1641 if (state -> update_queue_head) {
1642 lease_reference (&state -> ack_queue_tail -> next_pending,
1643 state -> update_queue_head, MDL);
1644 lease_dereference (&state -> update_queue_head, MDL);
1646 lease_reference (&state -> update_queue_head,
1647 state -> ack_queue_head, MDL);
1648 if (!state -> update_queue_tail) {
1649 #if defined (POINTER_DEBUG)
1650 if (state -> ack_queue_tail -> next_pending) {
1651 log_error ("next pending on ack queue tail.");
1652 abort ();
1654 #endif
1655 lease_reference (&state -> update_queue_tail,
1656 state -> ack_queue_tail, MDL);
1658 lease_dereference (&state -> ack_queue_tail, MDL);
1659 lease_dereference (&state -> ack_queue_head, MDL);
1660 state -> cur_unacked_updates = 0;
1662 cancel_timeout (dhcp_failover_keepalive, state);
1663 break;
1665 case recover:
1666 case recover_wait:
1667 case recover_done:
1668 case potential_conflict:
1669 case partner_down:
1670 case communications_interrupted:
1671 case resolution_interrupted:
1672 case startup:
1673 default:
1674 break;
1677 /* Tentatively make the transition. */
1678 saved_state = state -> me.state;
1679 saved_stos = state -> me.stos;
1681 /* Keep the old stos if we're going into recover_wait or if we're
1682 coming into or out of startup. */
1683 if (new_state != recover_wait && new_state != startup &&
1684 saved_state != startup)
1685 state -> me.stos = cur_time;
1687 /* If we're in shutdown, peer is in partner_down, and we're moving
1688 to recover, we can skip waiting for MCLT to expire. This happens
1689 when a server is moved administratively into shutdown prior to
1690 actually shutting down. Of course, if there are any updates
1691 pending we can't actually do this. */
1692 if (new_state == recover && saved_state == shut_down &&
1693 state -> partner.state == partner_down &&
1694 !state -> update_queue_head && !state -> ack_queue_head)
1695 state -> me.stos = cur_time - state -> mclt;
1697 state -> me.state = new_state;
1698 if (new_state == startup && saved_state != startup)
1699 state -> saved_state = saved_state;
1701 /* If we can't record the new state, we can't make a state transition. */
1702 if (!write_failover_state (state) || !commit_leases ()) {
1703 log_error ("Unable to record current failover state for %s",
1704 state -> name);
1705 state -> me.state = saved_state;
1706 state -> me.stos = saved_stos;
1707 return ISC_R_IOERROR;
1710 log_info ("failover peer %s: I move from %s to %s",
1711 state -> name, dhcp_failover_state_name_print (saved_state),
1712 dhcp_failover_state_name_print (state -> me.state));
1714 /* If we were in startup and we just left it, cancel the timeout. */
1715 if (new_state != startup && saved_state == startup)
1716 cancel_timeout (dhcp_failover_startup_timeout, state);
1718 /* Set our service state. */
1719 dhcp_failover_set_service_state (state);
1721 /* Tell the peer about it. */
1722 if (state -> link_to_peer)
1723 dhcp_failover_send_state (state);
1725 switch (new_state) {
1726 case normal:
1727 if (state -> partner.state == normal)
1728 dhcp_failover_state_pool_check (state);
1729 break;
1731 case potential_conflict:
1732 if (state -> i_am == primary)
1733 dhcp_failover_send_update_request (state);
1734 break;
1736 case startup:
1737 #if defined (DEBUG_FAILOVER_TIMING)
1738 log_info ("add_timeout +15 %s",
1739 "dhcp_failover_startup_timeout");
1740 #endif
1741 add_timeout (cur_time + 15,
1742 dhcp_failover_startup_timeout,
1743 state,
1744 (tvref_t)omapi_object_reference,
1745 (tvunref_t)
1746 omapi_object_dereference);
1747 break;
1749 /* If we come back in recover_wait and there's still waiting
1750 to do, set a timeout. */
1751 case recover_wait:
1752 if (state -> me.stos + state -> mclt > cur_time) {
1753 #if defined (DEBUG_FAILOVER_TIMING)
1754 log_info ("add_timeout +%d %s",
1755 (int)(cur_time -
1756 state -> me.stos + state -> mclt),
1757 "dhcp_failover_startup_timeout");
1758 #endif
1759 add_timeout ((int)(state -> me.stos + state -> mclt),
1760 dhcp_failover_recover_done,
1761 state,
1762 (tvref_t)omapi_object_reference,
1763 (tvunref_t)
1764 omapi_object_dereference);
1765 } else
1766 dhcp_failover_recover_done (state);
1767 break;
1769 case recover:
1770 if (state -> link_to_peer)
1771 dhcp_failover_send_update_request_all (state);
1772 break;
1774 case partner_down:
1775 /* For every expired lease, set a timeout for it to become free. */
1776 for (s = shared_networks; s; s = s -> next) {
1777 for (p = s -> pools; p; p = p -> next) {
1778 if (p -> failover_peer == state) {
1779 for (l = p -> expired; l; l = l -> next)
1780 l -> tsfp = state -> me.stos + state -> mclt;
1781 if (p -> next_event_time >
1782 state -> me.stos + state -> mclt) {
1783 p -> next_event_time =
1784 state -> me.stos + state -> mclt;
1785 #if defined (DEBUG_FAILOVER_TIMING)
1786 log_info ("add_timeout +%d %s",
1787 (int)(cur_time - p -> next_event_time),
1788 "pool_timer");
1789 #endif
1790 add_timeout (p -> next_event_time, pool_timer, p,
1791 (tvref_t)pool_reference,
1792 (tvunref_t)pool_dereference);
1797 break;
1800 default:
1801 break;
1804 return ISC_R_SUCCESS;
1807 isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state,
1808 failover_message_t *msg)
1810 enum failover_state previous_state = state -> partner.state;
1811 enum failover_state new_state;
1812 int startupp;
1814 new_state = msg -> server_state;
1815 startupp = (msg -> server_flags & FTF_STARTUP) ? 1 : 0;
1817 if (state -> partner.state == new_state && state -> me.state) {
1818 switch (state -> me.state) {
1819 case startup:
1820 dhcp_failover_set_state (state, state -> saved_state);
1821 return ISC_R_SUCCESS;
1823 case unknown_state:
1824 case normal:
1825 case potential_conflict:
1826 case recover_done:
1827 case shut_down:
1828 case paused:
1829 case recover_wait:
1830 return ISC_R_SUCCESS;
1832 /* If we get a peer state change when we're
1833 disconnected, we always process it. */
1834 case partner_down:
1835 case communications_interrupted:
1836 case resolution_interrupted:
1837 case recover:
1838 break;
1842 state -> partner.state = new_state;
1844 log_info ("failover peer %s: peer moves from %s to %s",
1845 state -> name,
1846 dhcp_failover_state_name_print (previous_state),
1847 dhcp_failover_state_name_print (state -> partner.state));
1849 if (!write_failover_state (state) || !commit_leases ()) {
1850 /* This is bad, but it's not fatal. Of course, if we
1851 can't write to the lease database, we're not going to
1852 get much done anyway. */
1853 log_error ("Unable to record current failover state for %s",
1854 state -> name);
1857 /* Do any state transitions that are required as a result of the
1858 peer's state transition. */
1860 switch (state -> me.state == startup ?
1861 state -> saved_state : state -> me.state) {
1862 case startup: /* can't happen. */
1863 break;
1865 case normal:
1866 switch (new_state) {
1867 case normal:
1868 dhcp_failover_state_pool_check (state);
1869 break;
1871 case communications_interrupted:
1872 break;
1874 case partner_down:
1875 if (state -> me.state == startup)
1876 dhcp_failover_set_state (state, recover);
1877 else
1878 dhcp_failover_set_state (state,
1879 potential_conflict);
1880 break;
1882 case potential_conflict:
1883 case resolution_interrupted:
1884 /* None of these transitions should ever occur. */
1885 dhcp_failover_set_state (state, shut_down);
1886 break;
1888 case recover:
1889 dhcp_failover_set_state (state, partner_down);
1890 break;
1892 case shut_down:
1893 /* XXX This one is specified, but it's specified in
1894 XXX the documentation for the shut_down state,
1895 XXX not the normal state. */
1896 dhcp_failover_set_state (state, partner_down);
1897 break;
1899 case paused:
1900 dhcp_failover_set_state (state,
1901 communications_interrupted);
1902 break;
1904 case recover_wait:
1905 case recover_done:
1906 /* We probably don't need to do anything here. */
1907 break;
1909 case unknown_state:
1910 case startup:
1911 break;
1913 break;
1915 case recover:
1916 switch (new_state) {
1917 case recover:
1918 log_info ("failover peer %s: requesting %s",
1919 state -> name, "full update from peer");
1920 /* Don't send updreqall if we're really in the
1921 startup state, because that will result in two
1922 being sent. */
1923 if (state -> me.state == recover)
1924 dhcp_failover_send_update_request_all (state);
1925 break;
1927 case potential_conflict:
1928 case resolution_interrupted:
1929 case normal:
1930 dhcp_failover_set_state (state, potential_conflict);
1931 break;
1933 case partner_down:
1934 case communications_interrupted:
1935 /* We're supposed to send an update request at this
1936 point. */
1937 /* XXX we don't currently have code here to do any
1938 XXX clever detection of when we should send an
1939 XXX UPDREQALL message rather than an UPDREQ
1940 XXX message. What to do, what to do? */
1941 /* Currently when we enter recover state, no matter
1942 * the reason, we send an UPDREQALL. So, it makes
1943 * the most sense to stick to that until something
1944 * better is done.
1945 * Furthermore, we only went to send the update
1946 * request if we are not in startup state.
1948 if (state -> me.state == recover)
1949 dhcp_failover_send_update_request_all (state);
1950 break;
1952 case shut_down:
1953 /* XXX We're not explicitly told what to do in this
1954 XXX case, but this transition is consistent with
1955 XXX what is elsewhere in the draft. */
1956 dhcp_failover_set_state (state, partner_down);
1957 break;
1959 /* We can't really do anything in this case. */
1960 case paused:
1961 break;
1963 /* We should have asked for an update already. */
1964 case recover_done:
1965 case recover_wait:
1966 break;
1968 case unknown_state:
1969 case startup:
1970 break;
1972 break;
1974 case potential_conflict:
1975 switch (new_state) {
1976 case normal:
1977 if (previous_state == potential_conflict &&
1978 state -> i_am == secondary)
1979 dhcp_failover_send_update_request (state);
1980 break;
1982 case recover_done:
1983 case recover_wait:
1984 case potential_conflict:
1985 case partner_down:
1986 case communications_interrupted:
1987 case resolution_interrupted:
1988 case paused:
1989 break;
1991 case recover:
1992 dhcp_failover_set_state (state, recover);
1993 break;
1995 case shut_down:
1996 dhcp_failover_set_state (state, partner_down);
1997 break;
1999 case unknown_state:
2000 case startup:
2001 break;
2003 break;
2005 case partner_down:
2006 /* Take no action if other server is starting up. */
2007 if (startupp)
2008 break;
2010 switch (new_state) {
2011 /* This is where we should be. */
2012 case recover:
2013 case recover_wait:
2014 break;
2016 case recover_done:
2017 dhcp_failover_set_state (state, normal);
2018 break;
2020 case normal:
2021 case potential_conflict:
2022 case partner_down:
2023 case communications_interrupted:
2024 case resolution_interrupted:
2025 dhcp_failover_set_state (state, potential_conflict);
2026 break;
2028 /* These don't change anything. */
2029 case shut_down:
2030 case paused:
2031 break;
2033 case unknown_state:
2034 case startup:
2035 break;
2037 break;
2039 case communications_interrupted:
2040 switch (new_state) {
2041 case paused:
2042 /* Stick with the status quo. */
2043 break;
2045 /* If we're in communications-interrupted and an
2046 amnesiac peer connects, go to the partner_down
2047 state immediately. */
2048 case recover:
2049 dhcp_failover_set_state (state, partner_down);
2050 break;
2052 case normal:
2053 case communications_interrupted:
2054 case recover_done:
2055 case recover_wait:
2056 /* XXX so we don't need to do this specially in
2057 XXX the CONNECT and CONNECTACK handlers. */
2058 dhcp_failover_send_updates (state);
2059 dhcp_failover_set_state (state, normal);
2060 break;
2062 case potential_conflict:
2063 case partner_down:
2064 case resolution_interrupted:
2065 dhcp_failover_set_state (state, potential_conflict);
2066 break;
2068 case shut_down:
2069 dhcp_failover_set_state (state, partner_down);
2070 break;
2072 case unknown_state:
2073 case startup:
2074 break;
2076 break;
2078 case resolution_interrupted:
2079 switch (new_state) {
2080 case normal:
2081 case recover:
2082 case potential_conflict:
2083 case partner_down:
2084 case communications_interrupted:
2085 case resolution_interrupted:
2086 case recover_done:
2087 case recover_wait:
2088 dhcp_failover_set_state (state, potential_conflict);
2089 break;
2091 case shut_down:
2092 dhcp_failover_set_state (state, partner_down);
2093 break;
2095 case paused:
2096 break;
2098 case unknown_state:
2099 case startup:
2100 break;
2102 break;
2104 case recover_done:
2105 switch (new_state) {
2106 case normal:
2107 case recover_done:
2108 dhcp_failover_set_state (state, normal);
2109 break;
2111 case potential_conflict:
2112 case partner_down:
2113 case communications_interrupted:
2114 case resolution_interrupted:
2115 case paused:
2116 case recover:
2117 case recover_wait:
2118 break;
2120 case shut_down:
2121 dhcp_failover_set_state (state, partner_down);
2122 break;
2124 case unknown_state:
2125 case startup:
2126 break;
2128 break;
2130 /* We are essentially dead in the water when we're in
2131 either shut_down or paused states, and do not do any
2132 automatic state transitions. */
2133 case shut_down:
2134 case paused:
2135 break;
2137 /* We still have to wait... */
2138 case recover_wait:
2139 break;
2141 case unknown_state:
2142 break;
2145 /* If we didn't make a transition out of startup as a result of
2146 the peer's state change, do it now as a result of the fact that
2147 we got a state change from the peer. */
2148 if (state -> me.state == startup && state -> saved_state != startup)
2149 dhcp_failover_set_state (state, state -> saved_state);
2151 /* For now, just set the service state based on the peer's state
2152 if necessary. */
2153 dhcp_failover_set_service_state (state);
2155 return ISC_R_SUCCESS;
2158 int dhcp_failover_pool_rebalance (dhcp_failover_state_t *state)
2160 int lts;
2161 int leases_queued = 0;
2162 struct lease *lp = (struct lease *)0;
2163 struct lease *next = (struct lease *)0;
2164 struct shared_network *s;
2165 struct pool *p;
2166 binding_state_t peer_lease_state;
2167 binding_state_t my_lease_state;
2168 struct lease **lq;
2169 int tenper;
2171 if (state -> me.state != normal || state -> i_am == secondary)
2172 return 0;
2174 for (s = shared_networks; s; s = s -> next) {
2175 for (p = s -> pools; p; p = p -> next) {
2176 if (p -> failover_peer != state)
2177 continue;
2179 /* Right now we're giving the peer half of the free leases.
2180 If we have more leases than the peer (i.e., more than
2181 half), then the number of leases we have, less the number
2182 of leases the peer has, will be how many more leases we
2183 have than the peer has. So if we send half that number
2184 to the peer, we should be even. */
2185 if (p -> failover_peer -> i_am == primary) {
2186 lts = (p -> free_leases - p -> backup_leases) / 2;
2187 peer_lease_state = FTS_BACKUP;
2188 my_lease_state = FTS_FREE;
2189 lq = &p -> free;
2190 } else {
2191 lts = (p -> backup_leases - p -> free_leases) / 2;
2192 peer_lease_state = FTS_FREE;
2193 my_lease_state = FTS_BACKUP;
2194 lq = &p -> backup;
2197 tenper = (p -> backup_leases + p -> free_leases) / 10;
2198 if (tenper == 0)
2199 tenper = 1;
2200 if (lts > tenper) {
2201 log_info ("pool %lx %s total %d free %d %s %d lts %d",
2202 (unsigned long)p,
2203 (p -> shared_network ?
2204 p -> shared_network -> name : ""), p -> lease_count,
2205 p -> free_leases, "backup", p -> backup_leases, lts);
2207 lease_reference (&lp, *lq, MDL);
2209 while (lp && lts) {
2210 /* Remember the next lease in the list. */
2211 if (next)
2212 lease_dereference (&next, MDL);
2213 if (lp -> next)
2214 lease_reference (&next, lp -> next, MDL);
2216 --lts;
2217 ++leases_queued;
2218 lp -> next_binding_state = peer_lease_state;
2219 lp -> tstp = cur_time;
2220 lp -> starts = cur_time;
2222 if (!supersede_lease (lp, (struct lease *)0, 0, 1, 0)
2223 || !write_lease (lp))
2225 log_info ("can't commit lease %s on giveaway",
2226 piaddr (lp -> ip_addr));
2229 lease_dereference (&lp, MDL);
2230 if (next)
2231 lease_reference (&lp, next, MDL);
2233 if (next)
2234 lease_dereference (&next, MDL);
2235 if (lp)
2236 lease_dereference (&lp, MDL);
2239 if (lts > 1) {
2240 log_info ("lease imbalance - lts = %d", lts);
2244 commit_leases();
2245 dhcp_failover_send_poolresp (state, leases_queued);
2246 dhcp_failover_send_updates (state);
2247 return leases_queued;
2250 int dhcp_failover_pool_check (struct pool *pool)
2252 int lts;
2253 struct lease *lp;
2254 int tenper;
2256 if (!pool -> failover_peer ||
2257 pool -> failover_peer -> me.state != normal)
2258 return 0;
2260 if (pool -> failover_peer -> i_am == primary)
2261 lts = (pool -> backup_leases - pool -> free_leases) / 2;
2262 else
2263 lts = (pool -> free_leases - pool -> backup_leases) / 2;
2265 log_info ("pool %lx %s total %d free %d backup %d lts %d",
2266 (unsigned long)pool,
2267 pool -> shared_network ? pool -> shared_network -> name : "",
2268 pool -> lease_count,
2269 pool -> free_leases, pool -> backup_leases, lts);
2271 tenper = (pool -> backup_leases + pool -> free_leases) / 10;
2272 if (tenper == 0)
2273 tenper = 1;
2274 if (lts > tenper) {
2275 /* XXX What about multiple pools? */
2276 if (pool -> failover_peer -> i_am == secondary) {
2277 /* Ask the primary to send us leases. */
2278 dhcp_failover_send_poolreq (pool -> failover_peer);
2279 return 1;
2280 } else {
2281 /* Figure out how many leases to skip on the backup
2282 list. We skip the earliest leases on the list
2283 to reduce the chance of trying to steal a lease
2284 that the secondary is about to allocate. */
2285 int i = pool -> backup_leases - lts;
2286 log_info ("Taking %d leases from secondary.", lts);
2287 for (lp = pool -> backup; lp; lp = lp -> next) {
2288 /* Skip to the last leases on the free
2289 list, because they are less likely
2290 to already have been allocated. */
2291 if (i)
2292 --i;
2293 else {
2294 lp -> desired_binding_state = FTS_FREE;
2295 dhcp_failover_queue_update (lp, 1);
2296 --lts;
2299 if (lts)
2300 log_info ("failed to take %d leases.", lts);
2303 return 0;
2306 int dhcp_failover_state_pool_check (dhcp_failover_state_t *state)
2308 struct shared_network *s;
2309 struct pool *p;
2311 for (s = shared_networks; s; s = s -> next) {
2312 for (p = s -> pools; p; p = p -> next) {
2313 if (p -> failover_peer != state)
2314 continue;
2315 /* Only need to request rebalance on one pool. */
2316 if (dhcp_failover_pool_check (p))
2317 return 1;
2320 return 0;
2323 isc_result_t dhcp_failover_send_updates (dhcp_failover_state_t *state)
2325 struct lease *lp = (struct lease *)0;
2326 isc_result_t status;
2328 /* Can't update peer if we're not talking to it! */
2329 if (!state -> link_to_peer)
2330 return ISC_R_SUCCESS;
2332 while ((state -> partner.max_flying_updates >
2333 state -> cur_unacked_updates) && state -> update_queue_head) {
2334 /* Grab the head of the update queue. */
2335 lease_reference (&lp, state -> update_queue_head, MDL);
2337 /* Send the update to the peer. */
2338 status = dhcp_failover_send_bind_update (state, lp);
2339 if (status != ISC_R_SUCCESS) {
2340 lease_dereference (&lp, MDL);
2341 return status;
2343 lp -> flags &= ~ON_UPDATE_QUEUE;
2345 /* Take it off the head of the update queue and put the next
2346 item in the update queue at the head. */
2347 lease_dereference (&state -> update_queue_head, MDL);
2348 if (lp -> next_pending) {
2349 lease_reference (&state -> update_queue_head,
2350 lp -> next_pending, MDL);
2351 lease_dereference (&lp -> next_pending, MDL);
2352 } else {
2353 lease_dereference (&state -> update_queue_tail, MDL);
2356 if (state -> ack_queue_head) {
2357 lease_reference
2358 (&state -> ack_queue_tail -> next_pending,
2359 lp, MDL);
2360 lease_dereference (&state -> ack_queue_tail, MDL);
2361 } else {
2362 lease_reference (&state -> ack_queue_head, lp, MDL);
2364 #if defined (POINTER_DEBUG)
2365 if (lp -> next_pending) {
2366 log_error ("ack_queue_tail: lp -> next_pending");
2367 abort ();
2369 #endif
2370 lease_reference (&state -> ack_queue_tail, lp, MDL);
2371 lp -> flags |= ON_ACK_QUEUE;
2372 lease_dereference (&lp, MDL);
2374 /* Count the object as an unacked update. */
2375 state -> cur_unacked_updates++;
2377 return ISC_R_SUCCESS;
2380 /* Queue an update for a lease. Always returns 1 at this point - it's
2381 not an error for this to be called on a lease for which there's no
2382 failover peer. */
2384 int dhcp_failover_queue_update (struct lease *lease, int immediate)
2386 dhcp_failover_state_t *state;
2388 if (!lease -> pool ||
2389 !lease -> pool -> failover_peer)
2390 return 1;
2392 /* If it's already on the update queue, leave it there. */
2393 if (lease -> flags & ON_UPDATE_QUEUE)
2394 return 1;
2396 /* Get the failover state structure for this lease. */
2397 state = lease -> pool -> failover_peer;
2399 /* If it's on the ack queue, take it off. */
2400 if (lease -> flags & ON_ACK_QUEUE)
2401 dhcp_failover_ack_queue_remove (state, lease);
2403 if (state -> update_queue_head) {
2404 lease_reference (&state -> update_queue_tail -> next_pending,
2405 lease, MDL);
2406 lease_dereference (&state -> update_queue_tail, MDL);
2407 } else {
2408 lease_reference (&state -> update_queue_head, lease, MDL);
2410 #if defined (POINTER_DEBUG)
2411 if (lease -> next_pending) {
2412 log_error ("next pending on update queue lease.");
2413 #if defined (DEBUG_RC_HISTORY)
2414 dump_rc_history (lease);
2415 #endif
2416 abort ();
2418 #endif
2419 lease_reference (&state -> update_queue_tail, lease, MDL);
2420 lease -> flags |= ON_UPDATE_QUEUE;
2421 if (immediate)
2422 dhcp_failover_send_updates (state);
2423 return 1;
2426 int dhcp_failover_send_acks (dhcp_failover_state_t *state)
2428 failover_message_t *msg = (failover_message_t *)0;
2430 /* Must commit all leases prior to acking them. */
2431 if (!commit_leases ())
2432 return 0;
2434 while (state -> toack_queue_head) {
2435 failover_message_reference
2436 (&msg, state -> toack_queue_head, MDL);
2437 failover_message_dereference
2438 (&state -> toack_queue_head, MDL);
2439 if (msg -> next) {
2440 failover_message_reference
2441 (&state -> toack_queue_head, msg -> next, MDL);
2444 dhcp_failover_send_bind_ack (state, msg, 0, (const char *)0);
2446 failover_message_dereference (&msg, MDL);
2449 if (state -> toack_queue_tail)
2450 failover_message_dereference (&state -> toack_queue_tail, MDL);
2451 state -> pending_acks = 0;
2453 return 1;
2456 void dhcp_failover_toack_queue_timeout (void *vs)
2458 dhcp_failover_state_t *state = vs;
2460 #if defined (DEBUG_FAILOVER_TIMING)
2461 log_info ("dhcp_failover_toack_queue_timeout");
2462 #endif
2464 dhcp_failover_send_acks (state);
2467 /* Queue an ack for a message. There is currently no way to queue a
2468 negative ack -- these need to be sent directly. */
2470 int dhcp_failover_queue_ack (dhcp_failover_state_t *state,
2471 failover_message_t *msg)
2473 if (state -> toack_queue_head) {
2474 failover_message_reference
2475 (&state -> toack_queue_tail -> next, msg, MDL);
2476 failover_message_dereference (&state -> toack_queue_tail, MDL);
2477 } else {
2478 failover_message_reference (&state -> toack_queue_head,
2479 msg, MDL);
2481 failover_message_reference (&state -> toack_queue_tail, msg, MDL);
2483 state -> pending_acks++;
2485 /* Flush the toack queue whenever we exceed half the number of
2486 allowed unacked updates. */
2487 if (state -> pending_acks >= state -> partner.max_flying_updates / 2) {
2488 dhcp_failover_send_acks (state);
2491 /* Schedule a timeout to flush the ack queue. */
2492 if (state -> pending_acks > 0) {
2493 #if defined (DEBUG_FAILOVER_TIMING)
2494 log_info ("add_timeout +2 %s",
2495 "dhcp_failover_toack_queue_timeout");
2496 #endif
2497 add_timeout (cur_time + 2,
2498 dhcp_failover_toack_queue_timeout, state,
2499 (tvref_t)dhcp_failover_state_reference,
2500 (tvunref_t)dhcp_failover_state_dereference);
2503 return 1;
2506 void dhcp_failover_ack_queue_remove (dhcp_failover_state_t *state,
2507 struct lease *lease)
2509 struct lease *lp;
2511 if (!(lease -> flags & ON_ACK_QUEUE))
2512 return;
2514 if (state -> ack_queue_head == lease) {
2515 lease_dereference (&state -> ack_queue_head, MDL);
2516 if (lease -> next_pending) {
2517 lease_reference (&state -> ack_queue_head,
2518 lease -> next_pending, MDL);
2519 lease_dereference (&lease -> next_pending, MDL);
2520 } else {
2521 lease_dereference (&state -> ack_queue_tail, MDL);
2523 } else {
2524 for (lp = state -> ack_queue_head;
2525 lp && lp -> next_pending != lease;
2526 lp = lp -> next_pending)
2529 if (!lp)
2530 return;
2532 lease_dereference (&lp -> next_pending, MDL);
2533 if (lease -> next_pending) {
2534 lease_reference (&lp -> next_pending,
2535 lease -> next_pending, MDL);
2536 lease_dereference (&lease -> next_pending, MDL);
2537 } else {
2538 lease_dereference (&state -> ack_queue_tail, MDL);
2539 if (lp -> next_pending) {
2540 log_error ("state -> ack_queue_tail");
2541 abort ();
2543 lease_reference (&state -> ack_queue_tail, lp, MDL);
2547 lease -> flags &= ~ON_ACK_QUEUE;
2548 state -> cur_unacked_updates--;
2551 * When updating leases as a result of an ack, we defer the commit
2552 * for performance reasons. When there are no more acks pending,
2553 * do a commit.
2555 if (state -> cur_unacked_updates == 0) {
2556 commit_leases();
2560 isc_result_t dhcp_failover_state_set_value (omapi_object_t *h,
2561 omapi_object_t *id,
2562 omapi_data_string_t *name,
2563 omapi_typed_data_t *value)
2565 isc_result_t status;
2567 if (h -> type != dhcp_type_failover_state)
2568 return ISC_R_INVALIDARG;
2570 /* This list of successful returns is completely wrong, but the
2571 fastest way to make dhcpctl do something vaguely sane when
2572 you try to change the local state. */
2574 if (!omapi_ds_strcmp (name, "name")) {
2575 return ISC_R_SUCCESS;
2576 } else if (!omapi_ds_strcmp (name, "partner-address")) {
2577 return ISC_R_SUCCESS;
2578 } else if (!omapi_ds_strcmp (name, "local-address")) {
2579 return ISC_R_SUCCESS;
2580 } else if (!omapi_ds_strcmp (name, "partner-port")) {
2581 return ISC_R_SUCCESS;
2582 } else if (!omapi_ds_strcmp (name, "local-port")) {
2583 return ISC_R_SUCCESS;
2584 } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
2585 return ISC_R_SUCCESS;
2586 } else if (!omapi_ds_strcmp (name, "mclt")) {
2587 return ISC_R_SUCCESS;
2588 } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
2589 return ISC_R_SUCCESS;
2590 } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
2591 return ISC_R_SUCCESS;
2592 } else if (!omapi_ds_strcmp (name, "partner-state")) {
2593 return ISC_R_SUCCESS;
2594 } else if (!omapi_ds_strcmp (name, "local-state")) {
2595 unsigned long l;
2596 status = omapi_get_int_value (&l, value);
2597 if (status != ISC_R_SUCCESS)
2598 return status;
2599 return dhcp_failover_set_state ((dhcp_failover_state_t *)h, l);
2600 } else if (!omapi_ds_strcmp (name, "partner-stos")) {
2601 return ISC_R_SUCCESS;
2602 } else if (!omapi_ds_strcmp (name, "local-stos")) {
2603 return ISC_R_SUCCESS;
2604 } else if (!omapi_ds_strcmp (name, "hierarchy")) {
2605 return ISC_R_SUCCESS;
2606 } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
2607 return ISC_R_SUCCESS;
2608 } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
2609 return ISC_R_SUCCESS;
2610 } else if (!omapi_ds_strcmp (name, "skew")) {
2611 return ISC_R_SUCCESS;
2612 } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
2613 return ISC_R_SUCCESS;
2614 } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
2615 return ISC_R_SUCCESS;
2618 if (h -> inner && h -> inner -> type -> set_value)
2619 return (*(h -> inner -> type -> set_value))
2620 (h -> inner, id, name, value);
2621 return ISC_R_NOTFOUND;
2624 void dhcp_failover_keepalive (void *vs)
2628 void dhcp_failover_reconnect (void *vs)
2630 dhcp_failover_state_t *state = vs;
2631 isc_result_t status;
2633 #if defined (DEBUG_FAILOVER_TIMING)
2634 log_info ("dhcp_failover_reconnect");
2635 #endif
2636 /* If we already connected the other way, let the connection
2637 recovery code initiate any retry that may be required. */
2638 if (state -> link_to_peer)
2639 return;
2641 status = dhcp_failover_link_initiate ((omapi_object_t *)state);
2642 if (status != ISC_R_SUCCESS && status != ISC_R_INCOMPLETE) {
2643 log_info ("failover peer %s: %s", state -> name,
2644 isc_result_totext (status));
2645 #if defined (DEBUG_FAILOVER_TIMING)
2646 log_info ("add_timeout +90 %s",
2647 "dhcp_failover_listener_restart");
2648 #endif
2649 add_timeout (cur_time + 90,
2650 dhcp_failover_listener_restart, state,
2651 (tvref_t)dhcp_failover_state_reference,
2652 (tvunref_t)dhcp_failover_state_dereference);
2656 void dhcp_failover_startup_timeout (void *vs)
2658 dhcp_failover_state_t *state = vs;
2660 #if defined (DEBUG_FAILOVER_TIMING)
2661 log_info ("dhcp_failover_startup_timeout");
2662 #endif
2664 dhcp_failover_state_transition (state, "disconnect");
2667 void dhcp_failover_link_startup_timeout (void *vl)
2669 dhcp_failover_link_t *link = vl;
2670 omapi_object_t *p;
2672 for (p = (omapi_object_t *)link; p -> inner; p = p -> inner)
2674 for (; p; p = p -> outer)
2675 if (p -> type == omapi_type_connection)
2676 break;
2677 if (p) {
2678 log_info ("failover: link startup timeout");
2679 omapi_disconnect (p, 1);
2683 void dhcp_failover_listener_restart (void *vs)
2685 dhcp_failover_state_t *state = vs;
2686 isc_result_t status;
2688 #if defined (DEBUG_FAILOVER_TIMING)
2689 log_info ("dhcp_failover_listener_restart");
2690 #endif
2692 status = dhcp_failover_listen ((omapi_object_t *)state);
2693 if (status != ISC_R_SUCCESS) {
2694 log_info ("failover peer %s: %s", state -> name,
2695 isc_result_totext (status));
2696 #if defined (DEBUG_FAILOVER_TIMING)
2697 log_info ("add_timeout +90 %s",
2698 "dhcp_failover_listener_restart");
2699 #endif
2700 add_timeout (cur_time + 90,
2701 dhcp_failover_listener_restart, state,
2702 (tvref_t)dhcp_failover_state_reference,
2703 (tvunref_t)dhcp_failover_state_dereference);
2707 isc_result_t dhcp_failover_state_get_value (omapi_object_t *h,
2708 omapi_object_t *id,
2709 omapi_data_string_t *name,
2710 omapi_value_t **value)
2712 dhcp_failover_state_t *s;
2713 struct option_cache *oc;
2714 struct data_string ds;
2715 isc_result_t status;
2717 if (h -> type != dhcp_type_failover_state)
2718 return ISC_R_INVALIDARG;
2719 s = (dhcp_failover_state_t *)h;
2721 if (!omapi_ds_strcmp (name, "name")) {
2722 if (s -> name)
2723 return omapi_make_string_value (value,
2724 name, s -> name, MDL);
2725 return ISC_R_NOTFOUND;
2726 } else if (!omapi_ds_strcmp (name, "partner-address")) {
2727 oc = s -> partner.address;
2728 getaddr:
2729 memset (&ds, 0, sizeof ds);
2730 if (!evaluate_option_cache (&ds, (struct packet *)0,
2731 (struct lease *)0,
2732 (struct client_state *)0,
2733 (struct option_state *)0,
2734 (struct option_state *)0,
2735 &global_scope, oc, MDL)) {
2736 return ISC_R_NOTFOUND;
2738 status = omapi_make_const_value (value,
2739 name, ds.data, ds.len, MDL);
2740 /* Disgusting kludge: */
2741 if (oc == s -> me.address && !s -> server_identifier.len)
2742 data_string_copy (&s -> server_identifier, &ds, MDL);
2743 data_string_forget (&ds, MDL);
2744 return status;
2745 } else if (!omapi_ds_strcmp (name, "local-address")) {
2746 oc = s -> me.address;
2747 goto getaddr;
2748 } else if (!omapi_ds_strcmp (name, "partner-port")) {
2749 return omapi_make_int_value (value, name,
2750 s -> partner.port, MDL);
2751 } else if (!omapi_ds_strcmp (name, "local-port")) {
2752 return omapi_make_int_value (value,
2753 name, s -> me.port, MDL);
2754 } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
2755 return omapi_make_uint_value (value, name,
2756 s -> me.max_flying_updates,
2757 MDL);
2758 } else if (!omapi_ds_strcmp (name, "mclt")) {
2759 return omapi_make_uint_value (value, name, s -> mclt, MDL);
2760 } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
2761 return omapi_make_int_value (value, name,
2762 s -> load_balance_max_secs, MDL);
2763 } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
2764 if (s -> hba)
2765 return omapi_make_const_value (value, name,
2766 s -> hba, 32, MDL);
2767 return ISC_R_NOTFOUND;
2768 } else if (!omapi_ds_strcmp (name, "partner-state")) {
2769 return omapi_make_uint_value (value, name,
2770 s -> partner.state, MDL);
2771 } else if (!omapi_ds_strcmp (name, "local-state")) {
2772 return omapi_make_uint_value (value, name,
2773 s -> me.state, MDL);
2774 } else if (!omapi_ds_strcmp (name, "partner-stos")) {
2775 return omapi_make_int_value (value, name,
2776 s -> partner.stos, MDL);
2777 } else if (!omapi_ds_strcmp (name, "local-stos")) {
2778 return omapi_make_int_value (value, name,
2779 s -> me.stos, MDL);
2780 } else if (!omapi_ds_strcmp (name, "hierarchy")) {
2781 return omapi_make_uint_value (value, name, s -> i_am, MDL);
2782 } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
2783 return omapi_make_int_value (value, name,
2784 s -> last_packet_sent, MDL);
2785 } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
2786 return omapi_make_int_value (value, name,
2787 s -> last_timestamp_received,
2788 MDL);
2789 } else if (!omapi_ds_strcmp (name, "skew")) {
2790 return omapi_make_int_value (value, name, s -> skew, MDL);
2791 } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
2792 return omapi_make_uint_value (value, name,
2793 s -> me.max_response_delay,
2794 MDL);
2795 } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
2796 return omapi_make_int_value (value, name,
2797 s -> cur_unacked_updates, MDL);
2800 if (h -> inner && h -> inner -> type -> get_value)
2801 return (*(h -> inner -> type -> get_value))
2802 (h -> inner, id, name, value);
2803 return ISC_R_NOTFOUND;
2806 isc_result_t dhcp_failover_state_destroy (omapi_object_t *h,
2807 const char *file, int line)
2809 dhcp_failover_state_t *s;
2811 if (h -> type != dhcp_type_failover_state)
2812 return ISC_R_INVALIDARG;
2813 s = (dhcp_failover_state_t *)h;
2815 if (s -> link_to_peer)
2816 dhcp_failover_link_dereference (&s -> link_to_peer, file, line);
2817 if (s -> name) {
2818 dfree (s -> name, MDL);
2819 s -> name = (char *)0;
2821 if (s -> partner.address)
2822 option_cache_dereference (&s -> partner.address, file, line);
2823 if (s -> me.address)
2824 option_cache_dereference (&s -> me.address, file, line);
2825 if (s -> hba) {
2826 dfree (s -> hba, file, line);
2827 s -> hba = (u_int8_t *)0;
2829 if (s -> update_queue_head)
2830 lease_dereference (&s -> update_queue_head, file, line);
2831 if (s -> update_queue_tail)
2832 lease_dereference (&s -> update_queue_tail, file, line);
2833 if (s -> ack_queue_head)
2834 lease_dereference (&s -> ack_queue_head, file, line);
2835 if (s -> ack_queue_tail)
2836 lease_dereference (&s -> ack_queue_tail, file, line);
2837 if (s -> send_update_done)
2838 lease_dereference (&s -> send_update_done, file, line);
2839 if (s -> toack_queue_head)
2840 failover_message_dereference (&s -> toack_queue_head,
2841 file, line);
2842 if (s -> toack_queue_tail)
2843 failover_message_dereference (&s -> toack_queue_tail,
2844 file, line);
2845 return ISC_R_SUCCESS;
2848 /* Write all the published values associated with the object through the
2849 specified connection. */
2851 isc_result_t dhcp_failover_state_stuff (omapi_object_t *c,
2852 omapi_object_t *id,
2853 omapi_object_t *h)
2855 dhcp_failover_state_t *s;
2856 omapi_connection_object_t *conn;
2857 isc_result_t status;
2859 if (c -> type != omapi_type_connection)
2860 return ISC_R_INVALIDARG;
2861 conn = (omapi_connection_object_t *)c;
2863 if (h -> type != dhcp_type_failover_state)
2864 return ISC_R_INVALIDARG;
2865 s = (dhcp_failover_state_t *)h;
2867 status = omapi_connection_put_name (c, "name");
2868 if (status != ISC_R_SUCCESS)
2869 return status;
2870 status = omapi_connection_put_string (c, s -> name);
2871 if (status != ISC_R_SUCCESS)
2872 return status;
2874 status = omapi_connection_put_name (c, "partner-address");
2875 if (status != ISC_R_SUCCESS)
2876 return status;
2877 status = omapi_connection_put_uint32 (c, sizeof s -> partner.address);
2878 if (status != ISC_R_SUCCESS)
2879 return status;
2880 status = omapi_connection_copyin (c, (u_int8_t *)&s -> partner.address,
2881 sizeof s -> partner.address);
2882 if (status != ISC_R_SUCCESS)
2883 return status;
2885 status = omapi_connection_put_name (c, "partner-port");
2886 if (status != ISC_R_SUCCESS)
2887 return status;
2888 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2889 if (status != ISC_R_SUCCESS)
2890 return status;
2891 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> partner.port);
2892 if (status != ISC_R_SUCCESS)
2893 return status;
2895 status = omapi_connection_put_name (c, "local-address");
2896 if (status != ISC_R_SUCCESS)
2897 return status;
2898 status = omapi_connection_put_uint32 (c, sizeof s -> me.address);
2899 if (status != ISC_R_SUCCESS)
2900 return status;
2901 status = omapi_connection_copyin (c, (u_int8_t *)&s -> me.address,
2902 sizeof s -> me.address);
2903 if (status != ISC_R_SUCCESS)
2904 return status;
2906 status = omapi_connection_put_name (c, "local-port");
2907 if (status != ISC_R_SUCCESS)
2908 return status;
2909 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2910 if (status != ISC_R_SUCCESS)
2911 return status;
2912 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.port);
2913 if (status != ISC_R_SUCCESS)
2914 return status;
2916 status = omapi_connection_put_name (c, "max-outstanding-updates");
2917 if (status != ISC_R_SUCCESS)
2918 return status;
2919 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2920 if (status != ISC_R_SUCCESS)
2921 return status;
2922 status = omapi_connection_put_uint32 (c,
2923 s -> me.max_flying_updates);
2924 if (status != ISC_R_SUCCESS)
2925 return status;
2927 status = omapi_connection_put_name (c, "mclt");
2928 if (status != ISC_R_SUCCESS)
2929 return status;
2930 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2931 if (status != ISC_R_SUCCESS)
2932 return status;
2933 status = omapi_connection_put_uint32 (c, s -> mclt);
2934 if (status != ISC_R_SUCCESS)
2935 return status;
2937 status = omapi_connection_put_name (c, "load-balance-max-secs");
2938 if (status != ISC_R_SUCCESS)
2939 return status;
2940 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2941 if (status != ISC_R_SUCCESS)
2942 return status;
2943 status = (omapi_connection_put_uint32
2944 (c, (u_int32_t)s -> load_balance_max_secs));
2945 if (status != ISC_R_SUCCESS)
2946 return status;
2949 if (s -> hba) {
2950 status = omapi_connection_put_name (c, "load-balance-hba");
2951 if (status != ISC_R_SUCCESS)
2952 return status;
2953 status = omapi_connection_put_uint32 (c, 32);
2954 if (status != ISC_R_SUCCESS)
2955 return status;
2956 status = omapi_connection_copyin (c, s -> hba, 32);
2957 if (status != ISC_R_SUCCESS)
2958 return status;
2961 status = omapi_connection_put_name (c, "partner-state");
2962 if (status != ISC_R_SUCCESS)
2963 return status;
2964 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2965 if (status != ISC_R_SUCCESS)
2966 return status;
2967 status = omapi_connection_put_uint32 (c, s -> partner.state);
2968 if (status != ISC_R_SUCCESS)
2969 return status;
2971 status = omapi_connection_put_name (c, "local-state");
2972 if (status != ISC_R_SUCCESS)
2973 return status;
2974 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2975 if (status != ISC_R_SUCCESS)
2976 return status;
2977 status = omapi_connection_put_uint32 (c, s -> me.state);
2978 if (status != ISC_R_SUCCESS)
2979 return status;
2981 status = omapi_connection_put_name (c, "partner-stos");
2982 if (status != ISC_R_SUCCESS)
2983 return status;
2984 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2985 if (status != ISC_R_SUCCESS)
2986 return status;
2987 status = omapi_connection_put_uint32 (c,
2988 (u_int32_t)s -> partner.stos);
2989 if (status != ISC_R_SUCCESS)
2990 return status;
2992 status = omapi_connection_put_name (c, "local-stos");
2993 if (status != ISC_R_SUCCESS)
2994 return status;
2995 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2996 if (status != ISC_R_SUCCESS)
2997 return status;
2998 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.stos);
2999 if (status != ISC_R_SUCCESS)
3000 return status;
3002 status = omapi_connection_put_name (c, "hierarchy");
3003 if (status != ISC_R_SUCCESS)
3004 return status;
3005 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3006 if (status != ISC_R_SUCCESS)
3007 return status;
3008 status = omapi_connection_put_uint32 (c, s -> i_am);
3009 if (status != ISC_R_SUCCESS)
3010 return status;
3012 status = omapi_connection_put_name (c, "last-packet-sent");
3013 if (status != ISC_R_SUCCESS)
3014 return status;
3015 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3016 if (status != ISC_R_SUCCESS)
3017 return status;
3018 status = (omapi_connection_put_uint32
3019 (c, (u_int32_t)s -> last_packet_sent));
3020 if (status != ISC_R_SUCCESS)
3021 return status;
3023 status = omapi_connection_put_name (c, "last-timestamp-received");
3024 if (status != ISC_R_SUCCESS)
3025 return status;
3026 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3027 if (status != ISC_R_SUCCESS)
3028 return status;
3029 status = (omapi_connection_put_uint32
3030 (c, (u_int32_t)s -> last_timestamp_received));
3031 if (status != ISC_R_SUCCESS)
3032 return status;
3034 status = omapi_connection_put_name (c, "skew");
3035 if (status != ISC_R_SUCCESS)
3036 return status;
3037 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3038 if (status != ISC_R_SUCCESS)
3039 return status;
3040 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> skew);
3041 if (status != ISC_R_SUCCESS)
3042 return status;
3044 status = omapi_connection_put_name (c, "max-response-delay");
3045 if (status != ISC_R_SUCCESS)
3046 return status;
3047 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3048 if (status != ISC_R_SUCCESS)
3049 return status;
3050 status = (omapi_connection_put_uint32
3051 (c, (u_int32_t)s -> me.max_response_delay));
3052 if (status != ISC_R_SUCCESS)
3053 return status;
3055 status = omapi_connection_put_name (c, "cur-unacked-updates");
3056 if (status != ISC_R_SUCCESS)
3057 return status;
3058 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3059 if (status != ISC_R_SUCCESS)
3060 return status;
3061 status = (omapi_connection_put_uint32
3062 (c, (u_int32_t)s -> cur_unacked_updates));
3063 if (status != ISC_R_SUCCESS)
3064 return status;
3066 if (h -> inner && h -> inner -> type -> stuff_values)
3067 return (*(h -> inner -> type -> stuff_values)) (c, id,
3068 h -> inner);
3069 return ISC_R_SUCCESS;
3072 isc_result_t dhcp_failover_state_lookup (omapi_object_t **sp,
3073 omapi_object_t *id,
3074 omapi_object_t *ref)
3076 omapi_value_t *tv = (omapi_value_t *)0;
3077 isc_result_t status;
3078 dhcp_failover_state_t *s;
3080 if (!ref)
3081 return ISC_R_NOKEYS;
3083 /* First see if we were sent a handle. */
3084 status = omapi_get_value_str (ref, id, "handle", &tv);
3085 if (status == ISC_R_SUCCESS) {
3086 status = omapi_handle_td_lookup (sp, tv -> value);
3088 omapi_value_dereference (&tv, MDL);
3089 if (status != ISC_R_SUCCESS)
3090 return status;
3092 /* Don't return the object if the type is wrong. */
3093 if ((*sp) -> type != dhcp_type_failover_state) {
3094 omapi_object_dereference (sp, MDL);
3095 return ISC_R_INVALIDARG;
3099 /* Look the failover state up by peer name. */
3100 status = omapi_get_value_str (ref, id, "name", &tv);
3101 if (status == ISC_R_SUCCESS) {
3102 for (s = failover_states; s; s = s -> next) {
3103 unsigned l = strlen (s -> name);
3104 if (l == tv -> value -> u.buffer.len &&
3105 !memcmp (s -> name,
3106 tv -> value -> u.buffer.value, l))
3107 break;
3109 omapi_value_dereference (&tv, MDL);
3111 /* If we already have a lease, and it's not the same one,
3112 then the query was invalid. */
3113 if (*sp && *sp != (omapi_object_t *)s) {
3114 omapi_object_dereference (sp, MDL);
3115 return ISC_R_KEYCONFLICT;
3116 } else if (!s) {
3117 if (*sp)
3118 omapi_object_dereference (sp, MDL);
3119 return ISC_R_NOTFOUND;
3120 } else if (!*sp)
3121 /* XXX fix so that hash lookup itself creates
3122 XXX the reference. */
3123 omapi_object_reference (sp, (omapi_object_t *)s, MDL);
3126 /* If we get to here without finding a lease, no valid key was
3127 specified. */
3128 if (!*sp)
3129 return ISC_R_NOKEYS;
3130 return ISC_R_SUCCESS;
3133 isc_result_t dhcp_failover_state_create (omapi_object_t **sp,
3134 omapi_object_t *id)
3136 return ISC_R_NOTIMPLEMENTED;
3139 isc_result_t dhcp_failover_state_remove (omapi_object_t *sp,
3140 omapi_object_t *id)
3142 return ISC_R_NOTIMPLEMENTED;
3145 int dhcp_failover_state_match (dhcp_failover_state_t *state,
3146 u_int8_t *addr, unsigned addrlen)
3148 struct data_string ds;
3149 int i;
3151 memset (&ds, 0, sizeof ds);
3152 if (evaluate_option_cache (&ds, (struct packet *)0,
3153 (struct lease *)0,
3154 (struct client_state *)0,
3155 (struct option_state *)0,
3156 (struct option_state *)0,
3157 &global_scope,
3158 state -> partner.address, MDL)) {
3159 for (i = 0; i + addrlen - 1 < ds.len; i += addrlen) {
3160 if (!memcmp (&ds.data [i],
3161 addr, addrlen)) {
3162 data_string_forget (&ds, MDL);
3163 return 1;
3166 data_string_forget (&ds, MDL);
3168 return 0;
3171 const char *dhcp_failover_reject_reason_print (int reason)
3173 switch (reason) {
3174 case FTR_ILLEGAL_IP_ADDR:
3175 return "Illegal IP address (not part of any address pool).";
3177 case FTR_FATAL_CONFLICT:
3178 return "Fatal conflict exists: address in use by other client.";
3180 case FTR_MISSING_BINDINFO:
3181 return "Missing binding information.";
3183 case FTR_TIMEMISMATCH:
3184 return "Connection rejected, time mismatch too great.";
3186 case FTR_INVALID_MCLT:
3187 return "Connection rejected, invalid MCLT.";
3189 case FTR_MISC_REJECT:
3190 return "Connection rejected, unknown reason.";
3192 case FTR_DUP_CONNECTION:
3193 return "Connection rejected, duplicate connection.";
3195 case FTR_INVALID_PARTNER:
3196 return "Connection rejected, invalid failover partner.";
3198 case FTR_TLS_UNSUPPORTED:
3199 return "TLS not supported.";
3201 case FTR_TLS_UNCONFIGURED:
3202 return "TLS supported but not configured.";
3204 case FTR_TLS_REQUIRED:
3205 return "TLS required but not supported by partner.";
3207 case FTR_DIGEST_UNSUPPORTED:
3208 return "Message digest not supported.";
3210 case FTR_DIGEST_UNCONFIGURED:
3211 return "Message digest not configured.";
3213 case FTR_VERSION_MISMATCH:
3214 return "Protocol version mismatch.";
3216 case FTR_MISSING_BIND_INFO:
3217 return "Missing binding information.";
3219 case FTR_OUTDATED_BIND_INFO:
3220 return "Outdated binding information.";
3222 case FTR_LESS_CRIT_BIND_INFO:
3223 return "Less critical binding information.";
3225 case FTR_NO_TRAFFIC:
3226 return "No traffic within sufficient time.";
3228 case FTR_HBA_CONFLICT:
3229 return "Hash bucket assignment conflict.";
3231 default:
3232 case FTR_UNKNOWN:
3233 return "Unknown: Error occurred but does not match any reason code.";
3237 const char *dhcp_failover_state_name_print (enum failover_state state)
3239 switch (state) {
3240 default:
3241 case unknown_state:
3242 return "unknown-state";
3244 case partner_down:
3245 return "partner-down";
3247 case normal:
3248 return "normal";
3250 case communications_interrupted:
3251 return "communications-interrupted";
3253 case resolution_interrupted:
3254 return "resolution-interrupted";
3256 case potential_conflict:
3257 return "potential-conflict";
3259 case recover:
3260 return "recover";
3262 case recover_done:
3263 return "recover-done";
3265 case recover_wait:
3266 return "recover-wait";
3268 case shut_down:
3269 return "shutdown";
3271 case paused:
3272 return "paused";
3274 case startup:
3275 return "startup";
3279 const char *dhcp_failover_message_name (unsigned type)
3281 switch (type) {
3282 case FTM_POOLREQ:
3283 return "pool-request";
3285 case FTM_POOLRESP:
3286 return "pool-response";
3288 case FTM_BNDUPD:
3289 return "bind-update";
3291 case FTM_BNDACK:
3292 return "bind-ack";
3294 case FTM_CONNECT:
3295 return "connect";
3297 case FTM_CONNECTACK:
3298 return "connect-ack";
3300 case FTM_UPDREQ:
3301 return "update-request";
3303 case FTM_UPDDONE:
3304 return "update-done";
3306 case FTM_UPDREQALL:
3307 return "update-request-all";
3309 case FTM_STATE:
3310 return "state";
3312 case FTM_CONTACT:
3313 return "contact";
3315 case FTM_DISCONNECT:
3316 return "disconnect";
3318 default:
3319 return "<unknown message type>";
3323 const char *dhcp_failover_option_name (unsigned type)
3325 switch (type) {
3326 case FTO_BINDING_STATUS:
3327 return "binding-status";
3329 case FTO_ASSIGNED_IP_ADDRESS:
3330 return "assigned-ip-address";
3332 case FTO_SERVER_ADDR:
3333 return "server-addr";
3335 case FTO_ADDRESSES_TRANSFERRED:
3336 return "addresses-transferred";
3338 case FTO_CLIENT_IDENTIFIER:
3339 return "client-identifier";
3341 case FTO_CHADDR:
3342 return "chaddr";
3344 case FTO_DDNS:
3345 return "ddns";
3347 case FTO_REJECT_REASON:
3348 return "reject-reason";
3350 case FTO_MESSAGE:
3351 return "message";
3353 case FTO_MCLT:
3354 return "mclt";
3356 case FTO_VENDOR_CLASS:
3357 return "vendor-class";
3359 case FTO_LEASE_EXPIRY:
3360 return "lease-expiry";
3362 case FTO_POTENTIAL_EXPIRY:
3363 return "potential-expiry";
3365 case FTO_GRACE_EXPIRY:
3366 return "grace-expiry";
3368 case FTO_CLTT:
3369 return "cltt";
3371 case FTO_STOS:
3372 return "stos";
3374 case FTO_SERVER_STATE:
3375 return "server-state";
3377 case FTO_SERVER_FLAGS:
3378 return "server-flags";
3380 case FTO_VENDOR_OPTIONS:
3381 return "vendor-options";
3383 case FTO_MAX_UNACKED:
3384 return "max-unacked";
3386 case FTO_RECEIVE_TIMER:
3387 return "receive-timer";
3389 case FTO_HBA:
3390 return "hba";
3392 case FTO_MESSAGE_DIGEST:
3393 return "message-digest";
3395 case FTO_PROTOCOL_VERSION:
3396 return "protocol-version";
3398 case FTO_TLS_REQUEST:
3399 return "tls-request";
3401 case FTO_TLS_REPLY:
3402 return "tls-reply";
3404 case FTO_REQUEST_OPTIONS:
3405 return "request-options";
3407 case FTO_REPLY_OPTIONS:
3408 return "reply-options";
3410 default:
3411 return "<unknown option>";
3415 failover_option_t *dhcp_failover_option_printf (unsigned code,
3416 char *obuf,
3417 unsigned *obufix,
3418 unsigned obufmax,
3419 const char *fmt, ...)
3421 va_list va;
3422 char tbuf [256];
3424 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3425 * It is unclear what the effects of truncation here are, or
3426 * how that condition should be handled. It seems that this
3427 * function is used for formatting messages in the failover
3428 * command channel. For now the safest thing is for
3429 * overflow-truncation to cause a fatal log.
3431 va_start (va, fmt);
3432 if (vsnprintf (tbuf, sizeof tbuf, fmt, va) >= sizeof tbuf)
3433 log_fatal ("%s: vsnprintf would truncate",
3434 "dhcp_failover_make_option");
3435 va_end (va);
3437 return dhcp_failover_make_option (code, obuf, obufix, obufmax,
3438 strlen (tbuf), tbuf);
3441 failover_option_t *dhcp_failover_make_option (unsigned code,
3442 char *obuf, unsigned *obufix,
3443 unsigned obufmax, ...)
3445 va_list va;
3446 struct failover_option_info *info;
3447 int i;
3448 unsigned size, count;
3449 unsigned val;
3450 u_int8_t *iaddr;
3451 unsigned ilen = 0;
3452 u_int8_t *bval;
3453 char *txt = NULL;
3454 #if defined (DEBUG_FAILOVER_MESSAGES)
3455 char tbuf [256];
3456 #endif
3457 failover_option_t option, *op;
3459 size = 0; /* XXXGCC -Wuninitialized */
3460 ilen = 0; /* XXXGCC -Wuninitialized */
3461 txt = NULL; /* XXXGCC -Wuninitialized */
3463 /* Note that the failover_option structure is used differently on
3464 input than on output - on input, count is an element count, and
3465 on output it's the number of bytes total in the option, including
3466 the option code and option length. */
3469 /* Bogus option code? */
3470 if (code < 1 || code > FTO_MAX || ft_options [code].type == FT_UNDEF) {
3471 return &null_failover_option;
3473 info = &ft_options [code];
3475 va_start (va, obufmax);
3477 /* Get the number of elements and the size of the buffer we need
3478 to allocate. */
3479 if (info -> type == FT_DDNS || info -> type == FT_DDNS1) {
3480 count = info -> type == FT_DDNS ? 1 : 2;
3481 size = va_arg (va, int) + count;
3482 } else {
3483 /* Find out how many items in this list. */
3484 if (info -> num_present)
3485 count = info -> num_present;
3486 else
3487 count = va_arg (va, int);
3489 /* Figure out size. */
3490 switch (info -> type) {
3491 case FT_UINT8:
3492 case FT_BYTES:
3493 case FT_DIGEST:
3494 size = count;
3495 break;
3497 case FT_TEXT_OR_BYTES:
3498 case FT_TEXT:
3499 txt = va_arg (va, char *);
3500 size = count;
3501 break;
3503 case FT_IPADDR:
3504 ilen = va_arg (va, unsigned);
3505 size = count * ilen;
3506 break;
3508 case FT_UINT32:
3509 size = count * 4;
3510 break;
3512 case FT_UINT16:
3513 size = count * 2;
3514 break;
3516 default:
3517 /* shouldn't get here. */
3518 log_fatal ("bogus type in failover_make_option: %d",
3519 info -> type);
3520 return &null_failover_option;
3524 size += 4;
3526 /* Allocate a buffer for the option. */
3527 option.count = size;
3528 option.data = dmalloc (option.count, MDL);
3529 if (!option.data) {
3530 va_end (va);
3531 return &null_failover_option;
3534 /* Put in the option code and option length. */
3535 putUShort (option.data, code);
3536 putUShort (&option.data [2], size - 4);
3538 #if defined (DEBUG_FAILOVER_MESSAGES)
3539 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3540 * It is unclear what the effects of truncation here are, or
3541 * how that condition should be handled. It seems that this
3542 * message may be sent over the failover command channel.
3543 * For now the safest thing is for overflow-truncation to cause
3544 * a fatal log.
3546 if (snprintf (tbuf, sizeof tbuf, " (%s<%d>", info -> name,
3547 option.count) >= sizeof tbuf)
3548 log_fatal ("dhcp_failover_make_option: tbuf overflow");
3549 failover_print (obuf, obufix, obufmax, tbuf);
3550 #endif
3552 /* Now put in the data. */
3553 switch (info -> type) {
3554 case FT_UINT8:
3555 for (i = 0; i < count; i++) {
3556 val = va_arg (va, unsigned);
3557 #if defined (DEBUG_FAILOVER_MESSAGES)
3558 /* %Audit% Cannot exceed 24 bytes. %2004.06.17,Safe% */
3559 sprintf (tbuf, " %d", val);
3560 failover_print (obuf, obufix, obufmax, tbuf);
3561 #endif
3562 option.data [i + 4] = val;
3564 break;
3566 case FT_IPADDR:
3567 for (i = 0; i < count; i++) {
3568 iaddr = va_arg (va, u_int8_t *);
3569 if (ilen != 4) {
3570 dfree (option.data, MDL);
3571 log_error ("IP addrlen=%d, should be 4.",
3572 ilen);
3573 va_end (va);
3574 return &null_failover_option;
3577 #if defined (DEBUG_FAILOVER_MESSAGES)
3578 /*%Audit% Cannot exceed 17 bytes. %2004.06.17,Safe%*/
3579 sprintf (tbuf, " %u.%u.%u.%u",
3580 iaddr [0], iaddr [1], iaddr [2], iaddr [3]);
3581 failover_print (obuf, obufix, obufmax, tbuf);
3582 #endif
3583 memcpy (&option.data [4 + i * ilen], iaddr, ilen);
3585 break;
3587 case FT_UINT32:
3588 for (i = 0; i < count; i++) {
3589 val = va_arg (va, unsigned);
3590 #if defined (DEBUG_FAILOVER_MESSAGES)
3591 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3592 sprintf (tbuf, " %d", val);
3593 failover_print (obuf, obufix, obufmax, tbuf);
3594 #endif
3595 putULong (&option.data [4 + i * 4], val);
3597 break;
3599 case FT_BYTES:
3600 case FT_DIGEST:
3601 bval = va_arg (va, u_int8_t *);
3602 #if defined (DEBUG_FAILOVER_MESSAGES)
3603 for (i = 0; i < count; i++) {
3604 /* 23 bytes plus nul, safe. */
3605 sprintf (tbuf, " %d", bval [i]);
3606 failover_print (obuf, obufix, obufmax, tbuf);
3608 #endif
3609 memcpy (&option.data [4], bval, count);
3610 break;
3612 /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
3613 terminated. Note that the caller should be careful not
3614 to provide a format and data that amount to more than 256
3615 bytes of data, since it will cause a fatal error. */
3616 case FT_TEXT_OR_BYTES:
3617 case FT_TEXT:
3618 #if defined (DEBUG_FAILOVER_MESSAGES)
3619 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3620 * It is unclear what the effects of truncation here are, or
3621 * how that condition should be handled. It seems that this
3622 * function is used for formatting messages in the failover
3623 * command channel. For now the safest thing is for
3624 * overflow-truncation to cause a fatal log.
3626 if (snprintf (tbuf, sizeof tbuf, "\"%s\"", txt) >= sizeof tbuf)
3627 log_fatal ("dhcp_failover_make_option: tbuf overflow");
3628 failover_print (obuf, obufix, obufmax, tbuf);
3629 #endif
3630 memcpy (&option.data [4], txt, count);
3631 break;
3633 case FT_DDNS:
3634 case FT_DDNS1:
3635 option.data [4] = va_arg (va, unsigned);
3636 if (count == 2)
3637 option.data [5] = va_arg (va, unsigned);
3638 bval = va_arg (va, u_int8_t *);
3639 memcpy (&option.data [4 + count], bval, size - count - 4);
3640 #if defined (DEBUG_FAILOVER_MESSAGES)
3641 for (i = 4; i < size; i++) {
3642 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3643 sprintf (tbuf, " %d", option.data [i]);
3644 failover_print (obuf, obufix, obufmax, tbuf);
3646 #endif
3647 break;
3649 case FT_UINT16:
3650 for (i = 0; i < count; i++) {
3651 val = va_arg (va, u_int32_t);
3652 #if defined (DEBUG_FAILOVER_MESSAGES)
3653 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3654 sprintf (tbuf, " %d", val);
3655 failover_print (obuf, obufix, obufmax, tbuf);
3656 #endif
3657 putUShort (&option.data [4 + i * 2], val);
3659 break;
3661 case FT_UNDEF:
3662 default:
3663 break;
3666 #if defined DEBUG_FAILOVER_MESSAGES
3667 failover_print (obuf, obufix, obufmax, ")");
3668 #endif
3669 va_end (va);
3671 /* Now allocate a place to store what we just set up. */
3672 op = dmalloc (sizeof (failover_option_t), MDL);
3673 if (!op) {
3674 dfree (option.data, MDL);
3675 return &null_failover_option;
3678 *op = option;
3679 return op;
3682 /* Send a failover message header. */
3684 isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link,
3685 omapi_object_t *connection,
3686 int msg_type, ...)
3688 unsigned size = 0;
3689 int bad_option = 0;
3690 int opix = 0;
3691 va_list list;
3692 failover_option_t *option;
3693 unsigned char *opbuf;
3694 isc_result_t status = ISC_R_SUCCESS;
3695 unsigned char cbuf;
3697 /* Run through the argument list once to compute the length of
3698 the option portion of the message. */
3699 va_start (list, msg_type);
3700 while ((option = va_arg (list, failover_option_t *))) {
3701 if (option != &skip_failover_option)
3702 size += option -> count;
3703 if (option == &null_failover_option)
3704 bad_option = 1;
3706 va_end (list);
3708 /* Allocate an option buffer, unless we got an error. */
3709 if (!bad_option && size) {
3710 opbuf = dmalloc (size, MDL);
3711 if (!opbuf)
3712 status = ISC_R_NOMEMORY;
3713 } else
3714 opbuf = (unsigned char *)0;
3716 va_start (list, msg_type);
3717 while ((option = va_arg (list, failover_option_t *))) {
3718 if (option == &skip_failover_option)
3719 continue;
3720 if (!bad_option && opbuf)
3721 memcpy (&opbuf [opix],
3722 option -> data, option -> count);
3723 if (option != &null_failover_option &&
3724 option != &skip_failover_option) {
3725 opix += option -> count;
3726 dfree (option -> data, MDL);
3727 dfree (option, MDL);
3730 va_end (list);
3732 if (bad_option)
3733 return ISC_R_INVALIDARG;
3735 /* Now send the message header. */
3737 /* Message length. */
3738 status = omapi_connection_put_uint16 (connection, size + 12);
3739 if (status != ISC_R_SUCCESS)
3740 goto err;
3742 /* Message type. */
3743 cbuf = msg_type;
3744 status = omapi_connection_copyin (connection, &cbuf, 1);
3745 if (status != ISC_R_SUCCESS)
3746 goto err;
3748 /* Payload offset. */
3749 cbuf = 12;
3750 status = omapi_connection_copyin (connection, &cbuf, 1);
3751 if (status != ISC_R_SUCCESS)
3752 goto err;
3754 /* Current time. */
3755 status = omapi_connection_put_uint32 (connection, (u_int32_t)cur_time);
3756 if (status != ISC_R_SUCCESS)
3757 goto err;
3759 /* Transaction ID. */
3760 status = omapi_connection_put_uint32 (connection, link -> xid++);
3761 if (status != ISC_R_SUCCESS)
3762 goto err;
3765 /* Payload. */
3766 if (opbuf) {
3767 status = omapi_connection_copyin (connection, opbuf, size);
3768 if (status != ISC_R_SUCCESS)
3769 goto err;
3770 dfree (opbuf, MDL);
3772 if (link -> state_object &&
3773 link -> state_object -> link_to_peer == link) {
3774 #if defined (DEBUG_FAILOVER_TIMING)
3775 log_info ("add_timeout +%d %s",
3776 (int)(link -> state_object ->
3777 partner.max_response_delay) / 3,
3778 "dhcp_failover_send_contact");
3779 #endif
3780 add_timeout (cur_time +
3781 (int)(link -> state_object ->
3782 partner.max_response_delay) / 3,
3783 dhcp_failover_send_contact, link -> state_object,
3784 (tvref_t)dhcp_failover_state_reference,
3785 (tvunref_t)dhcp_failover_state_dereference);
3787 return status;
3789 err:
3790 if (opbuf)
3791 dfree (opbuf, MDL);
3792 log_info ("dhcp_failover_put_message: something went wrong.");
3793 omapi_disconnect (connection, 1);
3794 return status;
3797 void dhcp_failover_timeout (void *vstate)
3799 dhcp_failover_state_t *state = vstate;
3800 dhcp_failover_link_t *link;
3802 #if defined (DEBUG_FAILOVER_TIMING)
3803 log_info ("dhcp_failover_timeout");
3804 #endif
3806 if (!state || state -> type != dhcp_type_failover_state)
3807 return;
3808 link = state -> link_to_peer;
3809 if (!link ||
3810 !link -> outer ||
3811 link -> outer -> type != omapi_type_connection)
3812 return;
3814 log_error ("timeout waiting for failover peer %s", state -> name);
3816 /* If we haven't gotten a timely response, blow away the connection.
3817 This will cause the state to change automatically. */
3818 omapi_disconnect (link -> outer, 1);
3821 void dhcp_failover_send_contact (void *vstate)
3823 dhcp_failover_state_t *state = vstate;
3824 dhcp_failover_link_t *link;
3825 isc_result_t status;
3827 #if defined (DEBUG_FAILOVER_MESSAGES)
3828 char obuf [64];
3829 unsigned obufix = 0;
3831 # define FMA obuf, &obufix, sizeof obuf
3832 failover_print (FMA, "(contact");
3833 #else
3834 # define FMA (char *)0, (unsigned *)0, 0
3835 #endif
3837 #if defined (DEBUG_FAILOVER_TIMING)
3838 log_info ("dhcp_failover_send_contact");
3839 #endif
3841 if (!state || state -> type != dhcp_type_failover_state)
3842 return;
3843 link = state -> link_to_peer;
3844 if (!link ||
3845 !link -> outer ||
3846 link -> outer -> type != omapi_type_connection)
3847 return;
3849 status = (dhcp_failover_put_message
3850 (link, link -> outer,
3851 FTM_CONTACT,
3852 (failover_option_t *)0));
3854 #if defined (DEBUG_FAILOVER_MESSAGES)
3855 if (status != ISC_R_SUCCESS)
3856 failover_print (FMA, " (failed)");
3857 failover_print (FMA, ")");
3858 if (obufix) {
3859 log_debug ("%s", obuf);
3861 #endif
3862 return;
3865 isc_result_t dhcp_failover_send_state (dhcp_failover_state_t *state)
3867 dhcp_failover_link_t *link;
3868 isc_result_t status;
3870 #if defined (DEBUG_FAILOVER_MESSAGES)
3871 char obuf [64];
3872 unsigned obufix = 0;
3874 # define FMA obuf, &obufix, sizeof obuf
3875 failover_print (FMA, "(state");
3876 #else
3877 # define FMA (char *)0, (unsigned *)0, 0
3878 #endif
3880 if (!state || state -> type != dhcp_type_failover_state)
3881 return ISC_R_INVALIDARG;
3882 link = state -> link_to_peer;
3883 if (!link ||
3884 !link -> outer ||
3885 link -> outer -> type != omapi_type_connection)
3886 return ISC_R_INVALIDARG;
3888 status = (dhcp_failover_put_message
3889 (link, link -> outer,
3890 FTM_STATE,
3891 dhcp_failover_make_option (FTO_SERVER_STATE, FMA,
3892 (state -> me.state == startup
3893 ? state -> saved_state
3894 : state -> me.state)),
3895 dhcp_failover_make_option
3896 (FTO_SERVER_FLAGS, FMA,
3897 (state -> service_state == service_startup
3898 ? FTF_STARTUP : 0)),
3899 dhcp_failover_make_option (FTO_STOS, FMA, state -> me.stos),
3900 (failover_option_t *)0));
3902 #if defined (DEBUG_FAILOVER_MESSAGES)
3903 if (status != ISC_R_SUCCESS)
3904 failover_print (FMA, " (failed)");
3905 failover_print (FMA, ")");
3906 if (obufix) {
3907 log_debug ("%s", obuf);
3909 #endif
3910 return ISC_R_SUCCESS;
3913 /* Send a connect message. */
3915 isc_result_t dhcp_failover_send_connect (omapi_object_t *l)
3917 dhcp_failover_link_t *link;
3918 dhcp_failover_state_t *state;
3919 isc_result_t status;
3920 #if defined (DEBUG_FAILOVER_MESSAGES)
3921 char obuf [64];
3922 unsigned obufix = 0;
3924 # define FMA obuf, &obufix, sizeof obuf
3925 failover_print (FMA, "(connect");
3926 #else
3927 # define FMA (char *)0, (unsigned *)0, 0
3928 #endif
3930 if (!l || l -> type != dhcp_type_failover_link)
3931 return ISC_R_INVALIDARG;
3932 link = (dhcp_failover_link_t *)l;
3933 state = link -> state_object;
3934 if (!l -> outer || l -> outer -> type != omapi_type_connection)
3935 return ISC_R_INVALIDARG;
3937 status =
3938 (dhcp_failover_put_message
3939 (link, l -> outer,
3940 FTM_CONNECT,
3941 dhcp_failover_make_option (FTO_SERVER_ADDR, FMA,
3942 state -> server_identifier.len,
3943 state -> server_identifier.data),
3944 dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
3945 state -> me.max_flying_updates),
3946 dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
3947 state -> me.max_response_delay),
3948 dhcp_failover_option_printf (FTO_VENDOR_CLASS, FMA,
3949 "isc-%s", DHCP_VERSION),
3950 dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
3951 DHCP_FAILOVER_VERSION),
3952 dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
3953 0, 0),
3954 dhcp_failover_make_option (FTO_MCLT, FMA,
3955 state -> mclt),
3956 (state -> hba
3957 ? dhcp_failover_make_option (FTO_HBA, FMA, 32, state -> hba)
3958 : &skip_failover_option),
3959 (failover_option_t *)0));
3961 #if defined (DEBUG_FAILOVER_MESSAGES)
3962 if (status != ISC_R_SUCCESS)
3963 failover_print (FMA, " (failed)");
3964 failover_print (FMA, ")");
3965 if (obufix) {
3966 log_debug ("%s", obuf);
3968 #endif
3969 return status;
3972 isc_result_t dhcp_failover_send_connectack (omapi_object_t *l,
3973 dhcp_failover_state_t *state,
3974 int reason, const char *errmsg)
3976 dhcp_failover_link_t *link;
3977 isc_result_t status;
3978 #if defined (DEBUG_FAILOVER_MESSAGES)
3979 char obuf [64];
3980 unsigned obufix = 0;
3982 # define FMA obuf, &obufix, sizeof obuf
3983 failover_print (FMA, "(connectack");
3984 #else
3985 # define FMA (char *)0, (unsigned *)0, 0
3986 #endif
3988 if (!l || l -> type != dhcp_type_failover_link)
3989 return ISC_R_INVALIDARG;
3990 link = (dhcp_failover_link_t *)l;
3991 if (!l -> outer || l -> outer -> type != omapi_type_connection)
3992 return ISC_R_INVALIDARG;
3994 status =
3995 (dhcp_failover_put_message
3996 (link, l -> outer,
3997 FTM_CONNECTACK,
3998 (state
3999 ? (dhcp_failover_make_option
4000 (FTO_SERVER_ADDR, FMA,
4001 state -> server_identifier.len,
4002 state -> server_identifier.data))
4003 : &skip_failover_option),
4004 (state
4005 ? dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
4006 state -> me.max_flying_updates)
4007 : &skip_failover_option),
4008 (state
4009 ? dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
4010 state -> me.max_response_delay)
4011 : &skip_failover_option),
4012 dhcp_failover_option_printf (FTO_VENDOR_CLASS, FMA,
4013 "isc-%s", DHCP_VERSION),
4014 dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
4015 DHCP_FAILOVER_VERSION),
4016 dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
4017 0, 0),
4018 (reason
4019 ? dhcp_failover_make_option (FTO_REJECT_REASON,
4020 FMA, reason)
4021 : &skip_failover_option),
4022 (errmsg
4023 ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4024 strlen (errmsg), errmsg)
4025 : &skip_failover_option),
4026 (failover_option_t *)0));
4028 #if defined (DEBUG_FAILOVER_MESSAGES)
4029 if (status != ISC_R_SUCCESS)
4030 failover_print (FMA, " (failed)");
4031 failover_print (FMA, ")");
4032 if (obufix) {
4033 log_debug ("%s", obuf);
4035 #endif
4036 return status;
4039 isc_result_t dhcp_failover_send_disconnect (omapi_object_t *l,
4040 int reason,
4041 const char *message)
4043 dhcp_failover_link_t *link;
4044 dhcp_failover_state_t *state;
4045 isc_result_t status;
4046 #if defined (DEBUG_FAILOVER_MESSAGES)
4047 char obuf [64];
4048 unsigned obufix = 0;
4050 # define FMA obuf, &obufix, sizeof obuf
4051 failover_print (FMA, "(disconnect");
4052 #else
4053 # define FMA (char *)0, (unsigned *)0, 0
4054 #endif
4056 if (!l || l -> type != dhcp_type_failover_link)
4057 return ISC_R_INVALIDARG;
4058 link = (dhcp_failover_link_t *)l;
4059 state = link -> state_object;
4060 if (!l -> outer || l -> outer -> type != omapi_type_connection)
4061 return ISC_R_INVALIDARG;
4063 if (!message && reason)
4064 message = dhcp_failover_reject_reason_print (reason);
4066 status = (dhcp_failover_put_message
4067 (link, l -> outer,
4068 FTM_DISCONNECT,
4069 dhcp_failover_make_option (FTO_REJECT_REASON,
4070 FMA, reason),
4071 (message
4072 ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4073 strlen (message), message)
4074 : &skip_failover_option),
4075 (failover_option_t *)0));
4077 #if defined (DEBUG_FAILOVER_MESSAGES)
4078 if (status != ISC_R_SUCCESS)
4079 failover_print (FMA, " (failed)");
4080 failover_print (FMA, ")");
4081 if (obufix) {
4082 log_debug ("%s", obuf);
4084 #endif
4085 return status;
4088 /* Send a Bind Update message. */
4090 isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state,
4091 struct lease *lease)
4093 dhcp_failover_link_t *link;
4094 isc_result_t status;
4095 #if defined (DEBUG_FAILOVER_MESSAGES)
4096 char obuf [64];
4097 unsigned obufix = 0;
4099 # define FMA obuf, &obufix, sizeof obuf
4100 failover_print (FMA, "(bndupd");
4101 #else
4102 # define FMA (char *)0, (unsigned *)0, 0
4103 #endif
4105 if (!state -> link_to_peer ||
4106 state -> link_to_peer -> type != dhcp_type_failover_link)
4107 return ISC_R_INVALIDARG;
4108 link = (dhcp_failover_link_t *)state -> link_to_peer;
4110 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4111 return ISC_R_INVALIDARG;
4113 /* Send the update. */
4114 status = (dhcp_failover_put_message
4115 (link, link -> outer,
4116 FTM_BNDUPD,
4117 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
4118 lease -> ip_addr.len,
4119 lease -> ip_addr.iabuf),
4120 dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
4121 lease -> desired_binding_state),
4122 lease -> uid_len
4123 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
4124 lease -> uid_len,
4125 lease -> uid)
4126 : &skip_failover_option,
4127 lease -> hardware_addr.hlen
4128 ? dhcp_failover_make_option (FTO_CHADDR, FMA,
4129 lease -> hardware_addr.hlen,
4130 lease -> hardware_addr.hbuf)
4131 : &skip_failover_option,
4132 dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
4133 lease -> ends),
4134 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
4135 lease -> tstp),
4136 dhcp_failover_make_option (FTO_STOS, FMA,
4137 lease -> starts),
4138 dhcp_failover_make_option (FTO_CLTT, FMA,
4139 lease -> cltt),
4140 &skip_failover_option, /* XXX DDNS */
4141 &skip_failover_option, /* XXX request options */
4142 &skip_failover_option, /* XXX reply options */
4143 (failover_option_t *)0));
4145 #if defined (DEBUG_FAILOVER_MESSAGES)
4146 if (status != ISC_R_SUCCESS)
4147 failover_print (FMA, " (failed)");
4148 failover_print (FMA, ")");
4149 if (obufix) {
4150 log_debug ("%s", obuf);
4152 #endif
4153 return status;
4156 /* Send a Bind ACK message. */
4158 isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *state,
4159 failover_message_t *msg,
4160 int reason, const char *message)
4162 dhcp_failover_link_t *link;
4163 isc_result_t status;
4164 #if defined (DEBUG_FAILOVER_MESSAGES)
4165 char obuf [64];
4166 unsigned obufix = 0;
4168 # define FMA obuf, &obufix, sizeof obuf
4169 failover_print (FMA, "(bndack");
4170 #else
4171 # define FMA (char *)0, (unsigned *)0, 0
4172 #endif
4174 if (!state -> link_to_peer ||
4175 state -> link_to_peer -> type != dhcp_type_failover_link)
4176 return ISC_R_INVALIDARG;
4177 link = (dhcp_failover_link_t *)state -> link_to_peer;
4179 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4180 return ISC_R_INVALIDARG;
4182 if (!message && reason)
4183 message = dhcp_failover_reject_reason_print (reason);
4185 /* Send the update. */
4186 status = (dhcp_failover_put_message
4187 (link, link -> outer,
4188 FTM_BNDACK,
4189 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
4190 sizeof msg -> assigned_addr,
4191 &msg -> assigned_addr),
4192 dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
4193 msg -> binding_status),
4194 (msg -> options_present & FTB_CLIENT_IDENTIFIER)
4195 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
4196 msg -> client_identifier.count,
4197 msg -> client_identifier.data)
4198 : &skip_failover_option,
4199 (msg -> options_present & FTB_CHADDR)
4200 ? dhcp_failover_make_option (FTO_CHADDR, FMA,
4201 msg -> chaddr.count,
4202 msg -> chaddr.data)
4203 : &skip_failover_option,
4204 dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
4205 msg -> expiry),
4206 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
4207 msg -> potential_expiry),
4208 dhcp_failover_make_option (FTO_STOS, FMA,
4209 msg -> stos),
4210 dhcp_failover_make_option (FTO_CLTT, FMA,
4211 msg -> client_ltt),
4212 reason
4213 ? dhcp_failover_make_option (FTO_REJECT_REASON,
4214 FMA, reason)
4215 : &skip_failover_option,
4216 (message
4217 ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4218 strlen (message), message)
4219 : &skip_failover_option),
4220 &skip_failover_option, /* XXX DDNS */
4221 &skip_failover_option, /* XXX request options */
4222 &skip_failover_option, /* XXX reply options */
4223 (failover_option_t *)0));
4225 #if defined (DEBUG_FAILOVER_MESSAGES)
4226 if (status != ISC_R_SUCCESS)
4227 failover_print (FMA, " (failed)");
4228 failover_print (FMA, ")");
4229 if (obufix) {
4230 log_debug ("%s", obuf);
4232 #endif
4233 return status;
4236 isc_result_t dhcp_failover_send_poolreq (dhcp_failover_state_t *state)
4238 dhcp_failover_link_t *link;
4239 isc_result_t status;
4240 #if defined (DEBUG_FAILOVER_MESSAGES)
4241 char obuf [64];
4242 unsigned obufix = 0;
4244 # define FMA obuf, &obufix, sizeof obuf
4245 failover_print (FMA, "(poolreq");
4246 #else
4247 # define FMA (char *)0, (unsigned *)0, 0
4248 #endif
4250 if (!state -> link_to_peer ||
4251 state -> link_to_peer -> type != dhcp_type_failover_link)
4252 return ISC_R_INVALIDARG;
4253 link = (dhcp_failover_link_t *)state -> link_to_peer;
4255 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4256 return ISC_R_INVALIDARG;
4258 status = (dhcp_failover_put_message
4259 (link, link -> outer,
4260 FTM_POOLREQ,
4261 (failover_option_t *)0));
4263 #if defined (DEBUG_FAILOVER_MESSAGES)
4264 if (status != ISC_R_SUCCESS)
4265 failover_print (FMA, " (failed)");
4266 failover_print (FMA, ")");
4267 if (obufix) {
4268 log_debug ("%s", obuf);
4270 #endif
4271 return status;
4274 isc_result_t dhcp_failover_send_poolresp (dhcp_failover_state_t *state,
4275 int leases)
4277 dhcp_failover_link_t *link;
4278 isc_result_t status;
4279 #if defined (DEBUG_FAILOVER_MESSAGES)
4280 char obuf [64];
4281 unsigned obufix = 0;
4283 # define FMA obuf, &obufix, sizeof obuf
4284 failover_print (FMA, "(poolresp");
4285 #else
4286 # define FMA (char *)0, (unsigned *)0, 0
4287 #endif
4289 if (!state -> link_to_peer ||
4290 state -> link_to_peer -> type != dhcp_type_failover_link)
4291 return ISC_R_INVALIDARG;
4292 link = (dhcp_failover_link_t *)state -> link_to_peer;
4294 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4295 return ISC_R_INVALIDARG;
4297 status = (dhcp_failover_put_message
4298 (link, link -> outer,
4299 FTM_POOLRESP,
4300 dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED, FMA,
4301 leases),
4302 (failover_option_t *)0));
4304 #if defined (DEBUG_FAILOVER_MESSAGES)
4305 if (status != ISC_R_SUCCESS)
4306 failover_print (FMA, " (failed)");
4307 failover_print (FMA, ")");
4308 if (obufix) {
4309 log_debug ("%s", obuf);
4311 #endif
4312 return status;
4315 isc_result_t dhcp_failover_send_update_request (dhcp_failover_state_t *state)
4317 dhcp_failover_link_t *link;
4318 isc_result_t status;
4319 #if defined (DEBUG_FAILOVER_MESSAGES)
4320 char obuf [64];
4321 unsigned obufix = 0;
4323 # define FMA obuf, &obufix, sizeof obuf
4324 failover_print (FMA, "(updreq");
4325 #else
4326 # define FMA (char *)0, (unsigned *)0, 0
4327 #endif
4329 if (!state -> link_to_peer ||
4330 state -> link_to_peer -> type != dhcp_type_failover_link)
4331 return ISC_R_INVALIDARG;
4332 link = (dhcp_failover_link_t *)state -> link_to_peer;
4334 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4335 return ISC_R_INVALIDARG;
4337 if (state -> curUPD)
4338 return ISC_R_ALREADYRUNNING;
4340 status = (dhcp_failover_put_message
4341 (link, link -> outer,
4342 FTM_UPDREQ,
4343 (failover_option_t *)0));
4345 if (status == ISC_R_SUCCESS)
4346 state -> curUPD = FTM_UPDREQ;
4348 #if defined (DEBUG_FAILOVER_MESSAGES)
4349 if (status != ISC_R_SUCCESS)
4350 failover_print (FMA, " (failed)");
4351 failover_print (FMA, ")");
4352 if (obufix) {
4353 log_debug ("%s", obuf);
4355 #endif
4356 log_info ("Sent update request message to %s", state -> name);
4357 return status;
4360 isc_result_t dhcp_failover_send_update_request_all (dhcp_failover_state_t
4361 *state)
4363 dhcp_failover_link_t *link;
4364 isc_result_t status;
4365 #if defined (DEBUG_FAILOVER_MESSAGES)
4366 char obuf [64];
4367 unsigned obufix = 0;
4369 # define FMA obuf, &obufix, sizeof obuf
4370 failover_print (FMA, "(updreqall");
4371 #else
4372 # define FMA (char *)0, (unsigned *)0, 0
4373 #endif
4375 if (!state -> link_to_peer ||
4376 state -> link_to_peer -> type != dhcp_type_failover_link)
4377 return ISC_R_INVALIDARG;
4378 link = (dhcp_failover_link_t *)state -> link_to_peer;
4380 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4381 return ISC_R_INVALIDARG;
4383 /* If there is an UPDREQ in progress, then upgrade to UPDREQALL. */
4384 if (state -> curUPD && (state -> curUPD != FTM_UPDREQ))
4385 return ISC_R_ALREADYRUNNING;
4387 status = (dhcp_failover_put_message
4388 (link, link -> outer,
4389 FTM_UPDREQALL,
4390 (failover_option_t *)0));
4392 if (status == ISC_R_SUCCESS)
4393 state -> curUPD = FTM_UPDREQALL;
4395 #if defined (DEBUG_FAILOVER_MESSAGES)
4396 if (status != ISC_R_SUCCESS)
4397 failover_print (FMA, " (failed)");
4398 failover_print (FMA, ")");
4399 if (obufix) {
4400 log_debug ("%s", obuf);
4402 #endif
4403 log_info ("Sent update request all message to %s", state -> name);
4404 return status;
4407 isc_result_t dhcp_failover_send_update_done (dhcp_failover_state_t *state)
4409 dhcp_failover_link_t *link;
4410 isc_result_t status;
4411 #if defined (DEBUG_FAILOVER_MESSAGES)
4412 char obuf [64];
4413 unsigned obufix = 0;
4415 # define FMA obuf, &obufix, sizeof obuf
4416 failover_print (FMA, "(upddone");
4417 #else
4418 # define FMA (char *)0, (unsigned *)0, 0
4419 #endif
4421 if (!state -> link_to_peer ||
4422 state -> link_to_peer -> type != dhcp_type_failover_link)
4423 return ISC_R_INVALIDARG;
4424 link = (dhcp_failover_link_t *)state -> link_to_peer;
4426 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4427 return ISC_R_INVALIDARG;
4429 status = (dhcp_failover_put_message
4430 (link, link -> outer,
4431 FTM_UPDDONE,
4432 (failover_option_t *)0));
4434 #if defined (DEBUG_FAILOVER_MESSAGES)
4435 if (status != ISC_R_SUCCESS)
4436 failover_print (FMA, " (failed)");
4437 failover_print (FMA, ")");
4438 if (obufix) {
4439 log_debug ("%s", obuf);
4441 #endif
4443 log_info ("Sent update done message to %s", state -> name);
4445 /* There may be uncommitted leases at this point (since
4446 dhcp_failover_process_bind_ack() doesn't commit leases);
4447 commit the lease file. */
4448 commit_leases();
4450 return status;
4453 isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *state,
4454 failover_message_t *msg)
4456 struct lease *lt, *lease;
4457 struct iaddr ia;
4458 int reason = FTR_MISC_REJECT;
4459 const char *message;
4460 int new_binding_state;
4462 ia.len = sizeof msg -> assigned_addr;
4463 memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
4465 lease = (struct lease *)0;
4466 lt = (struct lease *)0;
4467 if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
4468 message = "unknown IP address";
4469 reason = FTR_ILLEGAL_IP_ADDR;
4470 goto bad;
4473 /* XXX check for conflicts. */
4475 /* Install the new info. */
4476 if (!lease_copy (&lt, lease, MDL)) {
4477 message = "no memory";
4478 goto bad;
4481 if (msg -> options_present & FTB_CHADDR) {
4482 if (msg->binding_status == FTS_ABANDONED) {
4483 message = "BNDUPD to ABANDONED with a CHADDR";
4484 goto bad;
4486 if (msg -> chaddr.count > sizeof lt -> hardware_addr.hbuf) {
4487 message = "chaddr to long";
4488 goto bad;
4490 lt -> hardware_addr.hlen = msg -> chaddr.count;
4491 memcpy (lt -> hardware_addr.hbuf, msg -> chaddr.data,
4492 msg -> chaddr.count);
4493 } else if (msg->binding_status == FTS_ACTIVE ||
4494 msg->binding_status == FTS_EXPIRED ||
4495 msg->binding_status == FTS_RELEASED) {
4496 message = "BNDUPD without CHADDR";
4497 goto bad;
4498 } else if (msg->binding_status == FTS_ABANDONED) {
4499 lt->hardware_addr.hlen = 0;
4500 if (lt->scope)
4501 binding_scope_dereference(&lt->scope, MDL);
4504 /* There is no explicit message content to indicate that the client
4505 * supplied no client-identifier. So if we don't hear of a value,
4506 * we discard the last one.
4508 if (msg->options_present & FTB_CLIENT_IDENTIFIER) {
4509 if (msg->binding_status == FTS_ABANDONED) {
4510 message = "BNDUPD to ABANDONED with client-id";
4511 goto bad;
4514 lt->uid_len = msg->client_identifier.count;
4516 /* Allocate the lt->uid buffer if we haven't already, or
4517 * re-allocate the lt-uid buffer if we have one that is not
4518 * large enough. Otherwise, just use the extant buffer.
4520 if (!lt->uid || lt->uid == lt->uid_buf ||
4521 lt->uid_len > lt->uid_max) {
4522 if (lt->uid && lt->uid != lt->uid_buf)
4523 dfree(lt->uid, MDL);
4525 if (lt->uid_len > sizeof(lt->uid_buf)) {
4526 lt->uid_max = lt->uid_len;
4527 lt->uid = dmalloc(lt->uid_len, MDL);
4528 if (!lt->uid) {
4529 message = "no memory";
4530 goto bad;
4532 } else {
4533 lt->uid_max = sizeof(lt->uid_buf);
4534 lt->uid = lt->uid_buf;
4537 memcpy (lt -> uid,
4538 msg -> client_identifier.data, lt -> uid_len);
4539 } else if (lt->uid && msg->binding_status != FTS_RESET &&
4540 msg->binding_status != FTS_FREE &&
4541 msg->binding_status != FTS_BACKUP) {
4542 if (lt->uid != lt->uid_buf)
4543 dfree (lt->uid, MDL);
4544 lt->uid = NULL;
4545 lt->uid_max = lt->uid_len = 0;
4548 /* If the lease was expired, also remove the stale binding scope. */
4549 if (lt->scope && lt->ends < cur_time)
4550 binding_scope_dereference(&lt->scope, MDL);
4552 /* XXX Times may need to be adjusted based on clock skew! */
4553 if (msg -> options_present & FTB_STOS) {
4554 lt -> starts = msg -> stos;
4556 if (msg -> options_present & FTB_LEASE_EXPIRY) {
4557 lt -> ends = msg -> expiry;
4559 if (msg -> options_present & FTB_CLTT) {
4560 lt -> cltt = msg -> client_ltt;
4562 if (msg -> options_present & FTB_POTENTIAL_EXPIRY) {
4563 lt -> tsfp = msg -> potential_expiry;
4566 if (msg -> options_present & FTB_BINDING_STATUS) {
4567 #if defined (DEBUG_LEASE_STATE_TRANSITIONS)
4568 log_info ("processing state transition for %s: %s to %s",
4569 piaddr (lease -> ip_addr),
4570 binding_state_print (lease -> binding_state),
4571 binding_state_print (msg -> binding_status));
4572 #endif
4574 /* If we're in normal state, make sure the state transition
4575 we got is valid. */
4576 if (state -> me.state == normal) {
4577 new_binding_state =
4578 (normal_binding_state_transition_check
4579 (lease, state, msg -> binding_status,
4580 msg -> potential_expiry));
4581 /* XXX if the transition the peer asked for isn't
4582 XXX allowed, maybe we should make the transition
4583 XXX into potential-conflict at this point. */
4584 } else {
4585 new_binding_state =
4586 (conflict_binding_state_transition_check
4587 (lease, state, msg -> binding_status,
4588 msg -> potential_expiry));
4590 if (new_binding_state != msg -> binding_status) {
4591 char outbuf [100];
4593 if (snprintf (outbuf, sizeof outbuf,
4594 "%s: invalid state transition: %s to %s",
4595 piaddr (lease -> ip_addr),
4596 binding_state_print (lease -> binding_state),
4597 binding_state_print (msg -> binding_status))
4598 >= sizeof outbuf)
4599 log_fatal ("%s: impossible outbuf overflow",
4600 "dhcp_failover_process_bind_update");
4602 dhcp_failover_send_bind_ack (state, msg,
4603 FTR_FATAL_CONFLICT,
4604 outbuf);
4605 goto out;
4607 if (new_binding_state == FTS_EXPIRED ||
4608 new_binding_state == FTS_RELEASED ||
4609 new_binding_state == FTS_RESET)
4610 lt -> next_binding_state = FTS_FREE;
4611 else
4612 lt -> next_binding_state = new_binding_state;
4613 msg -> binding_status = lt -> next_binding_state;
4616 /* Try to install the new information. */
4617 if (!supersede_lease (lease, lt, 0, 0, 0) ||
4618 !write_lease (lease)) {
4619 message = "database update failed";
4620 bad:
4621 dhcp_failover_send_bind_ack (state, msg, reason, message);
4622 } else {
4624 dhcp_failover_queue_ack (state, msg);
4627 out:
4628 if (lt)
4629 lease_dereference (&lt, MDL);
4630 if (lease)
4631 lease_dereference (&lease, MDL);
4633 return ISC_R_SUCCESS;
4636 isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *state,
4637 failover_message_t *msg)
4639 struct lease *lt = (struct lease *)0;
4640 struct lease *lease = (struct lease *)0;
4641 struct iaddr ia;
4642 const char *message = "no memory";
4644 ia.len = sizeof msg -> assigned_addr;
4645 memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
4647 if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
4648 message = "no such lease";
4649 goto bad;
4652 /* XXX check for conflicts. */
4653 if (msg -> options_present & FTB_REJECT_REASON) {
4654 log_error ("bind update on %s from %s rejected: %.*s",
4655 piaddr (ia), state -> name,
4656 (int)((msg -> options_present & FTB_MESSAGE)
4657 ? msg -> message.count
4658 : strlen (dhcp_failover_reject_reason_print
4659 (msg -> reject_reason))),
4660 (msg -> options_present & FTB_MESSAGE)
4661 ? (const char *)(msg -> message.data)
4662 : (dhcp_failover_reject_reason_print
4663 (msg -> reject_reason)));
4664 goto unqueue;
4667 /* XXX Times may need to be adjusted based on clock skew! */
4668 if (msg -> options_present & FTB_POTENTIAL_EXPIRY) {
4669 /* XXX it could be a problem to do this directly if the
4670 XXX lease is sorted by tsfp. */
4671 if ((lease -> binding_state == FTS_EXPIRED ||
4672 lease -> binding_state == FTS_RESET ||
4673 lease -> binding_state == FTS_RELEASED) &&
4674 (msg -> options_present & FTB_BINDING_STATUS) &&
4675 msg -> binding_status == FTS_FREE)
4677 lease -> tsfp = msg -> potential_expiry;
4678 lease -> next_binding_state = FTS_FREE;
4679 supersede_lease (lease, (struct lease *)0, 0, 0, 0);
4680 write_lease (lease);
4681 if (state -> me.state == normal)
4682 commit_leases ();
4683 } else {
4684 lease -> tsfp = msg -> potential_expiry;
4685 if ((lease -> desired_binding_state !=
4686 lease -> binding_state) &&
4687 (msg -> options_present & FTB_BINDING_STATUS) &&
4688 (msg -> binding_status ==
4689 lease -> desired_binding_state)) {
4690 lease -> next_binding_state =
4691 lease -> desired_binding_state;
4692 supersede_lease (lease,
4693 (struct lease *)0, 0, 0, 0);
4695 write_lease (lease);
4696 /* Commit the lease only after a two-second timeout,
4697 so that if we get a bunch of acks in quick
4698 successtion (e.g., when stealing leases from the
4699 secondary), we do not do an immediate commit for
4700 each one. */
4701 add_timeout (cur_time + 2,
4702 commit_leases_timeout, (void *)0, 0, 0);
4704 } else if (lease -> desired_binding_state != lease -> binding_state &&
4705 (msg -> options_present & FTB_BINDING_STATUS) &&
4706 msg -> binding_status == lease -> desired_binding_state) {
4707 lease -> next_binding_state = lease -> desired_binding_state;
4708 supersede_lease (lease, (struct lease *)0, 0, 0, 0);
4709 write_lease (lease);
4710 add_timeout (cur_time + 2, commit_leases_timeout,
4711 (void *)0, 0, 0);
4714 unqueue:
4715 dhcp_failover_ack_queue_remove (state, lease);
4717 /* If we are supposed to send an update done after we send
4718 this lease, go ahead and send it. */
4719 if (state -> send_update_done == lease) {
4720 lease_dereference (&state -> send_update_done, MDL);
4721 dhcp_failover_send_update_done (state);
4724 /* If there are updates pending, we've created space to send at
4725 least one. */
4726 dhcp_failover_send_updates (state);
4728 out:
4729 lease_dereference (&lease, MDL);
4730 if (lt)
4731 lease_dereference (&lt, MDL);
4733 return ISC_R_SUCCESS;
4735 bad:
4736 log_info ("bind update on %s from %s: %s.",
4737 piaddr (ia), state -> name, message);
4738 goto out;
4741 isc_result_t dhcp_failover_generate_update_queue (dhcp_failover_state_t *state,
4742 int everythingp)
4744 struct shared_network *s;
4745 struct pool *p;
4746 struct lease *l, *n;
4747 int i;
4748 struct lease **lptr [5];
4749 #define FREE_LEASES 0
4750 #define ACTIVE_LEASES 1
4751 #define EXPIRED_LEASES 2
4752 #define ABANDONED_LEASES 3
4753 #define BACKUP_LEASES 4
4755 /* First remove everything from the update and ack queues. */
4756 l = n = (struct lease *)0;
4757 if (state -> update_queue_head) {
4758 lease_reference (&l, state -> update_queue_head, MDL);
4759 lease_dereference (&state -> update_queue_head, MDL);
4760 do {
4761 l -> flags &= ~ON_UPDATE_QUEUE;
4762 if (l -> next_pending) {
4763 lease_reference (&n,
4764 l -> next_pending, MDL);
4765 lease_dereference (&l -> next_pending, MDL);
4767 lease_dereference (&l, MDL);
4768 if (n) {
4769 lease_reference (&l, n, MDL);
4770 lease_dereference (&n, MDL);
4772 } while (l);
4773 lease_dereference (&state -> update_queue_tail, MDL);
4776 if (state -> ack_queue_head) {
4777 lease_reference (&l, state -> ack_queue_head, MDL);
4778 lease_dereference (&state -> ack_queue_head, MDL);
4779 do {
4780 l -> flags &= ~ON_ACK_QUEUE;
4781 if (l -> next_pending) {
4782 lease_reference (&n,
4783 l -> next_pending, MDL);
4784 lease_dereference (&l -> next_pending, MDL);
4786 lease_dereference (&l, MDL);
4787 if (n) {
4788 lease_reference (&l, n, MDL);
4789 lease_dereference (&n, MDL);
4791 } while (l);
4792 lease_dereference (&state -> ack_queue_tail, MDL);
4794 if (state -> send_update_done)
4795 lease_dereference (&state -> send_update_done, MDL);
4796 state -> cur_unacked_updates = 0;
4798 /* Loop through each pool in each shared network and call the
4799 expiry routine on the pool. */
4800 for (s = shared_networks; s; s = s -> next) {
4801 for (p = s -> pools; p; p = p -> next) {
4802 lptr [FREE_LEASES] = &p -> free;
4803 lptr [ACTIVE_LEASES] = &p -> active;
4804 lptr [EXPIRED_LEASES] = &p -> expired;
4805 lptr [ABANDONED_LEASES] = &p -> abandoned;
4806 lptr [BACKUP_LEASES] = &p -> backup;
4808 for (i = FREE_LEASES; i <= BACKUP_LEASES; i++) {
4809 for (l = *(lptr [i]); l; l = l -> next) {
4810 if (p -> failover_peer == state &&
4811 ((everythingp &&
4812 (l -> starts != MIN_TIME ||
4813 l -> ends != MIN_TIME)) ||
4814 l -> tstp > l -> tsfp)) {
4815 l -> desired_binding_state = l -> binding_state;
4816 dhcp_failover_queue_update (l, 0);
4822 return ISC_R_SUCCESS;
4825 isc_result_t
4826 dhcp_failover_process_update_request (dhcp_failover_state_t *state,
4827 failover_message_t *msg)
4829 /* Generate a fresh update queue. */
4830 dhcp_failover_generate_update_queue (state, 0);
4832 /* If there's anything on the update queue (there shouldn't be
4833 anything on the ack queue), trigger an update done message
4834 when we get an ack for that lease. */
4835 if (state -> update_queue_tail) {
4836 lease_reference (&state -> send_update_done,
4837 state -> update_queue_tail, MDL);
4838 dhcp_failover_send_updates (state);
4839 log_info ("Update request from %s: sending update",
4840 state -> name);
4841 } else {
4842 /* Otherwise, there are no updates to send, so we can
4843 just send an UPDDONE message immediately. */
4844 dhcp_failover_send_update_done (state);
4845 log_info ("Update request from %s: nothing pending",
4846 state -> name);
4849 return ISC_R_SUCCESS;
4852 isc_result_t
4853 dhcp_failover_process_update_request_all (dhcp_failover_state_t *state,
4854 failover_message_t *msg)
4856 /* Generate a fresh update queue that includes every lease. */
4857 dhcp_failover_generate_update_queue (state, 1);
4859 if (state -> update_queue_tail) {
4860 lease_reference (&state -> send_update_done,
4861 state -> update_queue_tail, MDL);
4862 dhcp_failover_send_updates (state);
4863 log_info ("Update request all from %s: sending update",
4864 state -> name);
4865 } else {
4866 /* This should really never happen, but it could happen
4867 on a server that currently has no leases configured. */
4868 dhcp_failover_send_update_done (state);
4869 log_info ("Update request all from %s: nothing pending",
4870 state -> name);
4873 return ISC_R_SUCCESS;
4876 isc_result_t
4877 dhcp_failover_process_update_done (dhcp_failover_state_t *state,
4878 failover_message_t *msg)
4880 log_info ("failover peer %s: peer update completed.",
4881 state -> name);
4883 state -> curUPD = 0;
4885 switch (state -> me.state) {
4886 case unknown_state:
4887 case partner_down:
4888 case normal:
4889 case communications_interrupted:
4890 case resolution_interrupted:
4891 case shut_down:
4892 case paused:
4893 case recover_done:
4894 case startup:
4895 case recover_wait:
4896 break; /* shouldn't happen. */
4898 /* We got the UPDDONE, so we can go into normal state! */
4899 case potential_conflict:
4900 dhcp_failover_set_state (state, normal);
4901 break;
4903 case recover:
4904 /* Wait for MCLT to expire before moving to recover_done,
4905 except that if both peers come up in recover, there is
4906 no point in waiting for MCLT to expire - this probably
4907 indicates the initial startup of a newly-configured
4908 failover pair. */
4909 if (state -> me.stos + state -> mclt > cur_time &&
4910 state -> partner.state != recover &&
4911 state -> partner.state != recover_done) {
4912 dhcp_failover_set_state (state, recover_wait);
4913 #if defined (DEBUG_FAILOVER_TIMING)
4914 log_info ("add_timeout +%d %s",
4915 (int)(cur_time -
4916 state -> me.stos + state -> mclt),
4917 "dhcp_failover_recover_done");
4918 #endif
4919 add_timeout ((int)(state -> me.stos + state -> mclt),
4920 dhcp_failover_recover_done,
4921 state,
4922 (tvref_t)omapi_object_reference,
4923 (tvunref_t)
4924 omapi_object_dereference);
4925 } else
4926 dhcp_failover_recover_done (state);
4929 return ISC_R_SUCCESS;
4932 void dhcp_failover_recover_done (void *sp)
4934 dhcp_failover_state_t *state = sp;
4936 #if defined (DEBUG_FAILOVER_TIMING)
4937 log_info ("dhcp_failover_recover_done");
4938 #endif
4940 dhcp_failover_set_state (state, recover_done);
4943 #if defined (DEBUG_FAILOVER_MESSAGES)
4944 /* Print hunks of failover messages, doing line breaks as appropriate.
4945 Note that this assumes syslog is being used, rather than, e.g., the
4946 Windows NT logging facility, where just dumping the whole message in
4947 one hunk would be more appropriate. */
4949 void failover_print (char *obuf,
4950 unsigned *obufix, unsigned obufmax, const char *s)
4952 int len = strlen (s);
4954 while (len + *obufix + 1 >= obufmax) {
4955 log_debug ("%s", obuf);
4956 if (!*obufix) {
4957 log_debug ("%s", s);
4958 *obufix = 0;
4959 return;
4961 *obufix = 0;
4963 strcpy (&obuf [*obufix], s);
4964 *obufix += len;
4966 #endif /* defined (DEBUG_FAILOVER_MESSAGES) */
4968 /* Taken from draft-ietf-dhc-loadb-01.txt: */
4969 /* A "mixing table" of 256 distinct values, in pseudo-random order. */
4970 unsigned char loadb_mx_tbl[256] = {
4971 251, 175, 119, 215, 81, 14, 79, 191, 103, 49,
4972 181, 143, 186, 157, 0, 232, 31, 32, 55, 60,
4973 152, 58, 17, 237, 174, 70, 160, 144, 220, 90,
4974 57, 223, 59, 3, 18, 140, 111, 166, 203, 196,
4975 134, 243, 124, 95, 222, 179, 197, 65, 180, 48,
4976 36, 15, 107, 46, 233, 130, 165, 30, 123, 161,
4977 209, 23, 97, 16, 40, 91, 219, 61, 100, 10,
4978 210, 109, 250, 127, 22, 138, 29, 108, 244, 67,
4979 207, 9, 178, 204, 74, 98, 126, 249, 167, 116,
4980 34, 77, 193, 200, 121, 5, 20, 113, 71, 35,
4981 128, 13, 182, 94, 25, 226, 227, 199, 75, 27,
4982 41, 245, 230, 224, 43, 225, 177, 26, 155, 150,
4983 212, 142, 218, 115, 241, 73, 88, 105, 39, 114,
4984 62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
4985 154, 122, 12, 84, 82, 163, 44, 139, 228, 236,
4986 205, 242, 217, 11, 187, 146, 159, 64, 86, 239,
4987 195, 42, 106, 198, 118, 112, 184, 172, 87, 2,
4988 173, 117, 176, 229, 247, 253, 137, 185, 99, 164,
4989 102, 147, 45, 66, 231, 52, 141, 211, 194, 206,
4990 246, 238, 56, 110, 78, 248, 63, 240, 189, 93,
4991 92, 51, 53, 183, 19, 171, 72, 50, 33, 104,
4992 101, 69, 8, 252, 83, 120, 76, 135, 85, 54,
4993 202, 125, 188, 213, 96, 235, 136, 208, 162, 129,
4994 190, 132, 156, 38, 47, 1, 7, 254, 24, 4,
4995 216, 131, 89, 21, 28, 133, 37, 153, 149, 80,
4996 170, 68, 6, 169, 234, 151 };
4998 static unsigned char loadb_p_hash (const unsigned char *, unsigned);
4999 static unsigned char loadb_p_hash (const unsigned char *key, unsigned len)
5001 unsigned char hash = len;
5002 int i;
5003 for(i = len; i > 0; )
5004 hash = loadb_mx_tbl [hash ^ (key [--i])];
5005 return hash;
5008 int load_balance_mine (struct packet *packet, dhcp_failover_state_t *state)
5010 struct option_cache *oc;
5011 struct data_string ds;
5012 unsigned char hbaix;
5013 int hm;
5015 if (state -> load_balance_max_secs < ntohs (packet -> raw -> secs)) {
5016 return 1;
5019 /* If we don't have a hash bucket array, we can't tell if this
5020 one's ours, so we assume it's not. */
5021 if (!state -> hba)
5022 return 0;
5024 oc = lookup_option (&dhcp_universe, packet -> options,
5025 DHO_DHCP_CLIENT_IDENTIFIER);
5026 memset (&ds, 0, sizeof ds);
5027 if (oc &&
5028 evaluate_option_cache (&ds, packet, (struct lease *)0,
5029 (struct client_state *)0,
5030 packet -> options, (struct option_state *)0,
5031 &global_scope, oc, MDL)) {
5032 hbaix = loadb_p_hash (ds.data, ds.len);
5033 } else {
5034 hbaix = loadb_p_hash (packet -> raw -> chaddr,
5035 packet -> raw -> hlen);
5037 hm = (state -> hba [hbaix / 8] & (1 << (hbaix & 3)));
5038 if (state -> i_am == primary)
5039 return hm;
5040 else
5041 return !hm;
5044 /* This deals with what to do with bind updates when
5045 we're in the normal state
5047 Note that tsfp had better be set from the latest bind update
5048 _before_ this function is called! */
5050 binding_state_t
5051 normal_binding_state_transition_check (struct lease *lease,
5052 dhcp_failover_state_t *state,
5053 binding_state_t binding_state,
5054 u_int32_t tsfp)
5056 binding_state_t new_state;
5058 new_state = 0; /* XXXGCC -Wuninitialized */
5060 /* If there is no transition, it's no problem. */
5061 if (binding_state == lease -> binding_state)
5062 return binding_state;
5064 switch (lease -> binding_state) {
5065 case FTS_FREE:
5066 case FTS_ABANDONED:
5067 switch (binding_state) {
5068 case FTS_ACTIVE:
5069 case FTS_ABANDONED:
5070 case FTS_BACKUP:
5071 case FTS_EXPIRED:
5072 case FTS_RELEASED:
5073 case FTS_RESET:
5074 /* If the lease was free, and our peer is primary,
5075 then it can make it active, or abandoned, or
5076 backup. Abandoned is treated like free in
5077 this case. */
5078 if (state -> i_am == secondary)
5079 return binding_state;
5081 /* Otherwise, it can't legitimately do any sort of
5082 state transition. Because the lease was free,
5083 and the error has already been made, we allow the
5084 peer to change its state anyway, but log a warning
5085 message in hopes that the error will be fixed. */
5086 case FTS_FREE: /* for compiler */
5087 new_state = binding_state;
5088 goto out;
5090 default:
5091 log_fatal ("Impossible case at %s:%d.", MDL);
5092 return FTS_RESET;
5094 case FTS_ACTIVE:
5095 /* The secondary can't change the state of an active
5096 lease. */
5097 if (state -> i_am == primary) {
5098 /* Except that the client may send the DHCPRELEASE
5099 to the secondary, and we have to accept that. */
5100 if (binding_state == FTS_RELEASED)
5101 return binding_state;
5102 new_state = lease -> binding_state;
5103 goto out;
5106 /* So this is only for transitions made by the primary: */
5107 switch (binding_state) {
5108 case FTS_FREE:
5109 case FTS_BACKUP:
5110 /* Can't set a lease to free or backup until the
5111 peer agrees that it's expired. */
5112 if (tsfp > cur_time) {
5113 new_state = lease -> binding_state;
5114 goto out;
5116 return binding_state;
5118 case FTS_EXPIRED:
5119 /* XXX 65 should be the clock skew between the peers
5120 XXX plus a fudge factor. This code will result
5121 XXX in problems if MCLT is really short or the
5122 XXX max-lease-time is really short (less than the
5123 XXX fudge factor. */
5124 if (lease -> ends - 65 > cur_time) {
5125 new_state = lease -> binding_state;
5126 goto out;
5129 case FTS_RELEASED:
5130 case FTS_ABANDONED:
5131 case FTS_RESET:
5132 case FTS_ACTIVE:
5133 return binding_state;
5135 default:
5136 log_fatal ("Impossible case at %s:%d.", MDL);
5137 return FTS_RESET;
5139 break;
5140 case FTS_EXPIRED:
5141 switch (binding_state) {
5142 case FTS_BACKUP:
5143 case FTS_FREE:
5144 /* Can't set a lease to free or backup until the
5145 peer agrees that it's expired. */
5146 if (tsfp > cur_time) {
5147 new_state = lease -> binding_state;
5148 goto out;
5150 return binding_state;
5152 case FTS_ACTIVE:
5153 case FTS_RELEASED:
5154 case FTS_ABANDONED:
5155 case FTS_RESET:
5156 case FTS_EXPIRED:
5157 return binding_state;
5159 default:
5160 log_fatal ("Impossible case at %s:%d.", MDL);
5161 return FTS_RESET;
5163 case FTS_RELEASED:
5164 switch (binding_state) {
5165 case FTS_FREE:
5166 case FTS_BACKUP:
5168 /* These are invalid state transitions - should we
5169 prevent them? */
5170 case FTS_EXPIRED:
5171 case FTS_ABANDONED:
5172 case FTS_RESET:
5173 case FTS_ACTIVE:
5174 case FTS_RELEASED:
5175 return binding_state;
5177 default:
5178 log_fatal ("Impossible case at %s:%d.", MDL);
5179 return FTS_RESET;
5181 case FTS_RESET:
5182 switch (binding_state) {
5183 case FTS_FREE:
5184 case FTS_BACKUP:
5185 /* Can't set a lease to free or backup until the
5186 peer agrees that it's expired. */
5187 if (tsfp > cur_time) {
5188 new_state = lease -> binding_state;
5189 goto out;
5191 return binding_state;
5193 case FTS_ACTIVE:
5194 case FTS_EXPIRED:
5195 case FTS_RELEASED:
5196 case FTS_ABANDONED:
5197 case FTS_RESET:
5198 return binding_state;
5200 default:
5201 log_fatal ("Impossible case at %s:%d.", MDL);
5202 return FTS_RESET;
5204 case FTS_BACKUP:
5205 switch (binding_state) {
5206 case FTS_ACTIVE:
5207 case FTS_ABANDONED:
5208 case FTS_EXPIRED:
5209 case FTS_RELEASED:
5210 case FTS_RESET:
5211 /* If the lease was in backup, and our peer
5212 is secondary, then it can make it active
5213 or abandoned. */
5214 if (state -> i_am == primary)
5215 return binding_state;
5217 /* Either the primary or the secondary can
5218 reasonably move a lease from the backup
5219 state to the free state. */
5220 case FTS_FREE:
5221 return binding_state;
5223 case FTS_BACKUP:
5224 new_state = lease -> binding_state;
5225 goto out;
5227 default:
5228 log_fatal ("Impossible case at %s:%d.", MDL);
5229 return FTS_RESET;
5232 default:
5233 log_fatal ("Impossible case at %s:%d.", MDL);
5234 return FTS_RESET;
5236 out:
5237 return new_state;
5240 /* Determine whether the state transition is okay when we're potentially
5241 in conflict with the peer. */
5242 binding_state_t
5243 conflict_binding_state_transition_check (struct lease *lease,
5244 dhcp_failover_state_t *state,
5245 binding_state_t binding_state,
5246 u_int32_t tsfp)
5248 binding_state_t new_state;
5250 new_state = 0; /* XXXGCC -Wuninitialized */
5252 /* If there is no transition, it's no problem. */
5253 if (binding_state == lease -> binding_state)
5254 new_state = binding_state;
5255 else {
5256 switch (lease -> binding_state) {
5257 /* If we think the lease is not in use, then the
5258 state into which the partner put it is just fine,
5259 whatever it is. */
5260 case FTS_FREE:
5261 case FTS_ABANDONED:
5262 case FTS_EXPIRED:
5263 case FTS_RELEASED:
5264 case FTS_RESET:
5265 case FTS_BACKUP:
5266 new_state = binding_state;
5267 break;
5269 /* If we think the lease *is* in use, then we're not
5270 going to take the partner's change if the partner
5271 thinks it's free. */
5272 case FTS_ACTIVE:
5273 switch (binding_state) {
5274 case FTS_FREE:
5275 case FTS_BACKUP:
5276 case FTS_ABANDONED:
5277 new_state = lease -> binding_state;
5278 break;
5280 case FTS_EXPIRED:
5281 case FTS_RELEASED:
5282 case FTS_RESET:
5283 if (lease -> ends > cur_time)
5284 new_state =
5285 lease -> binding_state;
5286 else
5287 new_state = binding_state;
5288 break;
5290 case FTS_ACTIVE:
5291 new_state = binding_state;
5292 break;
5294 default:
5295 log_fatal ("Impossible case at %s:%d.", MDL);
5296 return FTS_RESET;
5298 break;
5300 default:
5301 log_fatal ("Impossible case at %s:%d.", MDL);
5302 return FTS_RESET;
5305 return new_state;
5308 /* We can reallocate a lease under the following circumstances:
5310 (1) It belongs to us - it's FTS_FREE, and we're primary, or it's
5311 FTS_BACKUP, and we're secondary.
5312 (2) We're in partner_down, and the lease is not active, and we
5313 can be sure that the other server didn't make it active.
5314 We can only be sure that the server didn't make it active
5315 when we are in the partner_down state and one of the following
5316 two conditions holds:
5317 (a) in the case that the time sent from the peer is earlier than
5318 the time we entered the partner_down state, at least MCLT has
5319 gone by since we entered partner_down, or
5320 (b) in the case that the time sent from the peer is later than
5321 the time when we entered partner_down, the current time is
5322 later than the time sent from the peer by at least MCLT. */
5324 int lease_mine_to_reallocate (struct lease *lease)
5326 dhcp_failover_state_t *peer;
5328 if (lease && lease -> pool &&
5329 lease -> pool -> failover_peer) {
5330 peer = lease -> pool -> failover_peer;
5331 switch (lease -> binding_state) {
5332 case FTS_ACTIVE:
5333 return 0;
5335 case FTS_FREE:
5336 if (peer -> i_am == primary)
5337 return 1;
5338 if (peer -> service_state == service_partner_down &&
5339 (lease -> tsfp < peer -> me.stos
5340 ? peer -> me.stos + peer -> mclt < cur_time
5341 : lease -> tsfp + peer -> mclt < cur_time))
5342 return 1;
5343 return 0;
5345 case FTS_ABANDONED:
5346 case FTS_RESET:
5347 case FTS_RELEASED:
5348 case FTS_EXPIRED:
5349 if (peer -> service_state == service_partner_down &&
5350 (lease -> tsfp < peer -> me.stos
5351 ? peer -> me.stos + peer -> mclt < cur_time
5352 : lease -> tsfp + peer -> mclt < cur_time))
5353 return 1;
5354 return 0;
5355 case FTS_BACKUP:
5356 if (peer -> i_am == secondary)
5357 return 1;
5358 if (peer -> service_state == service_partner_down &&
5359 (lease -> tsfp < peer -> me.stos
5360 ? peer -> me.stos + peer -> mclt < cur_time
5361 : lease -> tsfp + peer -> mclt < cur_time))
5362 return 1;
5363 return 0;
5365 return 0;
5367 if (lease)
5368 return !(lease -> binding_state != FTS_FREE &&
5369 lease -> binding_state != FTS_BACKUP);
5370 else
5371 return 0;
5374 static isc_result_t failover_message_reference (failover_message_t **mp,
5375 failover_message_t *m,
5376 const char *file, int line)
5378 *mp = m;
5379 m -> refcnt++;
5380 return ISC_R_SUCCESS;
5383 static isc_result_t failover_message_dereference (failover_message_t **mp,
5384 const char *file, int line)
5386 failover_message_t *m;
5387 m = (*mp);
5388 m -> refcnt--;
5389 if (m -> refcnt == 0) {
5390 if (m -> next)
5391 failover_message_dereference (&m -> next,
5392 file, line);
5393 if (m -> chaddr.data)
5394 dfree (m -> chaddr.data, file, line);
5395 if (m -> client_identifier.data)
5396 dfree (m -> client_identifier.data, file, line);
5397 if (m -> hba.data)
5398 dfree (m -> hba.data, file, line);
5399 if (m -> message.data)
5400 dfree (m -> message.data, file, line);
5401 if (m -> reply_options.data)
5402 dfree (m -> reply_options.data, file, line);
5403 if (m -> request_options.data)
5404 dfree (m -> request_options.data, file, line);
5405 if (m -> vendor_class.data)
5406 dfree (m -> vendor_class.data, file, line);
5407 if (m -> vendor_options.data)
5408 dfree (m -> vendor_options.data, file, line);
5409 if (m -> ddns.data)
5410 dfree (m -> ddns.data, file, line);
5411 dfree (*mp, file, line);
5413 *mp = 0;
5414 return ISC_R_SUCCESS;
5417 OMAPI_OBJECT_ALLOC (dhcp_failover_state, dhcp_failover_state_t,
5418 dhcp_type_failover_state)
5419 OMAPI_OBJECT_ALLOC (dhcp_failover_listener, dhcp_failover_listener_t,
5420 dhcp_type_failover_listener)
5421 OMAPI_OBJECT_ALLOC (dhcp_failover_link, dhcp_failover_link_t,
5422 dhcp_type_failover_link)
5423 #endif /* defined (FAILOVER_PROTOCOL) */
5425 const char *binding_state_print (enum failover_state state)
5427 switch (state) {
5428 case FTS_FREE:
5429 return "free";
5430 break;
5432 case FTS_ACTIVE:
5433 return "active";
5434 break;
5436 case FTS_EXPIRED:
5437 return "expired";
5438 break;
5440 case FTS_RELEASED:
5441 return "released";
5442 break;
5444 case FTS_ABANDONED:
5445 return "abandoned";
5446 break;
5448 case FTS_RESET:
5449 return "reset";
5450 break;
5452 case FTS_BACKUP:
5453 return "backup";
5454 break;
5456 default:
5457 return "unknown";
5458 break;