Remove tm.h and xm.h handling, as it wasn't used. Use nm.h only when needed.
[dragonfly.git] / contrib / dhcp-3.0 / server / failover.c
blob8b3db614fbccdb4c818fc30171ea29eca7f202d9
1 /* failover.c
3 Failover protocol support code... */
5 /*
6 * Copyright (c) 2004 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.53.2.35 2004/11/24 17:39:19 dhankins Exp $ Copyright (c) 2004 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;
60 dhcp_failover_listener_t *l;
62 for (state = failover_states; state; state = state -> next) {
63 dhcp_failover_state_transition (state, "startup");
65 if (state -> pool_count == 0) {
66 log_error ("failover peer declaration with no %s",
67 "referring pools.");
68 log_error ("In order to use failover, you MUST %s",
69 "refer to your main failover declaration");
70 log_error ("in each pool declaration. You MUST %s",
71 "NOT use range declarations outside");
72 log_fatal ("of pool declarations.");
74 /* In case the peer is already running, immediately try
75 to establish a connection with it. */
76 status = dhcp_failover_link_initiate ((omapi_object_t *)state);
77 if (status != ISC_R_SUCCESS && status != ISC_R_INCOMPLETE) {
78 #if defined (DEBUG_FAILOVER_TIMING)
79 log_info ("add_timeout +90 dhcp_failover_reconnect");
80 #endif
81 add_timeout (cur_time + 90,
82 dhcp_failover_reconnect, state,
83 (tvref_t)
84 dhcp_failover_state_reference,
85 (tvunref_t)
86 dhcp_failover_state_dereference);
87 log_error ("failover peer %s: %s", state -> name,
88 isc_result_totext (status));
91 status = (dhcp_failover_listen
92 ((omapi_object_t *)state));
93 if (status != ISC_R_SUCCESS) {
94 #if defined (DEBUG_FAILOVER_TIMING)
95 log_info ("add_timeout +90 %s",
96 "dhcp_failover_listener_restart");
97 #endif
98 add_timeout (cur_time + 90,
99 dhcp_failover_listener_restart,
100 state,
101 (tvref_t)omapi_object_reference,
102 (tvunref_t)omapi_object_dereference);
107 int dhcp_failover_write_all_states ()
109 dhcp_failover_state_t *state;
111 for (state = failover_states; state; state = state -> next) {
112 if (!write_failover_state (state))
113 return 0;
115 return 1;
118 isc_result_t enter_failover_peer (peer)
119 dhcp_failover_state_t *peer;
121 dhcp_failover_state_t *dup = (dhcp_failover_state_t *)0;
122 isc_result_t status;
124 status = find_failover_peer (&dup, peer -> name, MDL);
125 if (status == ISC_R_NOTFOUND) {
126 if (failover_states) {
127 dhcp_failover_state_reference (&peer -> next,
128 failover_states, MDL);
129 dhcp_failover_state_dereference (&failover_states,
130 MDL);
132 dhcp_failover_state_reference (&failover_states, peer, MDL);
133 return ISC_R_SUCCESS;
135 dhcp_failover_state_dereference (&dup, MDL);
136 if (status == ISC_R_SUCCESS)
137 return ISC_R_EXISTS;
138 return status;
141 isc_result_t find_failover_peer (peer, name, file, line)
142 dhcp_failover_state_t **peer;
143 const char *name;
144 const char *file;
145 int line;
147 dhcp_failover_state_t *p;
149 for (p = failover_states; p; p = p -> next)
150 if (!strcmp (name, p -> name))
151 break;
152 if (p)
153 return dhcp_failover_state_reference (peer, p, file, line);
154 return ISC_R_NOTFOUND;
157 /* The failover protocol has three objects associated with it. For
158 each failover partner declaration in the dhcpd.conf file, primary
159 or secondary, there is a failover_state object. For any primary or
160 secondary state object that has a connection to its peer, there is
161 also a failover_link object, which has its own input state seperate
162 from the failover protocol state for managing the actual bytes
163 coming in off the wire. Finally, there will be one listener object
164 for every distinct port number associated with a secondary
165 failover_state object. Normally all secondary failover_state
166 objects are expected to listen on the same port number, so there
167 need be only one listener object, but if different port numbers are
168 specified for each failover object, there could be as many as one
169 listener object for each secondary failover_state object. */
171 /* This, then, is the implemention of the failover link object. */
173 isc_result_t dhcp_failover_link_initiate (omapi_object_t *h)
175 isc_result_t status;
176 dhcp_failover_link_t *obj;
177 omapi_value_t *value = (omapi_value_t *)0;
178 dhcp_failover_state_t *state;
179 omapi_object_t *o;
180 int i;
181 struct data_string ds;
182 omapi_addr_list_t *addrs = (omapi_addr_list_t *)0;
183 omapi_addr_t local_addr;
185 /* Find the failover state in the object chain. */
186 for (o = h; o -> outer; o = o -> outer)
188 for (; o; o = o -> inner) {
189 if (o -> type == dhcp_type_failover_state)
190 break;
192 if (!o)
193 return ISC_R_INVALIDARG;
194 state = (dhcp_failover_state_t *)o;
196 obj = (dhcp_failover_link_t *)0;
197 status = dhcp_failover_link_allocate (&obj, MDL);
198 if (status != ISC_R_SUCCESS)
199 return status;
200 option_cache_reference (&obj -> peer_address,
201 state -> partner.address, MDL);
202 obj -> peer_port = state -> partner.port;
203 dhcp_failover_state_reference (&obj -> state_object, state, MDL);
205 memset (&ds, 0, sizeof ds);
206 if (!evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0,
207 (struct client_state *)0,
208 (struct option_state *)0,
209 (struct option_state *)0,
210 &global_scope, obj -> peer_address, MDL)) {
211 dhcp_failover_link_dereference (&obj, MDL);
212 return ISC_R_UNEXPECTED;
215 /* Make an omapi address list out of a buffer containing zero or more
216 IPv4 addresses. */
217 status = omapi_addr_list_new (&addrs, ds.len / 4, MDL);
218 if (status != ISC_R_SUCCESS) {
219 dhcp_failover_link_dereference (&obj, MDL);
220 return status;
223 for (i = 0; i < addrs -> count; i++) {
224 addrs -> addresses [i].addrtype = AF_INET;
225 addrs -> addresses [i].addrlen = sizeof (struct in_addr);
226 memcpy (addrs -> addresses [i].address,
227 &ds.data [i * 4], sizeof (struct in_addr));
228 addrs -> addresses [i].port = obj -> peer_port;
230 data_string_forget (&ds, MDL);
232 /* Now figure out the local address that we're supposed to use. */
233 if (!state -> me.address ||
234 !evaluate_option_cache (&ds, (struct packet *)0,
235 (struct lease *)0,
236 (struct client_state *)0,
237 (struct option_state *)0,
238 (struct option_state *)0,
239 &global_scope, state -> me.address,
240 MDL)) {
241 memset (&local_addr, 0, sizeof local_addr);
242 local_addr.addrtype = AF_INET;
243 local_addr.addrlen = sizeof (struct in_addr);
244 if (!state -> server_identifier.len) {
245 log_fatal ("failover peer %s: no local address.",
246 state -> name);
248 } else {
249 if (ds.len != sizeof (struct in_addr)) {
250 data_string_forget (&ds, MDL);
251 dhcp_failover_link_dereference (&obj, MDL);
252 omapi_addr_list_dereference (&addrs, MDL);
253 return ISC_R_INVALIDARG;
255 local_addr.addrtype = AF_INET;
256 local_addr.addrlen = ds.len;
257 memcpy (local_addr.address, ds.data, ds.len);
258 if (!state -> server_identifier.len)
259 data_string_copy (&state -> server_identifier,
260 &ds, MDL);
261 data_string_forget (&ds, MDL);
262 local_addr.port = 0; /* Let the O.S. choose. */
265 status = omapi_connect_list ((omapi_object_t *)obj,
266 addrs, &local_addr);
267 omapi_addr_list_dereference (&addrs, MDL);
269 dhcp_failover_link_dereference (&obj, MDL);
270 return status;
273 isc_result_t dhcp_failover_link_signal (omapi_object_t *h,
274 const char *name, va_list ap)
276 isc_result_t status;
277 dhcp_failover_link_t *link;
278 omapi_object_t *c;
279 u_int16_t nlen;
280 u_int32_t vlen;
281 dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
283 if (h -> type != dhcp_type_failover_link) {
284 /* XXX shouldn't happen. Put an assert here? */
285 return ISC_R_UNEXPECTED;
287 link = (dhcp_failover_link_t *)h;
289 if (!strcmp (name, "connect")) {
290 if (link -> state_object -> i_am == primary) {
291 status = dhcp_failover_send_connect (h);
292 if (status != ISC_R_SUCCESS) {
293 log_info ("dhcp_failover_send_connect: %s",
294 isc_result_totext (status));
295 omapi_disconnect (h -> outer, 1);
297 } else
298 status = ISC_R_SUCCESS;
299 /* Allow the peer fifteen seconds to send us a
300 startup message. */
301 #if defined (DEBUG_FAILOVER_TIMING)
302 log_info ("add_timeout +15 %s",
303 "dhcp_failover_link_startup_timeout");
304 #endif
305 add_timeout (cur_time + 15,
306 dhcp_failover_link_startup_timeout,
307 link,
308 (tvref_t)dhcp_failover_link_reference,
309 (tvunref_t)dhcp_failover_link_dereference);
310 return status;
313 if (!strcmp (name, "disconnect")) {
314 if (link -> state_object) {
315 dhcp_failover_state_reference (&state,
316 link -> state_object, MDL);
317 link -> state = dhcp_flink_disconnected;
319 /* Make the transition. */
320 if (state -> link_to_peer == link) {
321 dhcp_failover_state_transition (link -> state_object,
322 name);
324 /* Start trying to reconnect. */
325 #if defined (DEBUG_FAILOVER_TIMING)
326 log_info ("add_timeout +5 %s",
327 "dhcp_failover_reconnect");
328 #endif
329 add_timeout (cur_time + 5, dhcp_failover_reconnect,
330 state,
331 (tvref_t)dhcp_failover_state_reference,
332 (tvunref_t)dhcp_failover_state_dereference);
334 dhcp_failover_state_dereference (&state, MDL);
336 return ISC_R_SUCCESS;
339 if (!strcmp (name, "status")) {
340 if (link -> state_object) {
341 isc_result_t status;
343 status = va_arg(ap, isc_result_t);
345 if ((status == ISC_R_HOSTUNREACH) || (status == ISC_R_TIMEDOUT)) {
346 dhcp_failover_state_reference (&state,
347 link -> state_object, MDL);
348 link -> state = dhcp_flink_disconnected;
350 /* Make the transition. */
351 dhcp_failover_state_transition (link -> state_object,
352 "disconnect");
354 /* Start trying to reconnect. */
355 #if defined (DEBUG_FAILOVER_TIMING)
356 log_info ("add_timeout +5 %s",
357 "dhcp_failover_reconnect");
358 #endif
359 add_timeout (cur_time + 5, dhcp_failover_reconnect,
360 state,
361 (tvref_t)dhcp_failover_state_reference,
362 (tvunref_t)dhcp_failover_state_dereference);
364 dhcp_failover_state_dereference (&state, MDL);
366 return ISC_R_SUCCESS;
369 /* Not a signal we recognize? */
370 if (strcmp (name, "ready")) {
371 if (h -> inner && h -> inner -> type -> signal_handler)
372 return (*(h -> inner -> type -> signal_handler))
373 (h -> inner, name, ap);
374 return ISC_R_NOTFOUND;
377 if (!h -> outer || h -> outer -> type != omapi_type_connection)
378 return ISC_R_INVALIDARG;
379 c = h -> outer;
381 /* We get here because we requested that we be woken up after
382 some number of bytes were read, and that number of bytes
383 has in fact been read. */
384 switch (link -> state) {
385 case dhcp_flink_start:
386 link -> state = dhcp_flink_message_length_wait;
387 if ((omapi_connection_require (c, 2)) != ISC_R_SUCCESS)
388 break;
389 case dhcp_flink_message_length_wait:
390 next_message:
391 link -> state = dhcp_flink_message_wait;
392 link -> imsg = dmalloc (sizeof (failover_message_t), MDL);
393 if (!link -> imsg) {
394 status = ISC_R_NOMEMORY;
395 dhcp_flink_fail:
396 if (link -> imsg) {
397 failover_message_dereference (&link->imsg,
398 MDL);
400 link -> state = dhcp_flink_disconnected;
401 log_info ("message length wait: %s",
402 isc_result_totext (status));
403 omapi_disconnect (c, 1);
404 /* XXX just blow away the protocol state now?
405 XXX or will disconnect blow it away? */
406 return ISC_R_UNEXPECTED;
408 memset (link -> imsg, 0, sizeof (failover_message_t));
409 link -> imsg -> refcnt = 1;
410 /* Get the length: */
411 omapi_connection_get_uint16 (c, &link -> imsg_len);
412 link -> imsg_count = 0; /* Bytes read. */
414 /* Maximum of 2048 bytes in any failover message. */
415 if (link -> imsg_len > DHCP_FAILOVER_MAX_MESSAGE_SIZE) {
416 status = ISC_R_UNEXPECTED;
417 goto dhcp_flink_fail;
420 if ((omapi_connection_require (c, link -> imsg_len - 2U)) !=
421 ISC_R_SUCCESS)
422 break;
423 case dhcp_flink_message_wait:
424 /* Read in the message. At this point we have the
425 entire message in the input buffer. For each
426 incoming value ID, set a bit in the bitmask
427 indicating that we've gotten it. Maybe flag an
428 error message if the bit is already set. Once
429 we're done reading, we can check the bitmask to
430 make sure that the required fields for each message
431 have been included. */
433 link -> imsg_count += 2; /* Count the length as read. */
435 /* Get message type. */
436 omapi_connection_copyout (&link -> imsg -> type, c, 1);
437 link -> imsg_count++;
439 /* Get message payload offset. */
440 omapi_connection_copyout (&link -> imsg_payoff, c, 1);
441 link -> imsg_count++;
443 /* Get message time. */
444 omapi_connection_get_uint32 (c, &link -> imsg -> time);
445 link -> imsg_count += 4;
447 /* Get transaction ID. */
448 omapi_connection_get_uint32 (c, &link -> imsg -> xid);
449 link -> imsg_count += 4;
451 #if defined (DEBUG_FAILOVER_MESSAGES)
452 log_info ("link: message %s payoff %d time %ld xid %ld",
453 dhcp_failover_message_name (link -> imsg -> type),
454 link -> imsg_payoff,
455 (unsigned long)link -> imsg -> time,
456 (unsigned long)link -> imsg -> xid);
457 #endif
458 /* Skip over any portions of the message header that we
459 don't understand. */
460 if (link -> imsg_payoff - link -> imsg_count) {
461 omapi_connection_copyout ((unsigned char *)0, c,
462 (link -> imsg_payoff -
463 link -> imsg_count));
464 link -> imsg_count = link -> imsg_payoff;
467 /* Now start sucking options off the wire. */
468 while (link -> imsg_count < link -> imsg_len) {
469 status = do_a_failover_option (c, link);
470 if (status != ISC_R_SUCCESS)
471 goto dhcp_flink_fail;
474 /* If it's a connect message, try to associate it with
475 a state object. */
476 /* XXX this should be authenticated! */
477 if (link -> imsg -> type == FTM_CONNECT) {
478 const char *errmsg;
479 int reason;
480 /* See if we can find a failover_state object that
481 matches this connection. This message should only
482 be received by a secondary from a primary. */
483 for (s = failover_states; s; s = s -> next) {
484 if (dhcp_failover_state_match
485 (s, (u_int8_t *)&link -> imsg -> server_addr,
486 sizeof link -> imsg -> server_addr))
487 state = s;
490 /* If we can't find a failover protocol state
491 for this remote host, drop the connection */
492 if (!state) {
493 errmsg = "unknown server";
494 reason = FTR_INVALID_PARTNER;
496 badconnect:
497 /* XXX Send a refusal message first?
498 XXX Look in protocol spec for guidance. */
499 log_error ("Failover CONNECT from %u.%u.%u.%u: %s",
500 ((u_int8_t *)
501 (&link -> imsg -> server_addr)) [0],
502 ((u_int8_t *)
503 (&link -> imsg -> server_addr)) [1],
504 ((u_int8_t *)
505 (&link -> imsg -> server_addr)) [2],
506 ((u_int8_t *)
507 (&link -> imsg -> server_addr)) [3],
508 errmsg);
509 dhcp_failover_send_connectack
510 ((omapi_object_t *)link, state,
511 reason, errmsg);
512 log_info ("failover: disconnect: %s", errmsg);
513 omapi_disconnect (c, 0);
514 link -> state = dhcp_flink_disconnected;
515 return ISC_R_SUCCESS;
518 if ((cur_time > link -> imsg -> time &&
519 cur_time - link -> imsg -> time > 60) ||
520 (cur_time < link -> imsg -> time &&
521 link -> imsg -> time - cur_time > 60)) {
522 errmsg = "time offset too large";
523 reason = FTR_TIMEMISMATCH;
524 goto badconnect;
527 if (!(link -> imsg -> options_present & FTB_HBA) ||
528 link -> imsg -> hba.count != 32) {
529 errmsg = "invalid HBA";
530 reason = FTR_HBA_CONFLICT; /* XXX */
531 goto badconnect;
533 if (state -> hba)
534 dfree (state -> hba, MDL);
535 state -> hba = dmalloc (32, MDL);
536 if (!state -> hba) {
537 errmsg = "no memory";
538 reason = FTR_MISC_REJECT;
539 goto badconnect;
541 memcpy (state -> hba, link -> imsg -> hba.data, 32);
543 if (!link -> state_object)
544 dhcp_failover_state_reference
545 (&link -> state_object, state, MDL);
546 if (!link -> peer_address)
547 option_cache_reference
548 (&link -> peer_address,
549 state -> partner.address, MDL);
552 /* If we don't have a state object at this point, it's
553 some kind of bogus situation, so just drop the
554 connection. */
555 if (!link -> state_object) {
556 log_info ("failover: connect: no matching state.");
557 omapi_disconnect (c, 1);
558 link -> state = dhcp_flink_disconnected;
559 return ISC_R_INVALIDARG;
562 /* Once we have the entire message, and we've validated
563 it as best we can here, pass it to the parent. */
564 omapi_signal ((omapi_object_t *)link -> state_object,
565 "message", link);
566 link -> state = dhcp_flink_message_length_wait;
567 failover_message_dereference (&link -> imsg, MDL);
568 /* XXX This is dangerous because we could get into a tight
569 XXX loop reading input without servicing any other stuff.
570 XXX There needs to be a way to relinquish control but
571 XXX get it back immediately if there's no other work to
572 XXX do. */
573 if ((omapi_connection_require (c, 2)) == ISC_R_SUCCESS)
574 goto next_message;
575 break;
577 default:
578 /* XXX should never get here. Assertion? */
579 break;
581 return ISC_R_SUCCESS;
584 static isc_result_t do_a_failover_option (c, link)
585 omapi_object_t *c;
586 dhcp_failover_link_t *link;
588 u_int16_t option_code;
589 u_int16_t option_len;
590 unsigned char *op;
591 unsigned op_size;
592 unsigned op_count;
593 int i;
594 isc_result_t status;
596 if (link -> imsg_count + 2 > link -> imsg_len) {
597 log_error ("FAILOVER: message overflow at option code.");
598 return ISC_R_PROTOCOLERROR;
601 /* Get option code. */
602 omapi_connection_get_uint16 (c, &option_code);
603 link -> imsg_count += 2;
605 if (link -> imsg_count + 2 > link -> imsg_len) {
606 log_error ("FAILOVER: message overflow at length.");
607 return ISC_R_PROTOCOLERROR;
610 /* Get option length. */
611 omapi_connection_get_uint16 (c, &option_len);
612 link -> imsg_count += 2;
614 if (link -> imsg_count + option_len > link -> imsg_len) {
615 log_error ("FAILOVER: message overflow at data.");
616 return ISC_R_PROTOCOLERROR;
619 /* If it's an unknown code, skip over it. */
620 if (option_code > FTO_MAX) {
621 #if defined (DEBUG_FAILOVER_MESSAGES)
622 log_debug (" option code %d (%s) len %d (not recognized)",
623 option_code,
624 dhcp_failover_option_name (option_code),
625 option_len);
626 #endif
627 omapi_connection_copyout ((unsigned char *)0, c, option_len);
628 link -> imsg_count += option_len;
629 return ISC_R_SUCCESS;
632 /* If it's the digest, do it now. */
633 if (ft_options [option_code].type == FT_DIGEST) {
634 link -> imsg_count += option_len;
635 if (link -> imsg_count != link -> imsg_len) {
636 log_error ("FAILOVER: digest not at end of message");
637 return ISC_R_PROTOCOLERROR;
639 #if defined (DEBUG_FAILOVER_MESSAGES)
640 log_debug (" option %s len %d",
641 ft_options [option_code].name, option_len);
642 #endif
643 /* For now, just dump it. */
644 omapi_connection_copyout ((unsigned char *)0, c, option_len);
645 return ISC_R_SUCCESS;
648 /* Only accept an option once. */
649 if (link -> imsg -> options_present & ft_options [option_code].bit) {
650 log_error ("FAILOVER: duplicate option %s",
651 ft_options [option_code].name);
652 return ISC_R_PROTOCOLERROR;
655 /* Make sure the option is appropriate for this type of message.
656 Really, any option is generally allowed for any message, and the
657 cases where this is not true are too complicated to represent in
658 this way - what this code is doing is to just avoid saving the
659 value of an option we don't have any way to use, which allows
660 us to make the failover_message structure smaller. */
661 if (ft_options [option_code].bit &&
662 !(fto_allowed [link -> imsg -> type] &
663 ft_options [option_code].bit)) {
664 omapi_connection_copyout ((unsigned char *)0, c, option_len);
665 link -> imsg_count += option_len;
666 return ISC_R_SUCCESS;
669 /* Figure out how many elements, how big they are, and where
670 to store them. */
671 if (ft_options [option_code].num_present) {
672 /* If this option takes a fixed number of elements,
673 we expect the space for them to be preallocated,
674 and we can just read the data in. */
676 op = ((unsigned char *)link -> imsg) +
677 ft_options [option_code].offset;
678 op_size = ft_sizes [ft_options [option_code].type];
679 op_count = ft_options [option_code].num_present;
681 if (option_len != op_size * op_count) {
682 log_error ("FAILOVER: option size (%d:%d), option %s",
683 option_len,
684 (ft_sizes [ft_options [option_code].type] *
685 ft_options [option_code].num_present),
686 ft_options [option_code].name);
687 return ISC_R_PROTOCOLERROR;
689 } else {
690 failover_option_t *fo;
692 /* FT_DDNS* are special - one or two bytes of status
693 followed by the client FQDN. */
694 if (ft_options [option_code].type == FT_DDNS1 ||
695 ft_options [option_code].type == FT_DDNS1) {
696 ddns_fqdn_t *ddns =
697 ((ddns_fqdn_t *)
698 (((char *)link -> imsg) +
699 ft_options [option_code].offset));
701 op_count = (ft_options [option_code].type == FT_DDNS1
702 ? 1 : 2);
704 omapi_connection_copyout (&ddns -> codes [0],
705 c, op_count);
706 link -> imsg_count += op_count;
707 if (op_count == 1)
708 ddns -> codes [1] = 0;
709 op_size = 1;
710 op_count = option_len - op_count;
712 ddns -> length = op_count;
713 ddns -> data = dmalloc (op_count, MDL);
714 if (!ddns -> data) {
715 log_error ("FAILOVER: no memory getting%s(%d)",
716 " DNS data ", op_count);
718 /* Actually, NO_MEMORY, but if we lose here
719 we have to drop the connection. */
720 return ISC_R_PROTOCOLERROR;
722 omapi_connection_copyout (ddns -> data, c, op_count);
723 goto out;
726 /* A zero for num_present means that any number of
727 elements can appear, so we have to figure out how
728 many we got from the length of the option, and then
729 fill out a failover_option structure describing the
730 data. */
731 op_size = ft_sizes [ft_options [option_code].type];
733 /* Make sure that option data length is a multiple of the
734 size of the data type being sent. */
735 if (op_size > 1 && option_len % op_size) {
736 log_error ("FAILOVER: option_len %d not %s%d",
737 option_len, "multiple of ", op_size);
738 return ISC_R_PROTOCOLERROR;
741 op_count = option_len / op_size;
743 fo = ((failover_option_t *)
744 (((char *)link -> imsg) +
745 ft_options [option_code].offset));
747 fo -> count = op_count;
748 fo -> data = dmalloc (option_len, MDL);
749 if (!fo -> data) {
750 log_error ("FAILOVER: no memory getting %s (%d)",
751 "option data", op_count);
753 return ISC_R_PROTOCOLERROR;
755 op = fo -> data;
758 /* For single-byte message values and multi-byte values that
759 don't need swapping, just read them in all at once. */
760 if (op_size == 1 || ft_options [option_code].type == FT_IPADDR) {
761 omapi_connection_copyout ((unsigned char *)op, c, option_len);
762 link -> imsg_count += option_len;
763 goto out;
766 /* For values that require swapping, read them in one at a time
767 using routines that swap bytes. */
768 for (i = 0; i < op_count; i++) {
769 switch (ft_options [option_code].type) {
770 case FT_UINT32:
771 omapi_connection_get_uint32 (c, (u_int32_t *)op);
772 op += 4;
773 link -> imsg_count += 4;
774 break;
776 case FT_UINT16:
777 omapi_connection_get_uint16 (c, (u_int16_t *)op);
778 op += 2;
779 link -> imsg_count += 2;
780 break;
782 default:
783 /* Everything else should have been handled
784 already. */
785 log_error ("FAILOVER: option %s: bad type %d",
786 ft_options [option_code].name,
787 ft_options [option_code].type);
788 return ISC_R_PROTOCOLERROR;
791 out:
792 /* Remember that we got this option. */
793 link -> imsg -> options_present |= ft_options [option_code].bit;
794 return ISC_R_SUCCESS;
797 isc_result_t dhcp_failover_link_set_value (omapi_object_t *h,
798 omapi_object_t *id,
799 omapi_data_string_t *name,
800 omapi_typed_data_t *value)
802 if (h -> type != omapi_type_protocol)
803 return ISC_R_INVALIDARG;
805 /* Never valid to set these. */
806 if (!omapi_ds_strcmp (name, "link-port") ||
807 !omapi_ds_strcmp (name, "link-name") ||
808 !omapi_ds_strcmp (name, "link-state"))
809 return ISC_R_NOPERM;
811 if (h -> inner && h -> inner -> type -> set_value)
812 return (*(h -> inner -> type -> set_value))
813 (h -> inner, id, name, value);
814 return ISC_R_NOTFOUND;
817 isc_result_t dhcp_failover_link_get_value (omapi_object_t *h,
818 omapi_object_t *id,
819 omapi_data_string_t *name,
820 omapi_value_t **value)
822 dhcp_failover_link_t *link;
824 if (h -> type != omapi_type_protocol)
825 return ISC_R_INVALIDARG;
826 link = (dhcp_failover_link_t *)h;
828 if (!omapi_ds_strcmp (name, "link-port")) {
829 return omapi_make_int_value (value, name,
830 (int)link -> peer_port, MDL);
831 } else if (!omapi_ds_strcmp (name, "link-state")) {
832 if (link -> state < 0 ||
833 link -> state >= dhcp_flink_state_max)
834 return omapi_make_string_value (value, name,
835 "invalid link state",
836 MDL);
837 return omapi_make_string_value
838 (value, name,
839 dhcp_flink_state_names [link -> state], MDL);
842 if (h -> inner && h -> inner -> type -> get_value)
843 return (*(h -> inner -> type -> get_value))
844 (h -> inner, id, name, value);
845 return ISC_R_NOTFOUND;
848 isc_result_t dhcp_failover_link_destroy (omapi_object_t *h,
849 const char *file, int line)
851 dhcp_failover_link_t *link;
852 if (h -> type != dhcp_type_failover_link)
853 return ISC_R_INVALIDARG;
854 link = (dhcp_failover_link_t *)h;
856 if (link -> peer_address)
857 option_cache_dereference (&link -> peer_address, file, line);
858 if (link -> imsg)
859 failover_message_dereference (&link -> imsg, file, line);
860 if (link -> state_object)
861 dhcp_failover_state_dereference (&link -> state_object,
862 file, line);
863 return ISC_R_SUCCESS;
866 /* Write all the published values associated with the object through the
867 specified connection. */
869 isc_result_t dhcp_failover_link_stuff_values (omapi_object_t *c,
870 omapi_object_t *id,
871 omapi_object_t *l)
873 dhcp_failover_link_t *link;
874 isc_result_t status;
876 if (l -> type != dhcp_type_failover_link)
877 return ISC_R_INVALIDARG;
878 link = (dhcp_failover_link_t *)l;
880 status = omapi_connection_put_name (c, "link-port");
881 if (status != ISC_R_SUCCESS)
882 return status;
883 status = omapi_connection_put_uint32 (c, sizeof (int));
884 if (status != ISC_R_SUCCESS)
885 return status;
886 status = omapi_connection_put_uint32 (c, link -> peer_port);
887 if (status != ISC_R_SUCCESS)
888 return status;
890 status = omapi_connection_put_name (c, "link-state");
891 if (status != ISC_R_SUCCESS)
892 return status;
893 if (link -> state < 0 ||
894 link -> state >= dhcp_flink_state_max)
895 status = omapi_connection_put_string (c, "invalid link state");
896 else
897 status = (omapi_connection_put_string
898 (c, dhcp_flink_state_names [link -> state]));
899 if (status != ISC_R_SUCCESS)
900 return status;
902 if (link -> inner && link -> inner -> type -> stuff_values)
903 return (*(link -> inner -> type -> stuff_values)) (c, id,
904 link -> inner);
905 return ISC_R_SUCCESS;
908 /* Set up a listener for the omapi protocol. The handle stored points to
909 a listener object, not a protocol object. */
911 isc_result_t dhcp_failover_listen (omapi_object_t *h)
913 isc_result_t status;
914 dhcp_failover_listener_t *obj, *l;
915 omapi_value_t *value = (omapi_value_t *)0;
916 omapi_addr_t local_addr;
917 unsigned long port;
919 status = omapi_get_value_str (h, (omapi_object_t *)0,
920 "local-port", &value);
921 if (status != ISC_R_SUCCESS)
922 return status;
923 if (!value -> value) {
924 omapi_value_dereference (&value, MDL);
925 return ISC_R_INVALIDARG;
928 status = omapi_get_int_value (&port, value -> value);
929 omapi_value_dereference (&value, MDL);
930 if (status != ISC_R_SUCCESS)
931 return status;
932 local_addr.port = port;
934 status = omapi_get_value_str (h, (omapi_object_t *)0,
935 "local-address", &value);
936 if (status != ISC_R_SUCCESS)
937 return status;
938 if (!value -> value) {
939 nogood:
940 omapi_value_dereference (&value, MDL);
941 return ISC_R_INVALIDARG;
944 if (value -> value -> type != omapi_datatype_data ||
945 value -> value -> u.buffer.len != sizeof (struct in_addr))
946 goto nogood;
948 memcpy (local_addr.address, value -> value -> u.buffer.value,
949 value -> value -> u.buffer.len);
950 local_addr.addrlen = value -> value -> u.buffer.len;
951 local_addr.addrtype = AF_INET;
953 omapi_value_dereference (&value, MDL);
955 /* Are we already listening on this port and address? */
956 for (l = failover_listeners; l; l = l -> next) {
957 if (l -> address.port == local_addr.port &&
958 l -> address.addrtype == local_addr.addrtype &&
959 l -> address.addrlen == local_addr.addrlen &&
960 !memcmp (l -> address.address, local_addr.address,
961 local_addr.addrlen))
962 break;
964 /* Already listening. */
965 if (l)
966 return ISC_R_SUCCESS;
968 obj = (dhcp_failover_listener_t *)0;
969 status = dhcp_failover_listener_allocate (&obj, MDL);
970 if (status != ISC_R_SUCCESS)
971 return status;
972 obj -> address = local_addr;
974 status = omapi_listen_addr ((omapi_object_t *)obj, &obj -> address, 1);
975 if (status != ISC_R_SUCCESS)
976 return status;
978 status = omapi_object_reference (&h -> outer,
979 (omapi_object_t *)obj, MDL);
980 if (status != ISC_R_SUCCESS) {
981 dhcp_failover_listener_dereference (&obj, MDL);
982 return status;
984 status = omapi_object_reference (&obj -> inner, h, MDL);
985 if (status != ISC_R_SUCCESS) {
986 dhcp_failover_listener_dereference (&obj, MDL);
987 return status;
990 /* Put this listener on the list. */
991 if (failover_listeners) {
992 dhcp_failover_listener_reference (&obj -> next,
993 failover_listeners, MDL);
994 dhcp_failover_listener_dereference (&failover_listeners, MDL);
996 dhcp_failover_listener_reference (&failover_listeners, obj, MDL);
998 return dhcp_failover_listener_dereference (&obj, MDL);
1001 /* Signal handler for protocol listener - if we get a connect signal,
1002 create a new protocol connection, otherwise pass the signal down. */
1004 isc_result_t dhcp_failover_listener_signal (omapi_object_t *o,
1005 const char *name, va_list ap)
1007 isc_result_t status;
1008 omapi_connection_object_t *c;
1009 dhcp_failover_link_t *obj;
1010 dhcp_failover_listener_t *p;
1011 dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
1013 if (!o || o -> type != dhcp_type_failover_listener)
1014 return ISC_R_INVALIDARG;
1015 p = (dhcp_failover_listener_t *)o;
1017 /* Not a signal we recognize? */
1018 if (strcmp (name, "connect")) {
1019 if (p -> inner && p -> inner -> type -> signal_handler)
1020 return (*(p -> inner -> type -> signal_handler))
1021 (p -> inner, name, ap);
1022 return ISC_R_NOTFOUND;
1025 c = va_arg (ap, omapi_connection_object_t *);
1026 if (!c || c -> type != omapi_type_connection)
1027 return ISC_R_INVALIDARG;
1029 /* See if we can find a failover_state object that
1030 matches this connection. */
1031 for (s = failover_states; s; s = s -> next) {
1032 if (dhcp_failover_state_match
1033 (s, (u_int8_t *)&c -> remote_addr.sin_addr,
1034 sizeof c -> remote_addr.sin_addr)) {
1035 state = s;
1036 break;
1039 if (!state) {
1040 log_info ("failover: listener: no matching state");
1041 return omapi_disconnect ((omapi_object_t *)c, 1);
1044 obj = (dhcp_failover_link_t *)0;
1045 status = dhcp_failover_link_allocate (&obj, MDL);
1046 if (status != ISC_R_SUCCESS)
1047 return status;
1048 obj -> peer_port = ntohs (c -> remote_addr.sin_port);
1050 status = omapi_object_reference (&obj -> outer,
1051 (omapi_object_t *)c, MDL);
1052 if (status != ISC_R_SUCCESS) {
1053 lose:
1054 dhcp_failover_link_dereference (&obj, MDL);
1055 log_info ("failover: listener: picayune failure.");
1056 omapi_disconnect ((omapi_object_t *)c, 1);
1057 return status;
1060 status = omapi_object_reference (&c -> inner,
1061 (omapi_object_t *)obj, MDL);
1062 if (status != ISC_R_SUCCESS)
1063 goto lose;
1065 status = dhcp_failover_state_reference (&obj -> state_object,
1066 state, MDL);
1067 if (status != ISC_R_SUCCESS)
1068 goto lose;
1070 omapi_signal_in ((omapi_object_t *)obj, "connect");
1072 return dhcp_failover_link_dereference (&obj, MDL);
1075 isc_result_t dhcp_failover_listener_set_value (omapi_object_t *h,
1076 omapi_object_t *id,
1077 omapi_data_string_t *name,
1078 omapi_typed_data_t *value)
1080 if (h -> type != dhcp_type_failover_listener)
1081 return ISC_R_INVALIDARG;
1083 if (h -> inner && h -> inner -> type -> set_value)
1084 return (*(h -> inner -> type -> set_value))
1085 (h -> inner, id, name, value);
1086 return ISC_R_NOTFOUND;
1089 isc_result_t dhcp_failover_listener_get_value (omapi_object_t *h,
1090 omapi_object_t *id,
1091 omapi_data_string_t *name,
1092 omapi_value_t **value)
1094 if (h -> type != dhcp_type_failover_listener)
1095 return ISC_R_INVALIDARG;
1097 if (h -> inner && h -> inner -> type -> get_value)
1098 return (*(h -> inner -> type -> get_value))
1099 (h -> inner, id, name, value);
1100 return ISC_R_NOTFOUND;
1103 isc_result_t dhcp_failover_listener_destroy (omapi_object_t *h,
1104 const char *file, int line)
1106 dhcp_failover_listener_t *l;
1108 if (h -> type != dhcp_type_failover_listener)
1109 return ISC_R_INVALIDARG;
1110 l = (dhcp_failover_listener_t *)h;
1111 if (l -> next)
1112 dhcp_failover_listener_dereference (&l -> next, file, line);
1114 return ISC_R_SUCCESS;
1117 /* Write all the published values associated with the object through the
1118 specified connection. */
1120 isc_result_t dhcp_failover_listener_stuff (omapi_object_t *c,
1121 omapi_object_t *id,
1122 omapi_object_t *p)
1124 int i;
1126 if (p -> type != dhcp_type_failover_listener)
1127 return ISC_R_INVALIDARG;
1129 if (p -> inner && p -> inner -> type -> stuff_values)
1130 return (*(p -> inner -> type -> stuff_values)) (c, id,
1131 p -> inner);
1132 return ISC_R_SUCCESS;
1135 /* Set up master state machine for the failover protocol. */
1137 isc_result_t dhcp_failover_register (omapi_object_t *h)
1139 isc_result_t status;
1140 dhcp_failover_state_t *obj;
1141 unsigned long port;
1142 omapi_value_t *value = (omapi_value_t *)0;
1144 status = omapi_get_value_str (h, (omapi_object_t *)0,
1145 "local-port", &value);
1146 if (status != ISC_R_SUCCESS)
1147 return status;
1148 if (!value -> value) {
1149 omapi_value_dereference (&value, MDL);
1150 return ISC_R_INVALIDARG;
1153 status = omapi_get_int_value (&port, value -> value);
1154 omapi_value_dereference (&value, MDL);
1155 if (status != ISC_R_SUCCESS)
1156 return status;
1158 obj = (dhcp_failover_state_t *)0;
1159 dhcp_failover_state_allocate (&obj, MDL);
1160 obj -> me.port = port;
1162 status = omapi_listen ((omapi_object_t *)obj, port, 1);
1163 if (status != ISC_R_SUCCESS) {
1164 dhcp_failover_state_dereference (&obj, MDL);
1165 return status;
1168 status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj,
1169 MDL);
1170 if (status != ISC_R_SUCCESS) {
1171 dhcp_failover_state_dereference (&obj, MDL);
1172 return status;
1174 status = omapi_object_reference (&obj -> inner, h, MDL);
1175 dhcp_failover_state_dereference (&obj, MDL);
1176 return status;
1179 /* Signal handler for protocol state machine. */
1181 isc_result_t dhcp_failover_state_signal (omapi_object_t *o,
1182 const char *name, va_list ap)
1184 isc_result_t status;
1185 omapi_connection_object_t *c;
1186 omapi_protocol_object_t *obj;
1187 dhcp_failover_state_t *state;
1188 dhcp_failover_link_t *link;
1189 char *peer_name;
1191 if (!o || o -> type != dhcp_type_failover_state)
1192 return ISC_R_INVALIDARG;
1193 state = (dhcp_failover_state_t *)o;
1195 /* Not a signal we recognize? */
1196 if (strcmp (name, "disconnect") &&
1197 strcmp (name, "message")) {
1198 if (state -> inner && state -> inner -> type -> signal_handler)
1199 return (*(state -> inner -> type -> signal_handler))
1200 (state -> inner, name, ap);
1201 return ISC_R_NOTFOUND;
1204 /* Handle connect signals by seeing what state we're in
1205 and potentially doing a state transition. */
1206 if (!strcmp (name, "disconnect")) {
1207 link = va_arg (ap, dhcp_failover_link_t *);
1209 dhcp_failover_link_dereference (&state -> link_to_peer, MDL);
1210 dhcp_failover_state_transition (state, "disconnect");
1211 if (state -> i_am == primary) {
1212 #if defined (DEBUG_FAILOVER_TIMING)
1213 log_info ("add_timeout +90 %s",
1214 "dhcp_failover_reconnect");
1215 #endif
1216 add_timeout (cur_time + 90, dhcp_failover_reconnect,
1217 state,
1218 (tvref_t)dhcp_failover_state_reference,
1219 (tvunref_t)
1220 dhcp_failover_state_dereference);
1222 } else if (!strcmp (name, "message")) {
1223 link = va_arg (ap, dhcp_failover_link_t *);
1225 if (link -> imsg -> type == FTM_CONNECT) {
1226 /* If we already have a link to the peer, it must be
1227 dead, so drop it.
1228 XXX Is this the right thing to do?
1229 XXX Probably not - what if both peers start at
1230 XXX the same time? */
1231 if (state -> link_to_peer) {
1232 dhcp_failover_send_connectack
1233 ((omapi_object_t *)link, state,
1234 FTR_DUP_CONNECTION,
1235 "already connected");
1236 omapi_disconnect (link -> outer, 1);
1237 return ISC_R_SUCCESS;
1239 if (!(link -> imsg -> options_present & FTB_MCLT)) {
1240 dhcp_failover_send_connectack
1241 ((omapi_object_t *)link, state,
1242 FTR_INVALID_MCLT,
1243 "no MCLT provided");
1244 omapi_disconnect (link -> outer, 1);
1245 return ISC_R_SUCCESS;
1248 dhcp_failover_link_reference (&state -> link_to_peer,
1249 link, MDL);
1250 status = (dhcp_failover_send_connectack
1251 ((omapi_object_t *)link, state, 0, 0));
1252 if (status != ISC_R_SUCCESS) {
1253 dhcp_failover_link_dereference
1254 (&state -> link_to_peer, MDL);
1255 log_info ("dhcp_failover_send_connectack: %s",
1256 isc_result_totext (status));
1257 omapi_disconnect (link -> outer, 1);
1258 return ISC_R_SUCCESS;
1260 if (link -> imsg -> options_present & FTB_MAX_UNACKED)
1261 state -> partner.max_flying_updates =
1262 link -> imsg -> max_unacked;
1263 if (link -> imsg -> options_present &
1264 FTB_RECEIVE_TIMER)
1265 state -> partner.max_response_delay =
1266 link -> imsg -> receive_timer;
1267 state -> mclt = link -> imsg -> mclt;
1268 dhcp_failover_send_state (state);
1269 cancel_timeout (dhcp_failover_link_startup_timeout,
1270 link);
1271 } else if (link -> imsg -> type == FTM_CONNECTACK) {
1272 const char *errmsg;
1273 int reason;
1275 cancel_timeout (dhcp_failover_link_startup_timeout,
1276 link);
1278 if (link -> imsg -> reject_reason) {
1279 log_error ("Failover CONNECT to %u.%u.%u.%u%s%s",
1280 ((u_int8_t *)
1281 (&link -> imsg -> server_addr)) [0],
1282 ((u_int8_t *)
1283 (&link -> imsg -> server_addr)) [1],
1284 ((u_int8_t *)
1285 (&link -> imsg -> server_addr)) [2],
1286 ((u_int8_t *)
1287 (&link -> imsg -> server_addr)) [3],
1288 " rejected: ",
1289 (dhcp_failover_reject_reason_print
1290 (link -> imsg -> reject_reason)));
1291 /* XXX print message from peer if peer sent message. */
1292 omapi_disconnect (link -> outer, 1);
1293 return ISC_R_SUCCESS;
1296 if (!dhcp_failover_state_match
1297 (state,
1298 (u_int8_t *)&link -> imsg -> server_addr,
1299 sizeof link -> imsg -> server_addr)) {
1300 errmsg = "unknown server";
1301 reason = FTR_INVALID_PARTNER;
1302 badconnectack:
1303 log_error ("Failover CONNECTACK from %u.%u.%u.%u: %s",
1304 ((u_int8_t *)
1305 (&link -> imsg -> server_addr)) [0],
1306 ((u_int8_t *)
1307 (&link -> imsg -> server_addr)) [1],
1308 ((u_int8_t *)
1309 (&link -> imsg -> server_addr)) [2],
1310 ((u_int8_t *)
1311 (&link -> imsg -> server_addr)) [3],
1312 errmsg);
1313 dhcp_failover_send_disconnect ((omapi_object_t *)link,
1314 reason, errmsg);
1315 omapi_disconnect (link -> outer, 0);
1316 return ISC_R_SUCCESS;
1319 if (state -> link_to_peer) {
1320 errmsg = "already connected";
1321 reason = FTR_DUP_CONNECTION;
1322 goto badconnectack;
1325 if ((cur_time > link -> imsg -> time &&
1326 cur_time - link -> imsg -> time > 60) ||
1327 (cur_time < link -> imsg -> time &&
1328 link -> imsg -> time - cur_time > 60)) {
1329 errmsg = "time offset too large";
1330 reason = FTR_TIMEMISMATCH;
1331 goto badconnectack;
1334 dhcp_failover_link_reference (&state -> link_to_peer,
1335 link, MDL);
1336 #if 0
1337 /* XXX This is probably the right thing to do, but
1338 XXX for release three, to make the smallest possible
1339 XXX change, we are doing this when the peer state
1340 XXX changes instead. */
1341 if (state -> me.state == startup)
1342 dhcp_failover_set_state (state,
1343 state -> saved_state);
1344 else
1345 #endif
1346 dhcp_failover_send_state (state);
1348 if (link -> imsg -> options_present & FTB_MAX_UNACKED)
1349 state -> partner.max_flying_updates =
1350 link -> imsg -> max_unacked;
1351 if (link -> imsg -> options_present & FTB_RECEIVE_TIMER)
1352 state -> partner.max_response_delay =
1353 link -> imsg -> receive_timer;
1354 #if defined (DEBUG_FAILOVER_TIMING)
1355 log_info ("add_timeout +%d %s",
1356 (int)state -> partner.max_response_delay / 3,
1357 "dhcp_failover_send_contact");
1358 #endif
1359 add_timeout (cur_time +
1360 (int)state -> partner.max_response_delay / 3,
1361 dhcp_failover_send_contact, state,
1362 (tvref_t)dhcp_failover_state_reference,
1363 (tvunref_t)dhcp_failover_state_dereference);
1364 #if defined (DEBUG_FAILOVER_TIMING)
1365 log_info ("add_timeout +%d %s",
1366 (int)state -> me.max_response_delay,
1367 "dhcp_failover_timeout");
1368 #endif
1369 add_timeout (cur_time +
1370 (int)state -> me.max_response_delay,
1371 dhcp_failover_timeout, state,
1372 (tvref_t)dhcp_failover_state_reference,
1373 (tvunref_t)dhcp_failover_state_dereference);
1374 } else if (link -> imsg -> type == FTM_DISCONNECT) {
1375 if (link -> imsg -> reject_reason) {
1376 log_error ("Failover DISCONNECT from %u.%u.%u.%u%s%s",
1377 ((u_int8_t *)
1378 (&link -> imsg -> server_addr)) [0],
1379 ((u_int8_t *)
1380 (&link -> imsg -> server_addr)) [1],
1381 ((u_int8_t *)
1382 (&link -> imsg -> server_addr)) [2],
1383 ((u_int8_t *)
1384 (&link -> imsg -> server_addr)) [3],
1385 ": ",
1386 (dhcp_failover_reject_reason_print
1387 (link -> imsg -> reject_reason)));
1389 omapi_disconnect (link -> outer, 1);
1390 } else if (link -> imsg -> type == FTM_BNDUPD) {
1391 dhcp_failover_process_bind_update (state,
1392 link -> imsg);
1393 } else if (link -> imsg -> type == FTM_BNDACK) {
1394 dhcp_failover_process_bind_ack (state, link -> imsg);
1395 } else if (link -> imsg -> type == FTM_UPDREQ) {
1396 dhcp_failover_process_update_request (state,
1397 link -> imsg);
1398 } else if (link -> imsg -> type == FTM_UPDREQALL) {
1399 dhcp_failover_process_update_request_all
1400 (state, link -> imsg);
1401 } else if (link -> imsg -> type == FTM_UPDDONE) {
1402 dhcp_failover_process_update_done (state,
1403 link -> imsg);
1404 } else if (link -> imsg -> type == FTM_POOLREQ) {
1405 dhcp_failover_pool_rebalance (state);
1406 } else if (link -> imsg -> type == FTM_POOLRESP) {
1407 log_info ("pool response: %ld leases",
1408 (unsigned long)
1409 link -> imsg -> addresses_transferred);
1410 } else if (link -> imsg -> type == FTM_STATE) {
1411 dhcp_failover_peer_state_changed (state,
1412 link -> imsg);
1415 /* Add a timeout so that if the partner doesn't send
1416 another message for the maximum transmit idle time
1417 plus a grace of one second, we close the
1418 connection. */
1419 if (state -> link_to_peer &&
1420 state -> link_to_peer == link &&
1421 state -> link_to_peer -> state != dhcp_flink_disconnected)
1423 #if defined (DEBUG_FAILOVER_TIMING)
1424 log_info ("add_timeout +%d %s",
1425 (int)state -> me.max_response_delay,
1426 "dhcp_failover_timeout");
1427 #endif
1428 add_timeout (cur_time +
1429 (int)state -> me.max_response_delay,
1430 dhcp_failover_timeout, state,
1431 (tvref_t)dhcp_failover_state_reference,
1432 (tvunref_t)dhcp_failover_state_dereference);
1437 /* Handle all the events we care about... */
1438 return ISC_R_SUCCESS;
1441 isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *state,
1442 const char *name)
1444 isc_result_t status;
1446 /* XXX Check these state transitions against the spec! */
1447 if (!strcmp (name, "disconnect")) {
1448 if (state -> link_to_peer) {
1449 log_info ("peer %s: disconnected", state -> name);
1450 if (state -> link_to_peer -> state_object)
1451 dhcp_failover_state_dereference
1452 (&state -> link_to_peer -> state_object, MDL);
1453 dhcp_failover_link_dereference (&state -> link_to_peer,
1454 MDL);
1456 cancel_timeout (dhcp_failover_send_contact, state);
1457 cancel_timeout (dhcp_failover_timeout, state);
1458 cancel_timeout (dhcp_failover_startup_timeout, state);
1460 switch (state -> me.state == startup ?
1461 state -> saved_state : state -> me.state) {
1462 case resolution_interrupted:
1463 case partner_down:
1464 case communications_interrupted:
1465 case recover:
1466 /* Already in the right state? */
1467 if (state -> me.state == startup)
1468 return (dhcp_failover_set_state
1469 (state, state -> saved_state));
1470 return ISC_R_SUCCESS;
1472 case potential_conflict:
1473 return dhcp_failover_set_state
1474 (state, resolution_interrupted);
1476 case normal:
1477 return dhcp_failover_set_state
1478 (state, communications_interrupted);
1480 case unknown_state:
1481 return dhcp_failover_set_state
1482 (state, resolution_interrupted);
1483 case startup:
1484 break; /* can't happen. */
1486 } else if (!strcmp (name, "connect")) {
1487 switch (state -> me.state) {
1488 case communications_interrupted:
1489 status = dhcp_failover_set_state (state, normal);
1490 dhcp_failover_send_updates (state);
1491 return status;
1493 case resolution_interrupted:
1494 return dhcp_failover_set_state (state,
1495 potential_conflict);
1497 case partner_down:
1498 case potential_conflict:
1499 case normal:
1500 case recover:
1501 case shut_down:
1502 case paused:
1503 case unknown_state:
1504 case recover_done:
1505 case startup:
1506 case recover_wait:
1507 return dhcp_failover_send_state (state);
1509 } else if (!strcmp (name, "startup")) {
1510 dhcp_failover_set_state (state, startup);
1511 return ISC_R_SUCCESS;
1512 } else if (!strcmp (name, "connect-timeout")) {
1513 switch (state -> me.state) {
1514 case communications_interrupted:
1515 case partner_down:
1516 case resolution_interrupted:
1517 return ISC_R_SUCCESS;
1519 case normal:
1520 case recover:
1521 return dhcp_failover_set_state
1522 (state, communications_interrupted);
1524 case potential_conflict:
1525 return dhcp_failover_set_state
1526 (state, resolution_interrupted);
1528 case unknown_state:
1529 return dhcp_failover_set_state
1530 (state, communications_interrupted);
1532 default:
1533 return dhcp_failover_set_state
1534 (state, resolution_interrupted);
1537 return ISC_R_INVALIDARG;
1540 isc_result_t dhcp_failover_set_service_state (dhcp_failover_state_t *state)
1542 switch (state -> me.state) {
1543 case unknown_state:
1544 state -> service_state = not_responding;
1545 state -> nrr = " (my state unknown)";
1546 break;
1548 case partner_down:
1549 state -> service_state = service_partner_down;
1550 state -> nrr = "";
1551 break;
1553 case normal:
1554 state -> service_state = cooperating;
1555 state -> nrr = "";
1556 break;
1558 case communications_interrupted:
1559 state -> service_state = not_cooperating;
1560 state -> nrr = "";
1561 break;
1563 case resolution_interrupted:
1564 case potential_conflict:
1565 state -> service_state = not_responding;
1566 state -> nrr = " (resolving conflicts)";
1567 break;
1569 case recover:
1570 state -> service_state = not_responding;
1571 state -> nrr = " (recovering)";
1572 break;
1574 case shut_down:
1575 state -> service_state = not_responding;
1576 state -> nrr = " (shut down)";
1577 break;
1579 case paused:
1580 state -> service_state = not_responding;
1581 state -> nrr = " (paused)";
1582 break;
1584 case recover_wait:
1585 state -> service_state = not_responding;
1586 state -> nrr = " (recover wait)";
1587 break;
1589 case recover_done:
1590 state -> service_state = not_responding;
1591 state -> nrr = " (recover done)";
1592 break;
1594 case startup:
1595 state -> service_state = service_startup;
1596 state -> nrr = " (startup)";
1597 break;
1600 /* Some peer states can require us not to respond, even if our
1601 state doesn't. */
1602 /* XXX hm. I suspect this isn't true anymore. */
1603 if (state -> service_state != not_responding) {
1604 switch (state -> partner.state) {
1605 case partner_down:
1606 state -> service_state = not_responding;
1607 state -> nrr = " (recovering)";
1608 break;
1610 case potential_conflict:
1611 state -> service_state = not_responding;
1612 state -> nrr = " (resolving conflicts)";
1613 break;
1615 /* Other peer states don't affect our behaviour. */
1616 default:
1617 break;
1621 return ISC_R_SUCCESS;
1624 isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *state,
1625 enum failover_state new_state)
1627 enum failover_state saved_state;
1628 TIME saved_stos;
1629 struct pool *p;
1630 struct shared_network *s;
1631 struct lease *l;
1633 /* First make the transition out of the current state. */
1634 switch (state -> me.state) {
1635 case normal:
1636 /* Any updates that haven't been acked yet, we have to
1637 resend, just in case. */
1638 if (state -> ack_queue_tail) {
1639 struct lease *lp;
1641 /* Zap the flags. */
1642 for (lp = state -> ack_queue_head; lp; lp = lp -> next_pending)
1643 lp -> flags = ((lp -> flags & ~ON_ACK_QUEUE) |
1644 ON_UPDATE_QUEUE);
1646 /* Now hook the ack queue to the beginning of the update
1647 queue. */
1648 if (state -> update_queue_head) {
1649 lease_reference (&state -> ack_queue_tail -> next_pending,
1650 state -> update_queue_head, MDL);
1651 lease_dereference (&state -> update_queue_head, MDL);
1653 lease_reference (&state -> update_queue_head,
1654 state -> ack_queue_head, MDL);
1655 if (!state -> update_queue_tail) {
1656 #if defined (POINTER_DEBUG)
1657 if (state -> ack_queue_tail -> next_pending) {
1658 log_error ("next pending on ack queue tail.");
1659 abort ();
1661 #endif
1662 lease_reference (&state -> update_queue_tail,
1663 state -> ack_queue_tail, MDL);
1665 lease_dereference (&state -> ack_queue_tail, MDL);
1666 lease_dereference (&state -> ack_queue_head, MDL);
1667 state -> cur_unacked_updates = 0;
1669 cancel_timeout (dhcp_failover_keepalive, state);
1670 break;
1672 case recover:
1673 case recover_wait:
1674 case recover_done:
1675 case potential_conflict:
1676 case partner_down:
1677 case communications_interrupted:
1678 case resolution_interrupted:
1679 case startup:
1680 default:
1681 break;
1684 /* Tentatively make the transition. */
1685 saved_state = state -> me.state;
1686 saved_stos = state -> me.stos;
1688 /* Keep the old stos if we're going into recover_wait or if we're
1689 coming into or out of startup. */
1690 if (new_state != recover_wait && new_state != startup &&
1691 saved_state != startup)
1692 state -> me.stos = cur_time;
1694 /* If we're in shutdown, peer is in partner_down, and we're moving
1695 to recover, we can skip waiting for MCLT to expire. This happens
1696 when a server is moved administratively into shutdown prior to
1697 actually shutting down. Of course, if there are any updates
1698 pending we can't actually do this. */
1699 if (new_state == recover && saved_state == shut_down &&
1700 state -> partner.state == partner_down &&
1701 !state -> update_queue_head && !state -> ack_queue_head)
1702 state -> me.stos = cur_time - state -> mclt;
1704 state -> me.state = new_state;
1705 if (new_state == startup && saved_state != startup)
1706 state -> saved_state = saved_state;
1708 /* If we can't record the new state, we can't make a state transition. */
1709 if (!write_failover_state (state) || !commit_leases ()) {
1710 log_error ("Unable to record current failover state for %s",
1711 state -> name);
1712 state -> me.state = saved_state;
1713 state -> me.stos = saved_stos;
1714 return ISC_R_IOERROR;
1717 log_info ("failover peer %s: I move from %s to %s",
1718 state -> name, dhcp_failover_state_name_print (saved_state),
1719 dhcp_failover_state_name_print (state -> me.state));
1721 /* If we were in startup and we just left it, cancel the timeout. */
1722 if (new_state != startup && saved_state == startup)
1723 cancel_timeout (dhcp_failover_startup_timeout, state);
1725 /* Set our service state. */
1726 dhcp_failover_set_service_state (state);
1728 /* Tell the peer about it. */
1729 if (state -> link_to_peer)
1730 dhcp_failover_send_state (state);
1732 switch (new_state) {
1733 case normal:
1734 if (state -> partner.state == normal)
1735 dhcp_failover_state_pool_check (state);
1736 break;
1738 case potential_conflict:
1739 if (state -> i_am == primary)
1740 dhcp_failover_send_update_request (state);
1741 break;
1743 case startup:
1744 #if defined (DEBUG_FAILOVER_TIMING)
1745 log_info ("add_timeout +15 %s",
1746 "dhcp_failover_startup_timeout");
1747 #endif
1748 add_timeout (cur_time + 15,
1749 dhcp_failover_startup_timeout,
1750 state,
1751 (tvref_t)omapi_object_reference,
1752 (tvunref_t)
1753 omapi_object_dereference);
1754 break;
1756 /* If we come back in recover_wait and there's still waiting
1757 to do, set a timeout. */
1758 case recover_wait:
1759 if (state -> me.stos + state -> mclt > cur_time) {
1760 #if defined (DEBUG_FAILOVER_TIMING)
1761 log_info ("add_timeout +%d %s",
1762 (int)(cur_time -
1763 state -> me.stos + state -> mclt),
1764 "dhcp_failover_startup_timeout");
1765 #endif
1766 add_timeout ((int)(state -> me.stos + state -> mclt),
1767 dhcp_failover_recover_done,
1768 state,
1769 (tvref_t)omapi_object_reference,
1770 (tvunref_t)
1771 omapi_object_dereference);
1772 } else
1773 dhcp_failover_recover_done (state);
1774 break;
1776 case recover:
1777 if (state -> link_to_peer)
1778 dhcp_failover_send_update_request_all (state);
1779 break;
1781 case partner_down:
1782 /* For every expired lease, set a timeout for it to become free. */
1783 for (s = shared_networks; s; s = s -> next) {
1784 for (p = s -> pools; p; p = p -> next) {
1785 if (p -> failover_peer == state) {
1786 for (l = p -> expired; l; l = l -> next)
1787 l -> tsfp = state -> me.stos + state -> mclt;
1788 if (p -> next_event_time >
1789 state -> me.stos + state -> mclt) {
1790 p -> next_event_time =
1791 state -> me.stos + state -> mclt;
1792 #if defined (DEBUG_FAILOVER_TIMING)
1793 log_info ("add_timeout +%d %s",
1794 (int)(cur_time - p -> next_event_time),
1795 "pool_timer");
1796 #endif
1797 add_timeout (p -> next_event_time, pool_timer, p,
1798 (tvref_t)pool_reference,
1799 (tvunref_t)pool_dereference);
1804 break;
1807 default:
1808 break;
1811 return ISC_R_SUCCESS;
1814 isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state,
1815 failover_message_t *msg)
1817 enum failover_state previous_state = state -> partner.state;
1818 enum failover_state new_state;
1819 int startupp;
1820 isc_result_t status;
1822 new_state = msg -> server_state;
1823 startupp = (msg -> server_flags & FTF_STARTUP) ? 1 : 0;
1825 if (state -> partner.state == new_state && state -> me.state) {
1826 switch (state -> me.state) {
1827 case startup:
1828 dhcp_failover_set_state (state, state -> saved_state);
1829 return ISC_R_SUCCESS;
1831 case unknown_state:
1832 case normal:
1833 case potential_conflict:
1834 case recover_done:
1835 case shut_down:
1836 case paused:
1837 case recover_wait:
1838 return ISC_R_SUCCESS;
1840 /* If we get a peer state change when we're
1841 disconnected, we always process it. */
1842 case partner_down:
1843 case communications_interrupted:
1844 case resolution_interrupted:
1845 case recover:
1846 break;
1850 state -> partner.state = new_state;
1852 log_info ("failover peer %s: peer moves from %s to %s",
1853 state -> name,
1854 dhcp_failover_state_name_print (previous_state),
1855 dhcp_failover_state_name_print (state -> partner.state));
1857 if (!write_failover_state (state) || !commit_leases ()) {
1858 /* This is bad, but it's not fatal. Of course, if we
1859 can't write to the lease database, we're not going to
1860 get much done anyway. */
1861 log_error ("Unable to record current failover state for %s",
1862 state -> name);
1865 /* Do any state transitions that are required as a result of the
1866 peer's state transition. */
1868 switch (state -> me.state == startup ?
1869 state -> saved_state : state -> me.state) {
1870 case startup: /* can't happen. */
1871 break;
1873 case normal:
1874 switch (new_state) {
1875 case normal:
1876 dhcp_failover_state_pool_check (state);
1877 break;
1879 case communications_interrupted:
1880 break;
1882 case partner_down:
1883 if (state -> me.state == startup)
1884 dhcp_failover_set_state (state, recover);
1885 else
1886 dhcp_failover_set_state (state,
1887 potential_conflict);
1888 break;
1890 case potential_conflict:
1891 case resolution_interrupted:
1892 /* None of these transitions should ever occur. */
1893 dhcp_failover_set_state (state, shut_down);
1894 break;
1896 case recover:
1897 dhcp_failover_set_state (state, partner_down);
1898 break;
1900 case shut_down:
1901 /* XXX This one is specified, but it's specified in
1902 XXX the documentation for the shut_down state,
1903 XXX not the normal state. */
1904 dhcp_failover_set_state (state, partner_down);
1905 break;
1907 case paused:
1908 dhcp_failover_set_state (state,
1909 communications_interrupted);
1910 break;
1912 case recover_wait:
1913 case recover_done:
1914 /* We probably don't need to do anything here. */
1915 break;
1917 case unknown_state:
1918 case startup:
1919 break;
1921 break;
1923 case recover:
1924 switch (new_state) {
1925 case recover:
1926 log_info ("failover peer %s: requesting %s",
1927 state -> name, "full update from peer");
1928 /* Don't send updreqall if we're really in the
1929 startup state, because that will result in two
1930 being sent. */
1931 if (state -> me.state == recover)
1932 dhcp_failover_send_update_request_all (state);
1933 break;
1935 case potential_conflict:
1936 case resolution_interrupted:
1937 case normal:
1938 dhcp_failover_set_state (state, potential_conflict);
1939 break;
1941 case partner_down:
1942 case communications_interrupted:
1943 /* We're supposed to send an update request at this
1944 point. */
1945 /* XXX we don't currently have code here to do any
1946 XXX clever detection of when we should send an
1947 XXX UPDREQALL message rather than an UPDREQ
1948 XXX message. What to do, what to do? */
1949 dhcp_failover_send_update_request (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 int polarity;
2167 binding_state_t peer_lease_state;
2168 binding_state_t my_lease_state;
2169 struct lease **lq;
2170 int tenper;
2172 if (state -> me.state != normal || state -> i_am == secondary)
2173 return 0;
2175 for (s = shared_networks; s; s = s -> next) {
2176 for (p = s -> pools; p; p = p -> next) {
2177 if (p -> failover_peer != state)
2178 continue;
2180 /* Right now we're giving the peer half of the free leases.
2181 If we have more leases than the peer (i.e., more than
2182 half), then the number of leases we have, less the number
2183 of leases the peer has, will be how many more leases we
2184 have than the peer has. So if we send half that number
2185 to the peer, we should be even. */
2186 if (p -> failover_peer -> i_am == primary) {
2187 lts = (p -> free_leases - p -> backup_leases) / 2;
2188 peer_lease_state = FTS_BACKUP;
2189 my_lease_state = FTS_FREE;
2190 lq = &p -> free;
2191 } else {
2192 lts = (p -> backup_leases - p -> free_leases) / 2;
2193 peer_lease_state = FTS_FREE;
2194 my_lease_state = FTS_BACKUP;
2195 lq = &p -> backup;
2198 tenper = (p -> backup_leases + p -> free_leases) / 10;
2199 if (tenper == 0)
2200 tenper = 1;
2201 if (lts > tenper) {
2202 log_info ("pool %lx %s total %d free %d %s %d lts %d",
2203 (unsigned long)p,
2204 (p -> shared_network ?
2205 p -> shared_network -> name : ""), p -> lease_count,
2206 p -> free_leases, "backup", p -> backup_leases, lts);
2208 lease_reference (&lp, *lq, MDL);
2210 while (lp && lts) {
2211 /* Remember the next lease in the list. */
2212 if (next)
2213 lease_dereference (&next, MDL);
2214 if (lp -> next)
2215 lease_reference (&next, lp -> next, MDL);
2217 --lts;
2218 ++leases_queued;
2219 lp -> next_binding_state = peer_lease_state;
2220 lp -> tstp = cur_time;
2221 lp -> starts = cur_time;
2223 if (!supersede_lease (lp, (struct lease *)0, 0, 1, 0)
2224 || !write_lease (lp))
2226 log_info ("can't commit lease %s on giveaway",
2227 piaddr (lp -> ip_addr));
2230 lease_dereference (&lp, MDL);
2231 if (next)
2232 lease_reference (&lp, next, MDL);
2234 if (next)
2235 lease_dereference (&next, MDL);
2236 if (lp)
2237 lease_dereference (&lp, MDL);
2240 if (lts > 1) {
2241 log_info ("lease imbalance - lts = %d", lts);
2245 commit_leases();
2246 dhcp_failover_send_poolresp (state, leases_queued);
2247 dhcp_failover_send_updates (state);
2248 return leases_queued;
2251 int dhcp_failover_pool_check (struct pool *pool)
2253 int lts;
2254 struct lease *lp;
2255 int tenper;
2257 if (!pool -> failover_peer ||
2258 pool -> failover_peer -> me.state != normal)
2259 return 0;
2261 if (pool -> failover_peer -> i_am == primary)
2262 lts = (pool -> backup_leases - pool -> free_leases) / 2;
2263 else
2264 lts = (pool -> free_leases - pool -> backup_leases) / 2;
2266 log_info ("pool %lx %s total %d free %d backup %d lts %d",
2267 (unsigned long)pool,
2268 pool -> shared_network ? pool -> shared_network -> name : "",
2269 pool -> lease_count,
2270 pool -> free_leases, pool -> backup_leases, lts);
2272 tenper = (pool -> backup_leases + pool -> free_leases) / 10;
2273 if (tenper == 0)
2274 tenper = 1;
2275 if (lts > tenper) {
2276 /* XXX What about multiple pools? */
2277 if (pool -> failover_peer -> i_am == secondary) {
2278 /* Ask the primary to send us leases. */
2279 dhcp_failover_send_poolreq (pool -> failover_peer);
2280 return 1;
2281 } else {
2282 /* Figure out how many leases to skip on the backup
2283 list. We skip the earliest leases on the list
2284 to reduce the chance of trying to steal a lease
2285 that the secondary is about to allocate. */
2286 int i = pool -> backup_leases - lts;
2287 log_info ("Taking %d leases from secondary.", lts);
2288 for (lp = pool -> backup; lp; lp = lp -> next) {
2289 /* Skip to the last leases on the free
2290 list, because they are less likely
2291 to already have been allocated. */
2292 if (i)
2293 --i;
2294 else {
2295 lp -> desired_binding_state = FTS_FREE;
2296 dhcp_failover_queue_update (lp, 1);
2297 --lts;
2300 if (lts)
2301 log_info ("failed to take %d leases.", lts);
2304 return 0;
2307 int dhcp_failover_state_pool_check (dhcp_failover_state_t *state)
2309 struct lease *lp;
2310 struct shared_network *s;
2311 struct pool *p;
2313 for (s = shared_networks; s; s = s -> next) {
2314 for (p = s -> pools; p; p = p -> next) {
2315 if (p -> failover_peer != state)
2316 continue;
2317 /* Only need to request rebalance on one pool. */
2318 if (dhcp_failover_pool_check (p))
2319 return 1;
2322 return 0;
2325 isc_result_t dhcp_failover_send_updates (dhcp_failover_state_t *state)
2327 struct lease *lp = (struct lease *)0;
2328 isc_result_t status;
2330 /* Can't update peer if we're not talking to it! */
2331 if (!state -> link_to_peer)
2332 return ISC_R_SUCCESS;
2334 while ((state -> partner.max_flying_updates >
2335 state -> cur_unacked_updates) && state -> update_queue_head) {
2336 /* Grab the head of the update queue. */
2337 lease_reference (&lp, state -> update_queue_head, MDL);
2339 /* Send the update to the peer. */
2340 status = dhcp_failover_send_bind_update (state, lp);
2341 if (status != ISC_R_SUCCESS) {
2342 lease_dereference (&lp, MDL);
2343 return status;
2345 lp -> flags &= ~ON_UPDATE_QUEUE;
2347 /* Take it off the head of the update queue and put the next
2348 item in the update queue at the head. */
2349 lease_dereference (&state -> update_queue_head, MDL);
2350 if (lp -> next_pending) {
2351 lease_reference (&state -> update_queue_head,
2352 lp -> next_pending, MDL);
2353 lease_dereference (&lp -> next_pending, MDL);
2354 } else {
2355 lease_dereference (&state -> update_queue_tail, MDL);
2358 if (state -> ack_queue_head) {
2359 lease_reference
2360 (&state -> ack_queue_tail -> next_pending,
2361 lp, MDL);
2362 lease_dereference (&state -> ack_queue_tail, MDL);
2363 } else {
2364 lease_reference (&state -> ack_queue_head, lp, MDL);
2366 #if defined (POINTER_DEBUG)
2367 if (lp -> next_pending) {
2368 log_error ("ack_queue_tail: lp -> next_pending");
2369 abort ();
2371 #endif
2372 lease_reference (&state -> ack_queue_tail, lp, MDL);
2373 lp -> flags |= ON_ACK_QUEUE;
2374 lease_dereference (&lp, MDL);
2376 /* Count the object as an unacked update. */
2377 state -> cur_unacked_updates++;
2379 return ISC_R_SUCCESS;
2382 /* Queue an update for a lease. Always returns 1 at this point - it's
2383 not an error for this to be called on a lease for which there's no
2384 failover peer. */
2386 int dhcp_failover_queue_update (struct lease *lease, int immediate)
2388 dhcp_failover_state_t *state;
2390 if (!lease -> pool ||
2391 !lease -> pool -> failover_peer)
2392 return 1;
2394 /* If it's already on the update queue, leave it there. */
2395 if (lease -> flags & ON_UPDATE_QUEUE)
2396 return 1;
2398 /* Get the failover state structure for this lease. */
2399 state = lease -> pool -> failover_peer;
2401 /* If it's on the ack queue, take it off. */
2402 if (lease -> flags & ON_ACK_QUEUE)
2403 dhcp_failover_ack_queue_remove (state, lease);
2405 if (state -> update_queue_head) {
2406 lease_reference (&state -> update_queue_tail -> next_pending,
2407 lease, MDL);
2408 lease_dereference (&state -> update_queue_tail, MDL);
2409 } else {
2410 lease_reference (&state -> update_queue_head, lease, MDL);
2412 #if defined (POINTER_DEBUG)
2413 if (lease -> next_pending) {
2414 log_error ("next pending on update queue lease.");
2415 #if defined (DEBUG_RC_HISTORY)
2416 dump_rc_history (lease);
2417 #endif
2418 abort ();
2420 #endif
2421 lease_reference (&state -> update_queue_tail, lease, MDL);
2422 lease -> flags |= ON_UPDATE_QUEUE;
2423 if (immediate)
2424 dhcp_failover_send_updates (state);
2425 return 1;
2428 int dhcp_failover_send_acks (dhcp_failover_state_t *state)
2430 failover_message_t *msg = (failover_message_t *)0;
2432 /* Must commit all leases prior to acking them. */
2433 if (!commit_leases ())
2434 return 0;
2436 while (state -> toack_queue_head) {
2437 failover_message_reference
2438 (&msg, state -> toack_queue_head, MDL);
2439 failover_message_dereference
2440 (&state -> toack_queue_head, MDL);
2441 if (msg -> next) {
2442 failover_message_reference
2443 (&state -> toack_queue_head, msg -> next, MDL);
2446 dhcp_failover_send_bind_ack (state, msg, 0, (const char *)0);
2448 failover_message_dereference (&msg, MDL);
2451 if (state -> toack_queue_tail)
2452 failover_message_dereference (&state -> toack_queue_tail, MDL);
2453 state -> pending_acks = 0;
2455 return 1;
2458 void dhcp_failover_toack_queue_timeout (void *vs)
2460 dhcp_failover_state_t *state = vs;
2462 #if defined (DEBUG_FAILOVER_TIMING)
2463 log_info ("dhcp_failover_toack_queue_timeout");
2464 #endif
2466 dhcp_failover_send_acks (state);
2469 /* Queue an ack for a message. There is currently no way to queue a
2470 negative ack -- these need to be sent directly. */
2472 int dhcp_failover_queue_ack (dhcp_failover_state_t *state,
2473 failover_message_t *msg)
2475 if (state -> toack_queue_head) {
2476 failover_message_reference
2477 (&state -> toack_queue_tail -> next, msg, MDL);
2478 failover_message_dereference (&state -> toack_queue_tail, MDL);
2479 } else {
2480 failover_message_reference (&state -> toack_queue_head,
2481 msg, MDL);
2483 failover_message_reference (&state -> toack_queue_tail, msg, MDL);
2485 state -> pending_acks++;
2487 /* Flush the toack queue whenever we exceed half the number of
2488 allowed unacked updates. */
2489 if (state -> pending_acks >= state -> partner.max_flying_updates / 2) {
2490 dhcp_failover_send_acks (state);
2493 /* Schedule a timeout to flush the ack queue. */
2494 if (state -> pending_acks > 0) {
2495 #if defined (DEBUG_FAILOVER_TIMING)
2496 log_info ("add_timeout +2 %s",
2497 "dhcp_failover_toack_queue_timeout");
2498 #endif
2499 add_timeout (cur_time + 2,
2500 dhcp_failover_toack_queue_timeout, state,
2501 (tvref_t)dhcp_failover_state_reference,
2502 (tvunref_t)dhcp_failover_state_dereference);
2505 return 1;
2508 void dhcp_failover_ack_queue_remove (dhcp_failover_state_t *state,
2509 struct lease *lease)
2511 struct lease *lp;
2513 if (!(lease -> flags & ON_ACK_QUEUE))
2514 return;
2516 if (state -> ack_queue_head == lease) {
2517 lease_dereference (&state -> ack_queue_head, MDL);
2518 if (lease -> next_pending) {
2519 lease_reference (&state -> ack_queue_head,
2520 lease -> next_pending, MDL);
2521 lease_dereference (&lease -> next_pending, MDL);
2522 } else {
2523 lease_dereference (&state -> ack_queue_tail, MDL);
2525 } else {
2526 for (lp = state -> ack_queue_head;
2527 lp && lp -> next_pending != lease;
2528 lp = lp -> next_pending)
2531 if (!lp)
2532 return;
2534 lease_dereference (&lp -> next_pending, MDL);
2535 if (lease -> next_pending) {
2536 lease_reference (&lp -> next_pending,
2537 lease -> next_pending, MDL);
2538 lease_dereference (&lease -> next_pending, MDL);
2539 } else {
2540 lease_dereference (&state -> ack_queue_tail, MDL);
2541 if (lp -> next_pending) {
2542 log_error ("state -> ack_queue_tail");
2543 abort ();
2545 lease_reference (&state -> ack_queue_tail, lp, MDL);
2549 lease -> flags &= ~ON_ACK_QUEUE;
2550 state -> cur_unacked_updates--;
2553 * When updating leases as a result of an ack, we defer the commit
2554 * for performance reasons. When there are no more acks pending,
2555 * do a commit.
2557 if (state -> cur_unacked_updates == 0) {
2558 commit_leases();
2562 isc_result_t dhcp_failover_state_set_value (omapi_object_t *h,
2563 omapi_object_t *id,
2564 omapi_data_string_t *name,
2565 omapi_typed_data_t *value)
2567 isc_result_t status;
2569 if (h -> type != dhcp_type_failover_state)
2570 return ISC_R_INVALIDARG;
2572 /* This list of successful returns is completely wrong, but the
2573 fastest way to make dhcpctl do something vaguely sane when
2574 you try to change the local state. */
2576 if (!omapi_ds_strcmp (name, "name")) {
2577 return ISC_R_SUCCESS;
2578 } else if (!omapi_ds_strcmp (name, "partner-address")) {
2579 return ISC_R_SUCCESS;
2580 } else if (!omapi_ds_strcmp (name, "local-address")) {
2581 return ISC_R_SUCCESS;
2582 } else if (!omapi_ds_strcmp (name, "partner-port")) {
2583 return ISC_R_SUCCESS;
2584 } else if (!omapi_ds_strcmp (name, "local-port")) {
2585 return ISC_R_SUCCESS;
2586 } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
2587 return ISC_R_SUCCESS;
2588 } else if (!omapi_ds_strcmp (name, "mclt")) {
2589 return ISC_R_SUCCESS;
2590 } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
2591 return ISC_R_SUCCESS;
2592 } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
2593 return ISC_R_SUCCESS;
2594 } else if (!omapi_ds_strcmp (name, "partner-state")) {
2595 return ISC_R_SUCCESS;
2596 } else if (!omapi_ds_strcmp (name, "local-state")) {
2597 unsigned long l;
2598 status = omapi_get_int_value (&l, value);
2599 if (status != ISC_R_SUCCESS)
2600 return status;
2601 return dhcp_failover_set_state ((dhcp_failover_state_t *)h, l);
2602 } else if (!omapi_ds_strcmp (name, "partner-stos")) {
2603 return ISC_R_SUCCESS;
2604 } else if (!omapi_ds_strcmp (name, "local-stos")) {
2605 return ISC_R_SUCCESS;
2606 } else if (!omapi_ds_strcmp (name, "hierarchy")) {
2607 return ISC_R_SUCCESS;
2608 } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
2609 return ISC_R_SUCCESS;
2610 } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
2611 return ISC_R_SUCCESS;
2612 } else if (!omapi_ds_strcmp (name, "skew")) {
2613 return ISC_R_SUCCESS;
2614 } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
2615 return ISC_R_SUCCESS;
2616 } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
2617 return ISC_R_SUCCESS;
2620 if (h -> inner && h -> inner -> type -> set_value)
2621 return (*(h -> inner -> type -> set_value))
2622 (h -> inner, id, name, value);
2623 return ISC_R_NOTFOUND;
2626 void dhcp_failover_keepalive (void *vs)
2628 dhcp_failover_state_t *state = vs;
2631 void dhcp_failover_reconnect (void *vs)
2633 dhcp_failover_state_t *state = vs;
2634 isc_result_t status;
2636 #if defined (DEBUG_FAILOVER_TIMING)
2637 log_info ("dhcp_failover_reconnect");
2638 #endif
2639 /* If we already connected the other way, let the connection
2640 recovery code initiate any retry that may be required. */
2641 if (state -> link_to_peer)
2642 return;
2644 status = dhcp_failover_link_initiate ((omapi_object_t *)state);
2645 if (status != ISC_R_SUCCESS && status != ISC_R_INCOMPLETE) {
2646 log_info ("failover peer %s: %s", state -> name,
2647 isc_result_totext (status));
2648 #if defined (DEBUG_FAILOVER_TIMING)
2649 log_info ("add_timeout +90 %s",
2650 "dhcp_failover_listener_restart");
2651 #endif
2652 add_timeout (cur_time + 90,
2653 dhcp_failover_listener_restart, state,
2654 (tvref_t)dhcp_failover_state_reference,
2655 (tvunref_t)dhcp_failover_state_dereference);
2659 void dhcp_failover_startup_timeout (void *vs)
2661 dhcp_failover_state_t *state = vs;
2662 isc_result_t status;
2664 #if defined (DEBUG_FAILOVER_TIMING)
2665 log_info ("dhcp_failover_startup_timeout");
2666 #endif
2668 dhcp_failover_state_transition (state, "disconnect");
2671 void dhcp_failover_link_startup_timeout (void *vl)
2673 dhcp_failover_link_t *link = vl;
2674 isc_result_t status;
2675 omapi_object_t *p;
2677 for (p = (omapi_object_t *)link; p -> inner; p = p -> inner)
2679 for (; p; p = p -> outer)
2680 if (p -> type == omapi_type_connection)
2681 break;
2682 if (p) {
2683 log_info ("failover: link startup timeout");
2684 omapi_disconnect (p, 1);
2688 void dhcp_failover_listener_restart (void *vs)
2690 dhcp_failover_state_t *state = vs;
2691 isc_result_t status;
2693 #if defined (DEBUG_FAILOVER_TIMING)
2694 log_info ("dhcp_failover_listener_restart");
2695 #endif
2697 status = dhcp_failover_listen ((omapi_object_t *)state);
2698 if (status != ISC_R_SUCCESS) {
2699 log_info ("failover peer %s: %s", state -> name,
2700 isc_result_totext (status));
2701 #if defined (DEBUG_FAILOVER_TIMING)
2702 log_info ("add_timeout +90 %s",
2703 "dhcp_failover_listener_restart");
2704 #endif
2705 add_timeout (cur_time + 90,
2706 dhcp_failover_listener_restart, state,
2707 (tvref_t)dhcp_failover_state_reference,
2708 (tvunref_t)dhcp_failover_state_dereference);
2712 isc_result_t dhcp_failover_state_get_value (omapi_object_t *h,
2713 omapi_object_t *id,
2714 omapi_data_string_t *name,
2715 omapi_value_t **value)
2717 dhcp_failover_state_t *s;
2718 struct option_cache *oc;
2719 struct data_string ds;
2720 isc_result_t status;
2722 if (h -> type != dhcp_type_failover_state)
2723 return ISC_R_INVALIDARG;
2724 s = (dhcp_failover_state_t *)h;
2726 if (!omapi_ds_strcmp (name, "name")) {
2727 if (s -> name)
2728 return omapi_make_string_value (value,
2729 name, s -> name, MDL);
2730 return ISC_R_NOTFOUND;
2731 } else if (!omapi_ds_strcmp (name, "partner-address")) {
2732 oc = s -> partner.address;
2733 getaddr:
2734 memset (&ds, 0, sizeof ds);
2735 if (!evaluate_option_cache (&ds, (struct packet *)0,
2736 (struct lease *)0,
2737 (struct client_state *)0,
2738 (struct option_state *)0,
2739 (struct option_state *)0,
2740 &global_scope, oc, MDL)) {
2741 return ISC_R_NOTFOUND;
2743 status = omapi_make_const_value (value,
2744 name, ds.data, ds.len, MDL);
2745 /* Disgusting kludge: */
2746 if (oc == s -> me.address && !s -> server_identifier.len)
2747 data_string_copy (&s -> server_identifier, &ds, MDL);
2748 data_string_forget (&ds, MDL);
2749 return status;
2750 } else if (!omapi_ds_strcmp (name, "local-address")) {
2751 oc = s -> me.address;
2752 goto getaddr;
2753 } else if (!omapi_ds_strcmp (name, "partner-port")) {
2754 return omapi_make_int_value (value, name,
2755 s -> partner.port, MDL);
2756 } else if (!omapi_ds_strcmp (name, "local-port")) {
2757 return omapi_make_int_value (value,
2758 name, s -> me.port, MDL);
2759 } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
2760 return omapi_make_uint_value (value, name,
2761 s -> me.max_flying_updates,
2762 MDL);
2763 } else if (!omapi_ds_strcmp (name, "mclt")) {
2764 return omapi_make_uint_value (value, name, s -> mclt, MDL);
2765 } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
2766 return omapi_make_int_value (value, name,
2767 s -> load_balance_max_secs, MDL);
2768 } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
2769 if (s -> hba)
2770 return omapi_make_const_value (value, name,
2771 s -> hba, 32, MDL);
2772 return ISC_R_NOTFOUND;
2773 } else if (!omapi_ds_strcmp (name, "partner-state")) {
2774 return omapi_make_uint_value (value, name,
2775 s -> partner.state, MDL);
2776 } else if (!omapi_ds_strcmp (name, "local-state")) {
2777 return omapi_make_uint_value (value, name,
2778 s -> me.state, MDL);
2779 } else if (!omapi_ds_strcmp (name, "partner-stos")) {
2780 return omapi_make_int_value (value, name,
2781 s -> partner.stos, MDL);
2782 } else if (!omapi_ds_strcmp (name, "local-stos")) {
2783 return omapi_make_int_value (value, name,
2784 s -> me.stos, MDL);
2785 } else if (!omapi_ds_strcmp (name, "hierarchy")) {
2786 return omapi_make_uint_value (value, name, s -> i_am, MDL);
2787 } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
2788 return omapi_make_int_value (value, name,
2789 s -> last_packet_sent, MDL);
2790 } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
2791 return omapi_make_int_value (value, name,
2792 s -> last_timestamp_received,
2793 MDL);
2794 } else if (!omapi_ds_strcmp (name, "skew")) {
2795 return omapi_make_int_value (value, name, s -> skew, MDL);
2796 } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
2797 return omapi_make_uint_value (value, name,
2798 s -> me.max_response_delay,
2799 MDL);
2800 } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
2801 return omapi_make_int_value (value, name,
2802 s -> cur_unacked_updates, MDL);
2805 if (h -> inner && h -> inner -> type -> get_value)
2806 return (*(h -> inner -> type -> get_value))
2807 (h -> inner, id, name, value);
2808 return ISC_R_NOTFOUND;
2811 isc_result_t dhcp_failover_state_destroy (omapi_object_t *h,
2812 const char *file, int line)
2814 dhcp_failover_state_t *s;
2816 if (h -> type != dhcp_type_failover_state)
2817 return ISC_R_INVALIDARG;
2818 s = (dhcp_failover_state_t *)h;
2820 if (s -> link_to_peer)
2821 dhcp_failover_link_dereference (&s -> link_to_peer, file, line);
2822 if (s -> name) {
2823 dfree (s -> name, MDL);
2824 s -> name = (char *)0;
2826 if (s -> partner.address)
2827 option_cache_dereference (&s -> partner.address, file, line);
2828 if (s -> me.address)
2829 option_cache_dereference (&s -> me.address, file, line);
2830 if (s -> hba) {
2831 dfree (s -> hba, file, line);
2832 s -> hba = (u_int8_t *)0;
2834 if (s -> update_queue_head)
2835 lease_dereference (&s -> update_queue_head, file, line);
2836 if (s -> update_queue_tail)
2837 lease_dereference (&s -> update_queue_tail, file, line);
2838 if (s -> ack_queue_head)
2839 lease_dereference (&s -> ack_queue_head, file, line);
2840 if (s -> ack_queue_tail)
2841 lease_dereference (&s -> ack_queue_tail, file, line);
2842 if (s -> send_update_done)
2843 lease_dereference (&s -> send_update_done, file, line);
2844 if (s -> toack_queue_head)
2845 failover_message_dereference (&s -> toack_queue_head,
2846 file, line);
2847 if (s -> toack_queue_tail)
2848 failover_message_dereference (&s -> toack_queue_tail,
2849 file, line);
2850 return ISC_R_SUCCESS;
2853 /* Write all the published values associated with the object through the
2854 specified connection. */
2856 isc_result_t dhcp_failover_state_stuff (omapi_object_t *c,
2857 omapi_object_t *id,
2858 omapi_object_t *h)
2860 dhcp_failover_state_t *s;
2861 omapi_connection_object_t *conn;
2862 isc_result_t status;
2864 if (c -> type != omapi_type_connection)
2865 return ISC_R_INVALIDARG;
2866 conn = (omapi_connection_object_t *)c;
2868 if (h -> type != dhcp_type_failover_state)
2869 return ISC_R_INVALIDARG;
2870 s = (dhcp_failover_state_t *)h;
2872 status = omapi_connection_put_name (c, "name");
2873 if (status != ISC_R_SUCCESS)
2874 return status;
2875 status = omapi_connection_put_string (c, s -> name);
2876 if (status != ISC_R_SUCCESS)
2877 return status;
2879 status = omapi_connection_put_name (c, "partner-address");
2880 if (status != ISC_R_SUCCESS)
2881 return status;
2882 status = omapi_connection_put_uint32 (c, sizeof s -> partner.address);
2883 if (status != ISC_R_SUCCESS)
2884 return status;
2885 status = omapi_connection_copyin (c, (u_int8_t *)&s -> partner.address,
2886 sizeof s -> partner.address);
2887 if (status != ISC_R_SUCCESS)
2888 return status;
2890 status = omapi_connection_put_name (c, "partner-port");
2891 if (status != ISC_R_SUCCESS)
2892 return status;
2893 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2894 if (status != ISC_R_SUCCESS)
2895 return status;
2896 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> partner.port);
2897 if (status != ISC_R_SUCCESS)
2898 return status;
2900 status = omapi_connection_put_name (c, "local-address");
2901 if (status != ISC_R_SUCCESS)
2902 return status;
2903 status = omapi_connection_put_uint32 (c, sizeof s -> me.address);
2904 if (status != ISC_R_SUCCESS)
2905 return status;
2906 status = omapi_connection_copyin (c, (u_int8_t *)&s -> me.address,
2907 sizeof s -> me.address);
2908 if (status != ISC_R_SUCCESS)
2909 return status;
2911 status = omapi_connection_put_name (c, "local-port");
2912 if (status != ISC_R_SUCCESS)
2913 return status;
2914 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2915 if (status != ISC_R_SUCCESS)
2916 return status;
2917 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.port);
2918 if (status != ISC_R_SUCCESS)
2919 return status;
2921 status = omapi_connection_put_name (c, "max-outstanding-updates");
2922 if (status != ISC_R_SUCCESS)
2923 return status;
2924 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2925 if (status != ISC_R_SUCCESS)
2926 return status;
2927 status = omapi_connection_put_uint32 (c,
2928 s -> me.max_flying_updates);
2929 if (status != ISC_R_SUCCESS)
2930 return status;
2932 status = omapi_connection_put_name (c, "mclt");
2933 if (status != ISC_R_SUCCESS)
2934 return status;
2935 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2936 if (status != ISC_R_SUCCESS)
2937 return status;
2938 status = omapi_connection_put_uint32 (c, s -> mclt);
2939 if (status != ISC_R_SUCCESS)
2940 return status;
2942 status = omapi_connection_put_name (c, "load-balance-max-secs");
2943 if (status != ISC_R_SUCCESS)
2944 return status;
2945 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2946 if (status != ISC_R_SUCCESS)
2947 return status;
2948 status = (omapi_connection_put_uint32
2949 (c, (u_int32_t)s -> load_balance_max_secs));
2950 if (status != ISC_R_SUCCESS)
2951 return status;
2954 if (s -> hba) {
2955 status = omapi_connection_put_name (c, "load-balance-hba");
2956 if (status != ISC_R_SUCCESS)
2957 return status;
2958 status = omapi_connection_put_uint32 (c, 32);
2959 if (status != ISC_R_SUCCESS)
2960 return status;
2961 status = omapi_connection_copyin (c, s -> hba, 32);
2962 if (status != ISC_R_SUCCESS)
2963 return status;
2966 status = omapi_connection_put_name (c, "partner-state");
2967 if (status != ISC_R_SUCCESS)
2968 return status;
2969 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2970 if (status != ISC_R_SUCCESS)
2971 return status;
2972 status = omapi_connection_put_uint32 (c, s -> partner.state);
2973 if (status != ISC_R_SUCCESS)
2974 return status;
2976 status = omapi_connection_put_name (c, "local-state");
2977 if (status != ISC_R_SUCCESS)
2978 return status;
2979 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2980 if (status != ISC_R_SUCCESS)
2981 return status;
2982 status = omapi_connection_put_uint32 (c, s -> me.state);
2983 if (status != ISC_R_SUCCESS)
2984 return status;
2986 status = omapi_connection_put_name (c, "partner-stos");
2987 if (status != ISC_R_SUCCESS)
2988 return status;
2989 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2990 if (status != ISC_R_SUCCESS)
2991 return status;
2992 status = omapi_connection_put_uint32 (c,
2993 (u_int32_t)s -> partner.stos);
2994 if (status != ISC_R_SUCCESS)
2995 return status;
2997 status = omapi_connection_put_name (c, "local-stos");
2998 if (status != ISC_R_SUCCESS)
2999 return status;
3000 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3001 if (status != ISC_R_SUCCESS)
3002 return status;
3003 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.stos);
3004 if (status != ISC_R_SUCCESS)
3005 return status;
3007 status = omapi_connection_put_name (c, "hierarchy");
3008 if (status != ISC_R_SUCCESS)
3009 return status;
3010 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3011 if (status != ISC_R_SUCCESS)
3012 return status;
3013 status = omapi_connection_put_uint32 (c, s -> i_am);
3014 if (status != ISC_R_SUCCESS)
3015 return status;
3017 status = omapi_connection_put_name (c, "last-packet-sent");
3018 if (status != ISC_R_SUCCESS)
3019 return status;
3020 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3021 if (status != ISC_R_SUCCESS)
3022 return status;
3023 status = (omapi_connection_put_uint32
3024 (c, (u_int32_t)s -> last_packet_sent));
3025 if (status != ISC_R_SUCCESS)
3026 return status;
3028 status = omapi_connection_put_name (c, "last-timestamp-received");
3029 if (status != ISC_R_SUCCESS)
3030 return status;
3031 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3032 if (status != ISC_R_SUCCESS)
3033 return status;
3034 status = (omapi_connection_put_uint32
3035 (c, (u_int32_t)s -> last_timestamp_received));
3036 if (status != ISC_R_SUCCESS)
3037 return status;
3039 status = omapi_connection_put_name (c, "skew");
3040 if (status != ISC_R_SUCCESS)
3041 return status;
3042 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3043 if (status != ISC_R_SUCCESS)
3044 return status;
3045 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> skew);
3046 if (status != ISC_R_SUCCESS)
3047 return status;
3049 status = omapi_connection_put_name (c, "max-response-delay");
3050 if (status != ISC_R_SUCCESS)
3051 return status;
3052 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3053 if (status != ISC_R_SUCCESS)
3054 return status;
3055 status = (omapi_connection_put_uint32
3056 (c, (u_int32_t)s -> me.max_response_delay));
3057 if (status != ISC_R_SUCCESS)
3058 return status;
3060 status = omapi_connection_put_name (c, "cur-unacked-updates");
3061 if (status != ISC_R_SUCCESS)
3062 return status;
3063 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3064 if (status != ISC_R_SUCCESS)
3065 return status;
3066 status = (omapi_connection_put_uint32
3067 (c, (u_int32_t)s -> cur_unacked_updates));
3068 if (status != ISC_R_SUCCESS)
3069 return status;
3071 if (h -> inner && h -> inner -> type -> stuff_values)
3072 return (*(h -> inner -> type -> stuff_values)) (c, id,
3073 h -> inner);
3074 return ISC_R_SUCCESS;
3077 isc_result_t dhcp_failover_state_lookup (omapi_object_t **sp,
3078 omapi_object_t *id,
3079 omapi_object_t *ref)
3081 omapi_value_t *tv = (omapi_value_t *)0;
3082 isc_result_t status;
3083 dhcp_failover_state_t *s;
3085 if (!ref)
3086 return ISC_R_NOKEYS;
3088 /* First see if we were sent a handle. */
3089 status = omapi_get_value_str (ref, id, "handle", &tv);
3090 if (status == ISC_R_SUCCESS) {
3091 status = omapi_handle_td_lookup (sp, tv -> value);
3093 omapi_value_dereference (&tv, MDL);
3094 if (status != ISC_R_SUCCESS)
3095 return status;
3097 /* Don't return the object if the type is wrong. */
3098 if ((*sp) -> type != dhcp_type_failover_state) {
3099 omapi_object_dereference (sp, MDL);
3100 return ISC_R_INVALIDARG;
3104 /* Look the failover state up by peer name. */
3105 status = omapi_get_value_str (ref, id, "name", &tv);
3106 if (status == ISC_R_SUCCESS) {
3107 for (s = failover_states; s; s = s -> next) {
3108 unsigned l = strlen (s -> name);
3109 if (l == tv -> value -> u.buffer.len &&
3110 !memcmp (s -> name,
3111 tv -> value -> u.buffer.value, l))
3112 break;
3114 omapi_value_dereference (&tv, MDL);
3116 /* If we already have a lease, and it's not the same one,
3117 then the query was invalid. */
3118 if (*sp && *sp != (omapi_object_t *)s) {
3119 omapi_object_dereference (sp, MDL);
3120 return ISC_R_KEYCONFLICT;
3121 } else if (!s) {
3122 if (*sp)
3123 omapi_object_dereference (sp, MDL);
3124 return ISC_R_NOTFOUND;
3125 } else if (!*sp)
3126 /* XXX fix so that hash lookup itself creates
3127 XXX the reference. */
3128 omapi_object_reference (sp, (omapi_object_t *)s, MDL);
3131 /* If we get to here without finding a lease, no valid key was
3132 specified. */
3133 if (!*sp)
3134 return ISC_R_NOKEYS;
3135 return ISC_R_SUCCESS;
3138 isc_result_t dhcp_failover_state_create (omapi_object_t **sp,
3139 omapi_object_t *id)
3141 return ISC_R_NOTIMPLEMENTED;
3144 isc_result_t dhcp_failover_state_remove (omapi_object_t *sp,
3145 omapi_object_t *id)
3147 return ISC_R_NOTIMPLEMENTED;
3150 int dhcp_failover_state_match (dhcp_failover_state_t *state,
3151 u_int8_t *addr, unsigned addrlen)
3153 struct option_cache *oc;
3154 struct data_string ds;
3155 int i;
3157 memset (&ds, 0, sizeof ds);
3158 if (evaluate_option_cache (&ds, (struct packet *)0,
3159 (struct lease *)0,
3160 (struct client_state *)0,
3161 (struct option_state *)0,
3162 (struct option_state *)0,
3163 &global_scope,
3164 state -> partner.address, MDL)) {
3165 for (i = 0; i + addrlen - 1 < ds.len; i += addrlen) {
3166 if (!memcmp (&ds.data [i],
3167 addr, addrlen)) {
3168 data_string_forget (&ds, MDL);
3169 return 1;
3172 data_string_forget (&ds, MDL);
3174 return 0;
3177 const char *dhcp_failover_reject_reason_print (int reason)
3179 switch (reason) {
3180 case FTR_ILLEGAL_IP_ADDR:
3181 return "Illegal IP address (not part of any address pool).";
3183 case FTR_FATAL_CONFLICT:
3184 return "Fatal conflict exists: address in use by other client.";
3186 case FTR_MISSING_BINDINFO:
3187 return "Missing binding information.";
3189 case FTR_TIMEMISMATCH:
3190 return "Connection rejected, time mismatch too great.";
3192 case FTR_INVALID_MCLT:
3193 return "Connection rejected, invalid MCLT.";
3195 case FTR_MISC_REJECT:
3196 return "Connection rejected, unknown reason.";
3198 case FTR_DUP_CONNECTION:
3199 return "Connection rejected, duplicate connection.";
3201 case FTR_INVALID_PARTNER:
3202 return "Connection rejected, invalid failover partner.";
3204 case FTR_TLS_UNSUPPORTED:
3205 return "TLS not supported.";
3207 case FTR_TLS_UNCONFIGURED:
3208 return "TLS supported but not configured.";
3210 case FTR_TLS_REQUIRED:
3211 return "TLS required but not supported by partner.";
3213 case FTR_DIGEST_UNSUPPORTED:
3214 return "Message digest not supported.";
3216 case FTR_DIGEST_UNCONFIGURED:
3217 return "Message digest not configured.";
3219 case FTR_VERSION_MISMATCH:
3220 return "Protocol version mismatch.";
3222 case FTR_MISSING_BIND_INFO:
3223 return "Missing binding information.";
3225 case FTR_OUTDATED_BIND_INFO:
3226 return "Outdated binding information.";
3228 case FTR_LESS_CRIT_BIND_INFO:
3229 return "Less critical binding information.";
3231 case FTR_NO_TRAFFIC:
3232 return "No traffic within sufficient time.";
3234 case FTR_HBA_CONFLICT:
3235 return "Hash bucket assignment conflict.";
3237 default:
3238 case FTR_UNKNOWN:
3239 return "Unknown: Error occurred but does not match any reason code.";
3243 const char *dhcp_failover_state_name_print (enum failover_state state)
3245 switch (state) {
3246 default:
3247 case unknown_state:
3248 return "unknown-state";
3250 case partner_down:
3251 return "partner-down";
3253 case normal:
3254 return "normal";
3256 case communications_interrupted:
3257 return "communications-interrupted";
3259 case resolution_interrupted:
3260 return "resolution-interrupted";
3262 case potential_conflict:
3263 return "potential-conflict";
3265 case recover:
3266 return "recover";
3268 case recover_done:
3269 return "recover-done";
3271 case recover_wait:
3272 return "recover-wait";
3274 case shut_down:
3275 return "shutdown";
3277 case paused:
3278 return "paused";
3280 case startup:
3281 return "startup";
3285 const char *dhcp_failover_message_name (unsigned type)
3287 switch (type) {
3288 case FTM_POOLREQ:
3289 return "pool-request";
3291 case FTM_POOLRESP:
3292 return "pool-response";
3294 case FTM_BNDUPD:
3295 return "bind-update";
3297 case FTM_BNDACK:
3298 return "bind-ack";
3300 case FTM_CONNECT:
3301 return "connect";
3303 case FTM_CONNECTACK:
3304 return "connect-ack";
3306 case FTM_UPDREQ:
3307 return "update-request";
3309 case FTM_UPDDONE:
3310 return "update-done";
3312 case FTM_UPDREQALL:
3313 return "update-request-all";
3315 case FTM_STATE:
3316 return "state";
3318 case FTM_CONTACT:
3319 return "contact";
3321 case FTM_DISCONNECT:
3322 return "disconnect";
3324 default:
3325 return "<unknown message type>";
3329 const char *dhcp_failover_option_name (unsigned type)
3331 switch (type) {
3332 case FTO_BINDING_STATUS:
3333 return "binding-status";
3335 case FTO_ASSIGNED_IP_ADDRESS:
3336 return "assigned-ip-address";
3338 case FTO_SERVER_ADDR:
3339 return "server-addr";
3341 case FTO_ADDRESSES_TRANSFERRED:
3342 return "addresses-transferred";
3344 case FTO_CLIENT_IDENTIFIER:
3345 return "client-identifier";
3347 case FTO_CHADDR:
3348 return "chaddr";
3350 case FTO_DDNS:
3351 return "ddns";
3353 case FTO_REJECT_REASON:
3354 return "reject-reason";
3356 case FTO_MESSAGE:
3357 return "message";
3359 case FTO_MCLT:
3360 return "mclt";
3362 case FTO_VENDOR_CLASS:
3363 return "vendor-class";
3365 case FTO_LEASE_EXPIRY:
3366 return "lease-expiry";
3368 case FTO_POTENTIAL_EXPIRY:
3369 return "potential-expiry";
3371 case FTO_GRACE_EXPIRY:
3372 return "grace-expiry";
3374 case FTO_CLTT:
3375 return "cltt";
3377 case FTO_STOS:
3378 return "stos";
3380 case FTO_SERVER_STATE:
3381 return "server-state";
3383 case FTO_SERVER_FLAGS:
3384 return "server-flags";
3386 case FTO_VENDOR_OPTIONS:
3387 return "vendor-options";
3389 case FTO_MAX_UNACKED:
3390 return "max-unacked";
3392 case FTO_RECEIVE_TIMER:
3393 return "receive-timer";
3395 case FTO_HBA:
3396 return "hba";
3398 case FTO_MESSAGE_DIGEST:
3399 return "message-digest";
3401 case FTO_PROTOCOL_VERSION:
3402 return "protocol-version";
3404 case FTO_TLS_REQUEST:
3405 return "tls-request";
3407 case FTO_TLS_REPLY:
3408 return "tls-reply";
3410 case FTO_REQUEST_OPTIONS:
3411 return "request-options";
3413 case FTO_REPLY_OPTIONS:
3414 return "reply-options";
3416 default:
3417 return "<unknown option>";
3421 failover_option_t *dhcp_failover_option_printf (unsigned code,
3422 char *obuf,
3423 unsigned *obufix,
3424 unsigned obufmax,
3425 const char *fmt, ...)
3427 va_list va;
3428 char tbuf [256];
3430 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3431 * It is unclear what the effects of truncation here are, or
3432 * how that condition should be handled. It seems that this
3433 * function is used for formatting messages in the failover
3434 * command channel. For now the safest thing is for
3435 * overflow-truncation to cause a fatal log.
3437 va_start (va, fmt);
3438 if (vsnprintf (tbuf, sizeof tbuf, fmt, va) >= sizeof tbuf)
3439 log_fatal ("%s: vsnprintf would truncate",
3440 "dhcp_failover_make_option");
3441 va_end (va);
3443 return dhcp_failover_make_option (code, obuf, obufix, obufmax,
3444 strlen (tbuf), tbuf);
3447 failover_option_t *dhcp_failover_make_option (unsigned code,
3448 char *obuf, unsigned *obufix,
3449 unsigned obufmax, ...)
3451 va_list va;
3452 struct failover_option_info *info;
3453 int i;
3454 unsigned size, count;
3455 unsigned val;
3456 u_int8_t *iaddr;
3457 unsigned ilen = 0;
3458 u_int8_t *bval;
3459 char *txt = NULL;
3460 #if defined (DEBUG_FAILOVER_MESSAGES)
3461 char tbuf [256];
3462 #endif
3464 /* Note that the failover_option structure is used differently on
3465 input than on output - on input, count is an element count, and
3466 on output it's the number of bytes total in the option, including
3467 the option code and option length. */
3468 failover_option_t option, *op;
3471 /* Bogus option code? */
3472 if (code < 1 || code > FTO_MAX || ft_options [code].type == FT_UNDEF) {
3473 return &null_failover_option;
3475 info = &ft_options [code];
3477 va_start (va, obufmax);
3479 /* Get the number of elements and the size of the buffer we need
3480 to allocate. */
3481 if (info -> type == FT_DDNS || info -> type == FT_DDNS1) {
3482 count = info -> type == FT_DDNS ? 1 : 2;
3483 size = va_arg (va, int) + count;
3484 } else {
3485 /* Find out how many items in this list. */
3486 if (info -> num_present)
3487 count = info -> num_present;
3488 else
3489 count = va_arg (va, int);
3491 /* Figure out size. */
3492 switch (info -> type) {
3493 case FT_UINT8:
3494 case FT_BYTES:
3495 case FT_DIGEST:
3496 size = count;
3497 break;
3499 case FT_TEXT_OR_BYTES:
3500 case FT_TEXT:
3501 txt = va_arg (va, char *);
3502 size = count;
3503 break;
3505 case FT_IPADDR:
3506 ilen = va_arg (va, unsigned);
3507 size = count * ilen;
3508 break;
3510 case FT_UINT32:
3511 size = count * 4;
3512 break;
3514 case FT_UINT16:
3515 size = count * 2;
3516 break;
3518 default:
3519 /* shouldn't get here. */
3520 log_fatal ("bogus type in failover_make_option: %d",
3521 info -> type);
3522 return &null_failover_option;
3526 size += 4;
3528 /* Allocate a buffer for the option. */
3529 option.count = size;
3530 option.data = dmalloc (option.count, MDL);
3531 if (!option.data) {
3532 va_end (va);
3533 return &null_failover_option;
3536 /* Put in the option code and option length. */
3537 putUShort (option.data, code);
3538 putUShort (&option.data [2], size - 4);
3540 #if defined (DEBUG_FAILOVER_MESSAGES)
3541 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3542 * It is unclear what the effects of truncation here are, or
3543 * how that condition should be handled. It seems that this
3544 * message may be sent over the failover command channel.
3545 * For now the safest thing is for overflow-truncation to cause
3546 * a fatal log.
3548 if (snprintf (tbuf, sizeof tbuf, " (%s<%d>", info -> name,
3549 option.count) >= sizeof tbuf)
3550 log_fatal ("dhcp_failover_make_option: tbuf overflow");
3551 failover_print (obuf, obufix, obufmax, tbuf);
3552 #endif
3554 /* Now put in the data. */
3555 switch (info -> type) {
3556 case FT_UINT8:
3557 for (i = 0; i < count; i++) {
3558 val = va_arg (va, unsigned);
3559 #if defined (DEBUG_FAILOVER_MESSAGES)
3560 /* %Audit% Cannot exceed 24 bytes. %2004.06.17,Safe% */
3561 sprintf (tbuf, " %d", val);
3562 failover_print (obuf, obufix, obufmax, tbuf);
3563 #endif
3564 option.data [i + 4] = val;
3566 break;
3568 case FT_IPADDR:
3569 for (i = 0; i < count; i++) {
3570 iaddr = va_arg (va, u_int8_t *);
3571 if (ilen != 4) {
3572 dfree (option.data, MDL);
3573 log_error ("IP addrlen=%d, should be 4.",
3574 ilen);
3575 va_end (va);
3576 return &null_failover_option;
3579 #if defined (DEBUG_FAILOVER_MESSAGES)
3580 /*%Audit% Cannot exceed 17 bytes. %2004.06.17,Safe%*/
3581 sprintf (tbuf, " %u.%u.%u.%u",
3582 iaddr [0], iaddr [1], iaddr [2], iaddr [3]);
3583 failover_print (obuf, obufix, obufmax, tbuf);
3584 #endif
3585 memcpy (&option.data [4 + i * ilen], iaddr, ilen);
3587 break;
3589 case FT_UINT32:
3590 for (i = 0; i < count; i++) {
3591 val = va_arg (va, unsigned);
3592 #if defined (DEBUG_FAILOVER_MESSAGES)
3593 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3594 sprintf (tbuf, " %d", val);
3595 failover_print (obuf, obufix, obufmax, tbuf);
3596 #endif
3597 putULong (&option.data [4 + i * 4], val);
3599 break;
3601 case FT_BYTES:
3602 case FT_DIGEST:
3603 bval = va_arg (va, u_int8_t *);
3604 #if defined (DEBUG_FAILOVER_MESSAGES)
3605 for (i = 0; i < count; i++) {
3606 /* 23 bytes plus nul, safe. */
3607 sprintf (tbuf, " %d", bval [i]);
3608 failover_print (obuf, obufix, obufmax, tbuf);
3610 #endif
3611 memcpy (&option.data [4], bval, count);
3612 break;
3614 /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
3615 terminated. Note that the caller should be careful not
3616 to provide a format and data that amount to more than 256
3617 bytes of data, since it will cause a fatal error. */
3618 case FT_TEXT_OR_BYTES:
3619 case FT_TEXT:
3620 #if defined (DEBUG_FAILOVER_MESSAGES)
3621 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3622 * It is unclear what the effects of truncation here are, or
3623 * how that condition should be handled. It seems that this
3624 * function is used for formatting messages in the failover
3625 * command channel. For now the safest thing is for
3626 * overflow-truncation to cause a fatal log.
3628 if (snprintf (tbuf, sizeof tbuf, "\"%s\"", txt) >= sizeof tbuf)
3629 log_fatal ("dhcp_failover_make_option: tbuf overflow");
3630 failover_print (obuf, obufix, obufmax, tbuf);
3631 #endif
3632 memcpy (&option.data [4], txt, count);
3633 break;
3635 case FT_DDNS:
3636 case FT_DDNS1:
3637 option.data [4] = va_arg (va, unsigned);
3638 if (count == 2)
3639 option.data [5] = va_arg (va, unsigned);
3640 bval = va_arg (va, u_int8_t *);
3641 memcpy (&option.data [4 + count], bval, size - count - 4);
3642 #if defined (DEBUG_FAILOVER_MESSAGES)
3643 for (i = 4; i < size; i++) {
3644 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3645 sprintf (tbuf, " %d", option.data [i]);
3646 failover_print (obuf, obufix, obufmax, tbuf);
3648 #endif
3649 break;
3651 case FT_UINT16:
3652 for (i = 0; i < count; i++) {
3653 val = va_arg (va, u_int32_t);
3654 #if defined (DEBUG_FAILOVER_MESSAGES)
3655 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3656 sprintf (tbuf, " %d", val);
3657 failover_print (obuf, obufix, obufmax, tbuf);
3658 #endif
3659 putUShort (&option.data [4 + i * 2], val);
3661 break;
3663 case FT_UNDEF:
3664 default:
3665 break;
3668 #if defined DEBUG_FAILOVER_MESSAGES
3669 failover_print (obuf, obufix, obufmax, ")");
3670 #endif
3671 va_end (va);
3673 /* Now allocate a place to store what we just set up. */
3674 op = dmalloc (sizeof (failover_option_t), MDL);
3675 if (!op) {
3676 dfree (option.data, MDL);
3677 return &null_failover_option;
3680 *op = option;
3681 return op;
3684 /* Send a failover message header. */
3686 isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link,
3687 omapi_object_t *connection,
3688 int msg_type, ...)
3690 unsigned count = 0;
3691 unsigned size = 0;
3692 int bad_option = 0;
3693 int opix = 0;
3694 va_list list;
3695 failover_option_t *option;
3696 unsigned char *opbuf;
3697 isc_result_t status = ISC_R_SUCCESS;
3698 unsigned char cbuf;
3700 /* Run through the argument list once to compute the length of
3701 the option portion of the message. */
3702 va_start (list, msg_type);
3703 while ((option = va_arg (list, failover_option_t *))) {
3704 if (option != &skip_failover_option)
3705 size += option -> count;
3706 if (option == &null_failover_option)
3707 bad_option = 1;
3709 va_end (list);
3711 /* Allocate an option buffer, unless we got an error. */
3712 if (!bad_option && size) {
3713 opbuf = dmalloc (size, MDL);
3714 if (!opbuf)
3715 status = ISC_R_NOMEMORY;
3716 } else
3717 opbuf = (unsigned char *)0;
3719 va_start (list, msg_type);
3720 while ((option = va_arg (list, failover_option_t *))) {
3721 if (option == &skip_failover_option)
3722 continue;
3723 if (!bad_option && opbuf)
3724 memcpy (&opbuf [opix],
3725 option -> data, option -> count);
3726 if (option != &null_failover_option &&
3727 option != &skip_failover_option) {
3728 opix += option -> count;
3729 dfree (option -> data, MDL);
3730 dfree (option, MDL);
3734 if (bad_option)
3735 return ISC_R_INVALIDARG;
3737 /* Now send the message header. */
3739 /* Message length. */
3740 status = omapi_connection_put_uint16 (connection, size + 12);
3741 if (status != ISC_R_SUCCESS)
3742 goto err;
3744 /* Message type. */
3745 cbuf = msg_type;
3746 status = omapi_connection_copyin (connection, &cbuf, 1);
3747 if (status != ISC_R_SUCCESS)
3748 goto err;
3750 /* Payload offset. */
3751 cbuf = 12;
3752 status = omapi_connection_copyin (connection, &cbuf, 1);
3753 if (status != ISC_R_SUCCESS)
3754 goto err;
3756 /* Current time. */
3757 status = omapi_connection_put_uint32 (connection, (u_int32_t)cur_time);
3758 if (status != ISC_R_SUCCESS)
3759 goto err;
3761 /* Transaction ID. */
3762 status = omapi_connection_put_uint32 (connection, link -> xid++);
3763 if (status != ISC_R_SUCCESS)
3764 goto err;
3767 /* Payload. */
3768 if (opbuf) {
3769 status = omapi_connection_copyin (connection, opbuf, size);
3770 if (status != ISC_R_SUCCESS)
3771 goto err;
3772 dfree (opbuf, MDL);
3774 if (link -> state_object &&
3775 link -> state_object -> link_to_peer == link) {
3776 #if defined (DEBUG_FAILOVER_TIMING)
3777 log_info ("add_timeout +%d %s",
3778 (int)(link -> state_object ->
3779 partner.max_response_delay) / 3,
3780 "dhcp_failover_send_contact");
3781 #endif
3782 add_timeout (cur_time +
3783 (int)(link -> state_object ->
3784 partner.max_response_delay) / 3,
3785 dhcp_failover_send_contact, link -> state_object,
3786 (tvref_t)dhcp_failover_state_reference,
3787 (tvunref_t)dhcp_failover_state_dereference);
3789 return status;
3791 err:
3792 if (opbuf)
3793 dfree (opbuf, MDL);
3794 log_info ("dhcp_failover_put_message: something went wrong.");
3795 omapi_disconnect (connection, 1);
3796 return status;
3799 void dhcp_failover_timeout (void *vstate)
3801 dhcp_failover_state_t *state = vstate;
3802 dhcp_failover_link_t *link;
3803 isc_result_t status;
3805 #if defined (DEBUG_FAILOVER_TIMING)
3806 log_info ("dhcp_failover_timeout");
3807 #endif
3809 if (!state || state -> type != dhcp_type_failover_state)
3810 return;
3811 link = state -> link_to_peer;
3812 if (!link ||
3813 !link -> outer ||
3814 link -> outer -> type != omapi_type_connection)
3815 return;
3817 log_error ("timeout waiting for failover peer %s", state -> name);
3819 /* If we haven't gotten a timely response, blow away the connection.
3820 This will cause the state to change automatically. */
3821 omapi_disconnect (link -> outer, 1);
3824 void dhcp_failover_send_contact (void *vstate)
3826 dhcp_failover_state_t *state = vstate;
3827 dhcp_failover_link_t *link;
3828 isc_result_t status;
3830 #if defined (DEBUG_FAILOVER_MESSAGES)
3831 char obuf [64];
3832 unsigned obufix = 0;
3834 # define FMA obuf, &obufix, sizeof obuf
3835 failover_print (FMA, "(contact");
3836 #else
3837 # define FMA (char *)0, (unsigned *)0, 0
3838 #endif
3840 #if defined (DEBUG_FAILOVER_TIMING)
3841 log_info ("dhcp_failover_send_contact");
3842 #endif
3844 if (!state || state -> type != dhcp_type_failover_state)
3845 return;
3846 link = state -> link_to_peer;
3847 if (!link ||
3848 !link -> outer ||
3849 link -> outer -> type != omapi_type_connection)
3850 return;
3852 status = (dhcp_failover_put_message
3853 (link, link -> outer,
3854 FTM_CONTACT,
3855 (failover_option_t *)0));
3857 #if defined (DEBUG_FAILOVER_MESSAGES)
3858 if (status != ISC_R_SUCCESS)
3859 failover_print (FMA, " (failed)");
3860 failover_print (FMA, ")");
3861 if (obufix) {
3862 log_debug ("%s", obuf);
3864 #endif
3865 return;
3868 isc_result_t dhcp_failover_send_state (dhcp_failover_state_t *state)
3870 dhcp_failover_link_t *link;
3871 isc_result_t status;
3873 #if defined (DEBUG_FAILOVER_MESSAGES)
3874 char obuf [64];
3875 unsigned obufix = 0;
3877 # define FMA obuf, &obufix, sizeof obuf
3878 failover_print (FMA, "(state");
3879 #else
3880 # define FMA (char *)0, (unsigned *)0, 0
3881 #endif
3883 if (!state || state -> type != dhcp_type_failover_state)
3884 return ISC_R_INVALIDARG;
3885 link = state -> link_to_peer;
3886 if (!link ||
3887 !link -> outer ||
3888 link -> outer -> type != omapi_type_connection)
3889 return ISC_R_INVALIDARG;
3891 status = (dhcp_failover_put_message
3892 (link, link -> outer,
3893 FTM_STATE,
3894 dhcp_failover_make_option (FTO_SERVER_STATE, FMA,
3895 (state -> me.state == startup
3896 ? state -> saved_state
3897 : state -> me.state)),
3898 dhcp_failover_make_option
3899 (FTO_SERVER_FLAGS, FMA,
3900 (state -> service_state == service_startup
3901 ? FTF_STARTUP : 0)),
3902 dhcp_failover_make_option (FTO_STOS, FMA, state -> me.stos),
3903 (failover_option_t *)0));
3905 #if defined (DEBUG_FAILOVER_MESSAGES)
3906 if (status != ISC_R_SUCCESS)
3907 failover_print (FMA, " (failed)");
3908 failover_print (FMA, ")");
3909 if (obufix) {
3910 log_debug ("%s", obuf);
3912 #endif
3913 return ISC_R_SUCCESS;
3916 /* Send a connect message. */
3918 isc_result_t dhcp_failover_send_connect (omapi_object_t *l)
3920 dhcp_failover_link_t *link;
3921 dhcp_failover_state_t *state;
3922 isc_result_t status;
3923 char hba [32];
3924 #if defined (DEBUG_FAILOVER_MESSAGES)
3925 char obuf [64];
3926 unsigned obufix = 0;
3928 # define FMA obuf, &obufix, sizeof obuf
3929 failover_print (FMA, "(connect");
3930 #else
3931 # define FMA (char *)0, (unsigned *)0, 0
3932 #endif
3934 if (!l || l -> type != dhcp_type_failover_link)
3935 return ISC_R_INVALIDARG;
3936 link = (dhcp_failover_link_t *)l;
3937 state = link -> state_object;
3938 if (!l -> outer || l -> outer -> type != omapi_type_connection)
3939 return ISC_R_INVALIDARG;
3941 status =
3942 (dhcp_failover_put_message
3943 (link, l -> outer,
3944 FTM_CONNECT,
3945 dhcp_failover_make_option (FTO_SERVER_ADDR, FMA,
3946 state -> server_identifier.len,
3947 state -> server_identifier.data),
3948 dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
3949 state -> me.max_flying_updates),
3950 dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
3951 state -> me.max_response_delay),
3952 dhcp_failover_option_printf (FTO_VENDOR_CLASS, FMA,
3953 "isc-%s", DHCP_VERSION),
3954 dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
3955 DHCP_FAILOVER_VERSION),
3956 dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
3957 0, 0),
3958 dhcp_failover_make_option (FTO_MCLT, FMA,
3959 state -> mclt),
3960 (state -> hba
3961 ? dhcp_failover_make_option (FTO_HBA, FMA, 32, state -> hba)
3962 : &skip_failover_option),
3963 (failover_option_t *)0));
3965 #if defined (DEBUG_FAILOVER_MESSAGES)
3966 if (status != ISC_R_SUCCESS)
3967 failover_print (FMA, " (failed)");
3968 failover_print (FMA, ")");
3969 if (obufix) {
3970 log_debug ("%s", obuf);
3972 #endif
3973 return status;
3976 isc_result_t dhcp_failover_send_connectack (omapi_object_t *l,
3977 dhcp_failover_state_t *state,
3978 int reason, const char *errmsg)
3980 dhcp_failover_link_t *link;
3981 isc_result_t status;
3982 #if defined (DEBUG_FAILOVER_MESSAGES)
3983 char obuf [64];
3984 unsigned obufix = 0;
3986 # define FMA obuf, &obufix, sizeof obuf
3987 failover_print (FMA, "(connectack");
3988 #else
3989 # define FMA (char *)0, (unsigned *)0, 0
3990 #endif
3992 if (!l || l -> type != dhcp_type_failover_link)
3993 return ISC_R_INVALIDARG;
3994 link = (dhcp_failover_link_t *)l;
3995 if (!l -> outer || l -> outer -> type != omapi_type_connection)
3996 return ISC_R_INVALIDARG;
3998 status =
3999 (dhcp_failover_put_message
4000 (link, l -> outer,
4001 FTM_CONNECTACK,
4002 (state
4003 ? (dhcp_failover_make_option
4004 (FTO_SERVER_ADDR, FMA,
4005 state -> server_identifier.len,
4006 state -> server_identifier.data))
4007 : &skip_failover_option),
4008 (state
4009 ? dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
4010 state -> me.max_flying_updates)
4011 : &skip_failover_option),
4012 (state
4013 ? dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
4014 state -> me.max_response_delay)
4015 : &skip_failover_option),
4016 dhcp_failover_option_printf (FTO_VENDOR_CLASS, FMA,
4017 "isc-%s", DHCP_VERSION),
4018 dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
4019 DHCP_FAILOVER_VERSION),
4020 dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
4021 0, 0),
4022 (reason
4023 ? dhcp_failover_make_option (FTO_REJECT_REASON,
4024 FMA, reason)
4025 : &skip_failover_option),
4026 (errmsg
4027 ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4028 strlen (errmsg), errmsg)
4029 : &skip_failover_option),
4030 (failover_option_t *)0));
4032 #if defined (DEBUG_FAILOVER_MESSAGES)
4033 if (status != ISC_R_SUCCESS)
4034 failover_print (FMA, " (failed)");
4035 failover_print (FMA, ")");
4036 if (obufix) {
4037 log_debug ("%s", obuf);
4039 #endif
4040 return status;
4043 isc_result_t dhcp_failover_send_disconnect (omapi_object_t *l,
4044 int reason,
4045 const char *message)
4047 dhcp_failover_link_t *link;
4048 dhcp_failover_state_t *state;
4049 isc_result_t status;
4050 #if defined (DEBUG_FAILOVER_MESSAGES)
4051 char obuf [64];
4052 unsigned obufix = 0;
4054 # define FMA obuf, &obufix, sizeof obuf
4055 failover_print (FMA, "(disconnect");
4056 #else
4057 # define FMA (char *)0, (unsigned *)0, 0
4058 #endif
4060 if (!l || l -> type != dhcp_type_failover_link)
4061 return ISC_R_INVALIDARG;
4062 link = (dhcp_failover_link_t *)l;
4063 state = link -> state_object;
4064 if (!l -> outer || l -> outer -> type != omapi_type_connection)
4065 return ISC_R_INVALIDARG;
4067 if (!message && reason)
4068 message = dhcp_failover_reject_reason_print (reason);
4070 status = (dhcp_failover_put_message
4071 (link, l -> outer,
4072 FTM_DISCONNECT,
4073 dhcp_failover_make_option (FTO_REJECT_REASON,
4074 FMA, reason),
4075 (message
4076 ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4077 strlen (message), message)
4078 : &skip_failover_option),
4079 (failover_option_t *)0));
4081 #if defined (DEBUG_FAILOVER_MESSAGES)
4082 if (status != ISC_R_SUCCESS)
4083 failover_print (FMA, " (failed)");
4084 failover_print (FMA, ")");
4085 if (obufix) {
4086 log_debug ("%s", obuf);
4088 #endif
4089 return status;
4092 /* Send a Bind Update message. */
4094 isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state,
4095 struct lease *lease)
4097 dhcp_failover_link_t *link;
4098 isc_result_t status;
4099 #if defined (DEBUG_FAILOVER_MESSAGES)
4100 char obuf [64];
4101 unsigned obufix = 0;
4103 # define FMA obuf, &obufix, sizeof obuf
4104 failover_print (FMA, "(bndupd");
4105 #else
4106 # define FMA (char *)0, (unsigned *)0, 0
4107 #endif
4109 if (!state -> link_to_peer ||
4110 state -> link_to_peer -> type != dhcp_type_failover_link)
4111 return ISC_R_INVALIDARG;
4112 link = (dhcp_failover_link_t *)state -> link_to_peer;
4114 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4115 return ISC_R_INVALIDARG;
4117 /* Send the update. */
4118 status = (dhcp_failover_put_message
4119 (link, link -> outer,
4120 FTM_BNDUPD,
4121 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
4122 lease -> ip_addr.len,
4123 lease -> ip_addr.iabuf),
4124 dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
4125 lease -> desired_binding_state),
4126 lease -> uid_len
4127 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
4128 lease -> uid_len,
4129 lease -> uid)
4130 : &skip_failover_option,
4131 lease -> hardware_addr.hlen
4132 ? dhcp_failover_make_option (FTO_CHADDR, FMA,
4133 lease -> hardware_addr.hlen,
4134 lease -> hardware_addr.hbuf)
4135 : &skip_failover_option,
4136 dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
4137 lease -> ends),
4138 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
4139 lease -> tstp),
4140 dhcp_failover_make_option (FTO_STOS, FMA,
4141 lease -> starts),
4142 dhcp_failover_make_option (FTO_CLTT, FMA,
4143 lease -> cltt),
4144 &skip_failover_option, /* XXX DDNS */
4145 &skip_failover_option, /* XXX request options */
4146 &skip_failover_option, /* XXX reply options */
4147 (failover_option_t *)0));
4149 #if defined (DEBUG_FAILOVER_MESSAGES)
4150 if (status != ISC_R_SUCCESS)
4151 failover_print (FMA, " (failed)");
4152 failover_print (FMA, ")");
4153 if (obufix) {
4154 log_debug ("%s", obuf);
4156 #endif
4157 return status;
4160 /* Send a Bind ACK message. */
4162 isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *state,
4163 failover_message_t *msg,
4164 int reason, const char *message)
4166 dhcp_failover_link_t *link;
4167 isc_result_t status;
4168 #if defined (DEBUG_FAILOVER_MESSAGES)
4169 char obuf [64];
4170 unsigned obufix = 0;
4172 # define FMA obuf, &obufix, sizeof obuf
4173 failover_print (FMA, "(bndack");
4174 #else
4175 # define FMA (char *)0, (unsigned *)0, 0
4176 #endif
4178 if (!state -> link_to_peer ||
4179 state -> link_to_peer -> type != dhcp_type_failover_link)
4180 return ISC_R_INVALIDARG;
4181 link = (dhcp_failover_link_t *)state -> link_to_peer;
4183 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4184 return ISC_R_INVALIDARG;
4186 if (!message && reason)
4187 message = dhcp_failover_reject_reason_print (reason);
4189 /* Send the update. */
4190 status = (dhcp_failover_put_message
4191 (link, link -> outer,
4192 FTM_BNDACK,
4193 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
4194 sizeof msg -> assigned_addr,
4195 &msg -> assigned_addr),
4196 dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
4197 msg -> binding_status),
4198 (msg -> options_present & FTB_CLIENT_IDENTIFIER)
4199 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
4200 msg -> client_identifier.count,
4201 msg -> client_identifier.data)
4202 : &skip_failover_option,
4203 (msg -> options_present & FTB_CHADDR)
4204 ? dhcp_failover_make_option (FTO_CHADDR, FMA,
4205 msg -> chaddr.count,
4206 msg -> chaddr.data)
4207 : &skip_failover_option,
4208 dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
4209 msg -> expiry),
4210 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
4211 msg -> potential_expiry),
4212 dhcp_failover_make_option (FTO_STOS, FMA,
4213 msg -> stos),
4214 dhcp_failover_make_option (FTO_CLTT, FMA,
4215 msg -> client_ltt),
4216 reason
4217 ? dhcp_failover_make_option (FTO_REJECT_REASON,
4218 FMA, reason)
4219 : &skip_failover_option,
4220 (message
4221 ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4222 strlen (message), message)
4223 : &skip_failover_option),
4224 &skip_failover_option, /* XXX DDNS */
4225 &skip_failover_option, /* XXX request options */
4226 &skip_failover_option, /* XXX reply options */
4227 (failover_option_t *)0));
4229 #if defined (DEBUG_FAILOVER_MESSAGES)
4230 if (status != ISC_R_SUCCESS)
4231 failover_print (FMA, " (failed)");
4232 failover_print (FMA, ")");
4233 if (obufix) {
4234 log_debug ("%s", obuf);
4236 #endif
4237 return status;
4240 isc_result_t dhcp_failover_send_poolreq (dhcp_failover_state_t *state)
4242 dhcp_failover_link_t *link;
4243 isc_result_t status;
4244 #if defined (DEBUG_FAILOVER_MESSAGES)
4245 char obuf [64];
4246 unsigned obufix = 0;
4248 # define FMA obuf, &obufix, sizeof obuf
4249 failover_print (FMA, "(poolreq");
4250 #else
4251 # define FMA (char *)0, (unsigned *)0, 0
4252 #endif
4254 if (!state -> link_to_peer ||
4255 state -> link_to_peer -> type != dhcp_type_failover_link)
4256 return ISC_R_INVALIDARG;
4257 link = (dhcp_failover_link_t *)state -> link_to_peer;
4259 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4260 return ISC_R_INVALIDARG;
4262 status = (dhcp_failover_put_message
4263 (link, link -> outer,
4264 FTM_POOLREQ,
4265 (failover_option_t *)0));
4267 #if defined (DEBUG_FAILOVER_MESSAGES)
4268 if (status != ISC_R_SUCCESS)
4269 failover_print (FMA, " (failed)");
4270 failover_print (FMA, ")");
4271 if (obufix) {
4272 log_debug ("%s", obuf);
4274 #endif
4275 return status;
4278 isc_result_t dhcp_failover_send_poolresp (dhcp_failover_state_t *state,
4279 int leases)
4281 dhcp_failover_link_t *link;
4282 isc_result_t status;
4283 #if defined (DEBUG_FAILOVER_MESSAGES)
4284 char obuf [64];
4285 unsigned obufix = 0;
4287 # define FMA obuf, &obufix, sizeof obuf
4288 failover_print (FMA, "(poolresp");
4289 #else
4290 # define FMA (char *)0, (unsigned *)0, 0
4291 #endif
4293 if (!state -> link_to_peer ||
4294 state -> link_to_peer -> type != dhcp_type_failover_link)
4295 return ISC_R_INVALIDARG;
4296 link = (dhcp_failover_link_t *)state -> link_to_peer;
4298 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4299 return ISC_R_INVALIDARG;
4301 status = (dhcp_failover_put_message
4302 (link, link -> outer,
4303 FTM_POOLRESP,
4304 dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED, FMA,
4305 leases),
4306 (failover_option_t *)0));
4308 #if defined (DEBUG_FAILOVER_MESSAGES)
4309 if (status != ISC_R_SUCCESS)
4310 failover_print (FMA, " (failed)");
4311 failover_print (FMA, ")");
4312 if (obufix) {
4313 log_debug ("%s", obuf);
4315 #endif
4316 return status;
4319 isc_result_t dhcp_failover_send_update_request (dhcp_failover_state_t *state)
4321 dhcp_failover_link_t *link;
4322 isc_result_t status;
4323 #if defined (DEBUG_FAILOVER_MESSAGES)
4324 char obuf [64];
4325 unsigned obufix = 0;
4327 # define FMA obuf, &obufix, sizeof obuf
4328 failover_print (FMA, "(updreq");
4329 #else
4330 # define FMA (char *)0, (unsigned *)0, 0
4331 #endif
4333 if (!state -> link_to_peer ||
4334 state -> link_to_peer -> type != dhcp_type_failover_link)
4335 return ISC_R_INVALIDARG;
4336 link = (dhcp_failover_link_t *)state -> link_to_peer;
4338 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4339 return ISC_R_INVALIDARG;
4341 status = (dhcp_failover_put_message
4342 (link, link -> outer,
4343 FTM_UPDREQ,
4344 (failover_option_t *)0));
4346 #if defined (DEBUG_FAILOVER_MESSAGES)
4347 if (status != ISC_R_SUCCESS)
4348 failover_print (FMA, " (failed)");
4349 failover_print (FMA, ")");
4350 if (obufix) {
4351 log_debug ("%s", obuf);
4353 #endif
4354 log_info ("Sent update request message to %s", state -> name);
4355 return status;
4358 isc_result_t dhcp_failover_send_update_request_all (dhcp_failover_state_t
4359 *state)
4361 dhcp_failover_link_t *link;
4362 isc_result_t status;
4363 #if defined (DEBUG_FAILOVER_MESSAGES)
4364 char obuf [64];
4365 unsigned obufix = 0;
4367 # define FMA obuf, &obufix, sizeof obuf
4368 failover_print (FMA, "(updreqall");
4369 #else
4370 # define FMA (char *)0, (unsigned *)0, 0
4371 #endif
4373 if (!state -> link_to_peer ||
4374 state -> link_to_peer -> type != dhcp_type_failover_link)
4375 return ISC_R_INVALIDARG;
4376 link = (dhcp_failover_link_t *)state -> link_to_peer;
4378 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4379 return ISC_R_INVALIDARG;
4381 status = (dhcp_failover_put_message
4382 (link, link -> outer,
4383 FTM_UPDREQALL,
4384 (failover_option_t *)0));
4386 #if defined (DEBUG_FAILOVER_MESSAGES)
4387 if (status != ISC_R_SUCCESS)
4388 failover_print (FMA, " (failed)");
4389 failover_print (FMA, ")");
4390 if (obufix) {
4391 log_debug ("%s", obuf);
4393 #endif
4394 log_info ("Sent update request all message to %s", state -> name);
4395 return status;
4398 isc_result_t dhcp_failover_send_update_done (dhcp_failover_state_t *state)
4400 dhcp_failover_link_t *link;
4401 isc_result_t status;
4402 #if defined (DEBUG_FAILOVER_MESSAGES)
4403 char obuf [64];
4404 unsigned obufix = 0;
4406 # define FMA obuf, &obufix, sizeof obuf
4407 failover_print (FMA, "(upddone");
4408 #else
4409 # define FMA (char *)0, (unsigned *)0, 0
4410 #endif
4412 if (!state -> link_to_peer ||
4413 state -> link_to_peer -> type != dhcp_type_failover_link)
4414 return ISC_R_INVALIDARG;
4415 link = (dhcp_failover_link_t *)state -> link_to_peer;
4417 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4418 return ISC_R_INVALIDARG;
4420 status = (dhcp_failover_put_message
4421 (link, link -> outer,
4422 FTM_UPDDONE,
4423 (failover_option_t *)0));
4425 #if defined (DEBUG_FAILOVER_MESSAGES)
4426 if (status != ISC_R_SUCCESS)
4427 failover_print (FMA, " (failed)");
4428 failover_print (FMA, ")");
4429 if (obufix) {
4430 log_debug ("%s", obuf);
4432 #endif
4434 log_info ("Sent update done message to %s", state -> name);
4436 /* There may be uncommitted leases at this point (since
4437 dhcp_failover_process_bind_ack() doesn't commit leases);
4438 commit the lease file. */
4439 commit_leases();
4441 return status;
4444 isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *state,
4445 failover_message_t *msg)
4447 struct lease *lt, *lease;
4448 struct iaddr ia;
4449 int reason = FTR_MISC_REJECT;
4450 const char *message;
4451 int new_binding_state;
4453 ia.len = sizeof msg -> assigned_addr;
4454 memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
4456 lease = (struct lease *)0;
4457 lt = (struct lease *)0;
4458 if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
4459 message = "unknown IP address";
4460 reason = FTR_ILLEGAL_IP_ADDR;
4461 goto bad;
4464 /* XXX check for conflicts. */
4466 /* Install the new info. */
4467 if (!lease_copy (&lt, lease, MDL)) {
4468 message = "no memory";
4469 goto bad;
4472 if (msg -> options_present & FTB_CHADDR) {
4473 if (msg -> chaddr.count > sizeof lt -> hardware_addr.hbuf) {
4474 message = "chaddr to long";
4475 goto bad;
4477 lt -> hardware_addr.hlen = msg -> chaddr.count;
4478 memcpy (lt -> hardware_addr.hbuf, msg -> chaddr.data,
4479 msg -> chaddr.count);
4482 if (msg -> options_present & FTB_CLIENT_IDENTIFIER) {
4483 lt -> uid_len = msg -> client_identifier.count;
4484 if (lt -> uid_len > sizeof lt -> uid_buf) {
4485 lt -> uid_max = lt -> uid_len;
4486 lt -> uid = dmalloc (lt -> uid_len, MDL);
4487 if (!lt -> uid) {
4488 message = "no memory";
4489 goto bad;
4491 } else {
4492 lt -> uid_max = sizeof lt -> uid_buf;
4493 lt -> uid = lt -> uid_buf;
4495 memcpy (lt -> uid,
4496 msg -> client_identifier.data, lt -> uid_len);
4499 /* XXX Times may need to be adjusted based on clock skew! */
4500 if (msg -> options_present & FTB_STOS) {
4501 lt -> starts = msg -> stos;
4503 if (msg -> options_present & FTB_LEASE_EXPIRY) {
4504 lt -> ends = msg -> expiry;
4506 if (msg -> options_present & FTB_CLTT) {
4507 lt -> cltt = msg -> client_ltt;
4509 if (msg -> options_present & FTB_POTENTIAL_EXPIRY) {
4510 lt -> tsfp = msg -> potential_expiry;
4513 if (msg -> options_present & FTB_BINDING_STATUS) {
4514 #if defined (DEBUG_LEASE_STATE_TRANSITIONS)
4515 log_info ("processing state transition for %s: %s to %s",
4516 piaddr (lease -> ip_addr),
4517 binding_state_print (lease -> binding_state),
4518 binding_state_print (msg -> binding_status));
4519 #endif
4521 /* If we're in normal state, make sure the state transition
4522 we got is valid. */
4523 if (state -> me.state == normal) {
4524 new_binding_state =
4525 (normal_binding_state_transition_check
4526 (lease, state, msg -> binding_status,
4527 msg -> potential_expiry));
4528 /* XXX if the transition the peer asked for isn't
4529 XXX allowed, maybe we should make the transition
4530 XXX into potential-conflict at this point. */
4531 } else {
4532 new_binding_state =
4533 (conflict_binding_state_transition_check
4534 (lease, state, msg -> binding_status,
4535 msg -> potential_expiry));
4537 if (new_binding_state != msg -> binding_status) {
4538 char outbuf [100];
4540 if (snprintf (outbuf, sizeof outbuf,
4541 "%s: invalid state transition: %s to %s",
4542 piaddr (lease -> ip_addr),
4543 binding_state_print (lease -> binding_state),
4544 binding_state_print (msg -> binding_status))
4545 >= sizeof outbuf)
4546 log_fatal ("%s: impossible outbuf overflow",
4547 "dhcp_failover_process_bind_update");
4549 dhcp_failover_send_bind_ack (state, msg,
4550 FTR_FATAL_CONFLICT,
4551 outbuf);
4552 goto out;
4554 if (new_binding_state == FTS_EXPIRED ||
4555 new_binding_state == FTS_RELEASED ||
4556 new_binding_state == FTS_RESET)
4557 lt -> next_binding_state = FTS_FREE;
4558 else
4559 lt -> next_binding_state = new_binding_state;
4560 msg -> binding_status = lt -> next_binding_state;
4563 /* Try to install the new information. */
4564 if (!supersede_lease (lease, lt, 0, 0, 0) ||
4565 !write_lease (lease)) {
4566 message = "database update failed";
4567 bad:
4568 dhcp_failover_send_bind_ack (state, msg, reason, message);
4569 } else {
4571 dhcp_failover_queue_ack (state, msg);
4574 out:
4575 if (lt)
4576 lease_dereference (&lt, MDL);
4577 if (lease)
4578 lease_dereference (&lease, MDL);
4580 return ISC_R_SUCCESS;
4583 isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *state,
4584 failover_message_t *msg)
4586 struct lease *lt = (struct lease *)0;
4587 struct lease *lease = (struct lease *)0;
4588 struct iaddr ia;
4589 const char *message = "no memory";
4591 ia.len = sizeof msg -> assigned_addr;
4592 memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
4594 if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
4595 message = "no such lease";
4596 goto bad;
4599 /* XXX check for conflicts. */
4600 if (msg -> options_present & FTB_REJECT_REASON) {
4601 log_error ("bind update on %s from %s rejected: %.*s",
4602 piaddr (ia), state -> name,
4603 (int)((msg -> options_present & FTB_MESSAGE)
4604 ? msg -> message.count
4605 : strlen (dhcp_failover_reject_reason_print
4606 (msg -> reject_reason))),
4607 (msg -> options_present & FTB_MESSAGE)
4608 ? (const char *)(msg -> message.data)
4609 : (dhcp_failover_reject_reason_print
4610 (msg -> reject_reason)));
4611 goto unqueue;
4614 /* XXX Times may need to be adjusted based on clock skew! */
4615 if (msg -> options_present & FTB_POTENTIAL_EXPIRY) {
4616 /* XXX it could be a problem to do this directly if the
4617 XXX lease is sorted by tsfp. */
4618 if ((lease -> binding_state == FTS_EXPIRED ||
4619 lease -> binding_state == FTS_RESET ||
4620 lease -> binding_state == FTS_RELEASED) &&
4621 (msg -> options_present & FTB_BINDING_STATUS) &&
4622 msg -> binding_status == FTS_FREE)
4624 lease -> tsfp = msg -> potential_expiry;
4625 lease -> next_binding_state = FTS_FREE;
4626 supersede_lease (lease, (struct lease *)0, 0, 0, 0);
4627 write_lease (lease);
4628 if (state -> me.state == normal)
4629 commit_leases ();
4630 } else {
4631 lease -> tsfp = msg -> potential_expiry;
4632 if ((lease -> desired_binding_state !=
4633 lease -> binding_state) &&
4634 (msg -> options_present & FTB_BINDING_STATUS) &&
4635 (msg -> binding_status ==
4636 lease -> desired_binding_state)) {
4637 lease -> next_binding_state =
4638 lease -> desired_binding_state;
4639 supersede_lease (lease,
4640 (struct lease *)0, 0, 0, 0);
4642 write_lease (lease);
4643 /* Commit the lease only after a two-second timeout,
4644 so that if we get a bunch of acks in quick
4645 successtion (e.g., when stealing leases from the
4646 secondary), we do not do an immediate commit for
4647 each one. */
4648 add_timeout (cur_time + 2,
4649 commit_leases_timeout, (void *)0, 0, 0);
4651 } else if (lease -> desired_binding_state != lease -> binding_state &&
4652 (msg -> options_present & FTB_BINDING_STATUS) &&
4653 msg -> binding_status == lease -> desired_binding_state) {
4654 lease -> next_binding_state = lease -> desired_binding_state;
4655 supersede_lease (lease, (struct lease *)0, 0, 0, 0);
4656 write_lease (lease);
4657 add_timeout (cur_time + 2, commit_leases_timeout,
4658 (void *)0, 0, 0);
4661 unqueue:
4662 dhcp_failover_ack_queue_remove (state, lease);
4664 /* If we are supposed to send an update done after we send
4665 this lease, go ahead and send it. */
4666 if (state -> send_update_done == lease) {
4667 lease_dereference (&state -> send_update_done, MDL);
4668 dhcp_failover_send_update_done (state);
4671 /* If there are updates pending, we've created space to send at
4672 least one. */
4673 dhcp_failover_send_updates (state);
4675 out:
4676 lease_dereference (&lease, MDL);
4677 if (lt)
4678 lease_dereference (&lt, MDL);
4680 return ISC_R_SUCCESS;
4682 bad:
4683 log_info ("bind update on %s from %s: %s.",
4684 piaddr (ia), state -> name, message);
4685 goto out;
4688 isc_result_t dhcp_failover_generate_update_queue (dhcp_failover_state_t *state,
4689 int everythingp)
4691 struct shared_network *s;
4692 struct pool *p;
4693 struct lease *l, *n;
4694 int i;
4695 struct lease **lptr [5];
4696 #define FREE_LEASES 0
4697 #define ACTIVE_LEASES 1
4698 #define EXPIRED_LEASES 2
4699 #define ABANDONED_LEASES 3
4700 #define BACKUP_LEASES 4
4702 /* First remove everything from the update and ack queues. */
4703 l = n = (struct lease *)0;
4704 if (state -> update_queue_head) {
4705 lease_reference (&l, state -> update_queue_head, MDL);
4706 lease_dereference (&state -> update_queue_head, MDL);
4707 do {
4708 l -> flags &= ~ON_UPDATE_QUEUE;
4709 if (l -> next_pending) {
4710 lease_reference (&n,
4711 l -> next_pending, MDL);
4712 lease_dereference (&l -> next_pending, MDL);
4714 lease_dereference (&l, MDL);
4715 if (n) {
4716 lease_reference (&l, n, MDL);
4717 lease_dereference (&n, MDL);
4719 } while (l);
4720 lease_dereference (&state -> update_queue_tail, MDL);
4723 if (state -> ack_queue_head) {
4724 lease_reference (&l, state -> ack_queue_head, MDL);
4725 lease_dereference (&state -> ack_queue_head, MDL);
4726 do {
4727 l -> flags &= ~ON_ACK_QUEUE;
4728 if (l -> next_pending) {
4729 lease_reference (&n,
4730 l -> next_pending, MDL);
4731 lease_dereference (&l -> next_pending, MDL);
4733 lease_dereference (&l, MDL);
4734 if (n) {
4735 lease_reference (&l, n, MDL);
4736 lease_dereference (&n, MDL);
4738 } while (l);
4739 lease_dereference (&state -> ack_queue_tail, MDL);
4741 if (state -> send_update_done)
4742 lease_dereference (&state -> send_update_done, MDL);
4743 state -> cur_unacked_updates = 0;
4745 /* Loop through each pool in each shared network and call the
4746 expiry routine on the pool. */
4747 for (s = shared_networks; s; s = s -> next) {
4748 for (p = s -> pools; p; p = p -> next) {
4749 lptr [FREE_LEASES] = &p -> free;
4750 lptr [ACTIVE_LEASES] = &p -> active;
4751 lptr [EXPIRED_LEASES] = &p -> expired;
4752 lptr [ABANDONED_LEASES] = &p -> abandoned;
4753 lptr [BACKUP_LEASES] = &p -> backup;
4755 for (i = FREE_LEASES; i <= BACKUP_LEASES; i++) {
4756 for (l = *(lptr [i]); l; l = l -> next) {
4757 if (p -> failover_peer == state &&
4758 ((everythingp &&
4759 (l -> starts != MIN_TIME ||
4760 l -> ends != MIN_TIME)) ||
4761 l -> tstp > l -> tsfp)) {
4762 l -> desired_binding_state = l -> binding_state;
4763 dhcp_failover_queue_update (l, 0);
4769 return ISC_R_SUCCESS;
4772 isc_result_t
4773 dhcp_failover_process_update_request (dhcp_failover_state_t *state,
4774 failover_message_t *msg)
4776 /* Generate a fresh update queue. */
4777 dhcp_failover_generate_update_queue (state, 0);
4779 /* If there's anything on the update queue (there shouldn't be
4780 anything on the ack queue), trigger an update done message
4781 when we get an ack for that lease. */
4782 if (state -> update_queue_tail) {
4783 lease_reference (&state -> send_update_done,
4784 state -> update_queue_tail, MDL);
4785 dhcp_failover_send_updates (state);
4786 log_info ("Update request from %s: sending update",
4787 state -> name);
4788 } else {
4789 /* Otherwise, there are no updates to send, so we can
4790 just send an UPDDONE message immediately. */
4791 dhcp_failover_send_update_done (state);
4792 log_info ("Update request from %s: nothing pending",
4793 state -> name);
4796 return ISC_R_SUCCESS;
4799 isc_result_t
4800 dhcp_failover_process_update_request_all (dhcp_failover_state_t *state,
4801 failover_message_t *msg)
4803 /* Generate a fresh update queue that includes every lease. */
4804 dhcp_failover_generate_update_queue (state, 1);
4806 if (state -> update_queue_tail) {
4807 lease_reference (&state -> send_update_done,
4808 state -> update_queue_tail, MDL);
4809 dhcp_failover_send_updates (state);
4810 log_info ("Update request all from %s: sending update",
4811 state -> name);
4812 } else {
4813 /* This should really never happen, but it could happen
4814 on a server that currently has no leases configured. */
4815 dhcp_failover_send_update_done (state);
4816 log_info ("Update request all from %s: nothing pending",
4817 state -> name);
4820 return ISC_R_SUCCESS;
4823 isc_result_t
4824 dhcp_failover_process_update_done (dhcp_failover_state_t *state,
4825 failover_message_t *msg)
4827 log_info ("failover peer %s: peer update completed.",
4828 state -> name);
4830 switch (state -> me.state) {
4831 case unknown_state:
4832 case partner_down:
4833 case normal:
4834 case communications_interrupted:
4835 case resolution_interrupted:
4836 case shut_down:
4837 case paused:
4838 case recover_done:
4839 case startup:
4840 case recover_wait:
4841 break; /* shouldn't happen. */
4843 /* We got the UPDDONE, so we can go into normal state! */
4844 case potential_conflict:
4845 dhcp_failover_set_state (state, normal);
4846 break;
4848 case recover:
4849 /* Wait for MCLT to expire before moving to recover_done,
4850 except that if both peers come up in recover, there is
4851 no point in waiting for MCLT to expire - this probably
4852 indicates the initial startup of a newly-configured
4853 failover pair. */
4854 if (state -> me.stos + state -> mclt > cur_time &&
4855 state -> partner.state != recover &&
4856 state -> partner.state != recover_done) {
4857 dhcp_failover_set_state (state, recover_wait);
4858 #if defined (DEBUG_FAILOVER_TIMING)
4859 log_info ("add_timeout +%d %s",
4860 (int)(cur_time -
4861 state -> me.stos + state -> mclt),
4862 "dhcp_failover_recover_done");
4863 #endif
4864 add_timeout ((int)(state -> me.stos + state -> mclt),
4865 dhcp_failover_recover_done,
4866 state,
4867 (tvref_t)omapi_object_reference,
4868 (tvunref_t)
4869 omapi_object_dereference);
4870 } else
4871 dhcp_failover_recover_done (state);
4874 return ISC_R_SUCCESS;
4877 void dhcp_failover_recover_done (void *sp)
4879 dhcp_failover_state_t *state = sp;
4881 #if defined (DEBUG_FAILOVER_TIMING)
4882 log_info ("dhcp_failover_recover_done");
4883 #endif
4885 dhcp_failover_set_state (state, recover_done);
4888 #if defined (DEBUG_FAILOVER_MESSAGES)
4889 /* Print hunks of failover messages, doing line breaks as appropriate.
4890 Note that this assumes syslog is being used, rather than, e.g., the
4891 Windows NT logging facility, where just dumping the whole message in
4892 one hunk would be more appropriate. */
4894 void failover_print (char *obuf,
4895 unsigned *obufix, unsigned obufmax, const char *s)
4897 int len = strlen (s);
4899 while (len + *obufix + 1 >= obufmax) {
4900 log_debug ("%s", obuf);
4901 if (!*obufix) {
4902 log_debug ("%s", s);
4903 *obufix = 0;
4904 return;
4906 *obufix = 0;
4908 strcpy (&obuf [*obufix], s);
4909 *obufix += len;
4911 #endif /* defined (DEBUG_FAILOVER_MESSAGES) */
4913 /* Taken from draft-ietf-dhc-loadb-01.txt: */
4914 /* A "mixing table" of 256 distinct values, in pseudo-random order. */
4915 unsigned char loadb_mx_tbl[256] = {
4916 251, 175, 119, 215, 81, 14, 79, 191, 103, 49,
4917 181, 143, 186, 157, 0, 232, 31, 32, 55, 60,
4918 152, 58, 17, 237, 174, 70, 160, 144, 220, 90,
4919 57, 223, 59, 3, 18, 140, 111, 166, 203, 196,
4920 134, 243, 124, 95, 222, 179, 197, 65, 180, 48,
4921 36, 15, 107, 46, 233, 130, 165, 30, 123, 161,
4922 209, 23, 97, 16, 40, 91, 219, 61, 100, 10,
4923 210, 109, 250, 127, 22, 138, 29, 108, 244, 67,
4924 207, 9, 178, 204, 74, 98, 126, 249, 167, 116,
4925 34, 77, 193, 200, 121, 5, 20, 113, 71, 35,
4926 128, 13, 182, 94, 25, 226, 227, 199, 75, 27,
4927 41, 245, 230, 224, 43, 225, 177, 26, 155, 150,
4928 212, 142, 218, 115, 241, 73, 88, 105, 39, 114,
4929 62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
4930 154, 122, 12, 84, 82, 163, 44, 139, 228, 236,
4931 205, 242, 217, 11, 187, 146, 159, 64, 86, 239,
4932 195, 42, 106, 198, 118, 112, 184, 172, 87, 2,
4933 173, 117, 176, 229, 247, 253, 137, 185, 99, 164,
4934 102, 147, 45, 66, 231, 52, 141, 211, 194, 206,
4935 246, 238, 56, 110, 78, 248, 63, 240, 189, 93,
4936 92, 51, 53, 183, 19, 171, 72, 50, 33, 104,
4937 101, 69, 8, 252, 83, 120, 76, 135, 85, 54,
4938 202, 125, 188, 213, 96, 235, 136, 208, 162, 129,
4939 190, 132, 156, 38, 47, 1, 7, 254, 24, 4,
4940 216, 131, 89, 21, 28, 133, 37, 153, 149, 80,
4941 170, 68, 6, 169, 234, 151 };
4943 static unsigned char loadb_p_hash (const unsigned char *, unsigned);
4944 static unsigned char loadb_p_hash (const unsigned char *key, unsigned len)
4946 unsigned char hash = len;
4947 int i;
4948 for(i = len; i > 0; )
4949 hash = loadb_mx_tbl [hash ^ (key [--i])];
4950 return hash;
4953 int load_balance_mine (struct packet *packet, dhcp_failover_state_t *state)
4955 struct option_cache *oc;
4956 struct data_string ds;
4957 unsigned char hbaix;
4958 int hm;
4960 if (state -> load_balance_max_secs < ntohs (packet -> raw -> secs)) {
4961 return 1;
4964 /* If we don't have a hash bucket array, we can't tell if this
4965 one's ours, so we assume it's not. */
4966 if (!state -> hba)
4967 return 0;
4969 oc = lookup_option (&dhcp_universe, packet -> options,
4970 DHO_DHCP_CLIENT_IDENTIFIER);
4971 memset (&ds, 0, sizeof ds);
4972 if (oc &&
4973 evaluate_option_cache (&ds, packet, (struct lease *)0,
4974 (struct client_state *)0,
4975 packet -> options, (struct option_state *)0,
4976 &global_scope, oc, MDL)) {
4977 hbaix = loadb_p_hash (ds.data, ds.len);
4978 } else {
4979 hbaix = loadb_p_hash (packet -> raw -> chaddr,
4980 packet -> raw -> hlen);
4982 hm = (state -> hba [hbaix / 8] & (1 << (hbaix & 3)));
4983 if (state -> i_am == primary)
4984 return hm;
4985 else
4986 return !hm;
4989 /* This deals with what to do with bind updates when
4990 we're in the normal state
4992 Note that tsfp had better be set from the latest bind update
4993 _before_ this function is called! */
4995 binding_state_t
4996 normal_binding_state_transition_check (struct lease *lease,
4997 dhcp_failover_state_t *state,
4998 binding_state_t binding_state,
4999 u_int32_t tsfp)
5001 binding_state_t new_state;
5003 /* If there is no transition, it's no problem. */
5004 if (binding_state == lease -> binding_state)
5005 return binding_state;
5007 switch (lease -> binding_state) {
5008 case FTS_FREE:
5009 case FTS_ABANDONED:
5010 switch (binding_state) {
5011 case FTS_ACTIVE:
5012 case FTS_ABANDONED:
5013 case FTS_BACKUP:
5014 case FTS_EXPIRED:
5015 case FTS_RELEASED:
5016 case FTS_RESET:
5017 /* If the lease was free, and our peer is primary,
5018 then it can make it active, or abandoned, or
5019 backup. Abandoned is treated like free in
5020 this case. */
5021 if (state -> i_am == secondary)
5022 return binding_state;
5024 /* Otherwise, it can't legitimately do any sort of
5025 state transition. Because the lease was free,
5026 and the error has already been made, we allow the
5027 peer to change its state anyway, but log a warning
5028 message in hopes that the error will be fixed. */
5029 case FTS_FREE: /* for compiler */
5030 new_state = binding_state;
5031 goto out;
5033 default:
5034 log_fatal ("Impossible case at %s:%d.", MDL);
5035 return FTS_RESET;
5037 case FTS_ACTIVE:
5038 /* The secondary can't change the state of an active
5039 lease. */
5040 if (state -> i_am == primary) {
5041 /* Except that the client may send the DHCPRELEASE
5042 to the secondary, and we have to accept that. */
5043 if (binding_state == FTS_RELEASED)
5044 return binding_state;
5045 new_state = lease -> binding_state;
5046 goto out;
5049 /* So this is only for transitions made by the primary: */
5050 switch (binding_state) {
5051 case FTS_FREE:
5052 case FTS_BACKUP:
5053 /* Can't set a lease to free or backup until the
5054 peer agrees that it's expired. */
5055 if (tsfp > cur_time) {
5056 new_state = lease -> binding_state;
5057 goto out;
5059 return binding_state;
5061 case FTS_EXPIRED:
5062 /* XXX 65 should be the clock skew between the peers
5063 XXX plus a fudge factor. This code will result
5064 XXX in problems if MCLT is really short or the
5065 XXX max-lease-time is really short (less than the
5066 XXX fudge factor. */
5067 if (lease -> ends - 65 > cur_time) {
5068 new_state = lease -> binding_state;
5069 goto out;
5072 case FTS_RELEASED:
5073 case FTS_ABANDONED:
5074 case FTS_RESET:
5075 case FTS_ACTIVE:
5076 return binding_state;
5078 default:
5079 log_fatal ("Impossible case at %s:%d.", MDL);
5080 return FTS_RESET;
5082 break;
5083 case FTS_EXPIRED:
5084 switch (binding_state) {
5085 case FTS_BACKUP:
5086 case FTS_FREE:
5087 /* Can't set a lease to free or backup until the
5088 peer agrees that it's expired. */
5089 if (tsfp > cur_time) {
5090 new_state = lease -> binding_state;
5091 goto out;
5093 return binding_state;
5095 case FTS_ACTIVE:
5096 case FTS_RELEASED:
5097 case FTS_ABANDONED:
5098 case FTS_RESET:
5099 case FTS_EXPIRED:
5100 return binding_state;
5102 default:
5103 log_fatal ("Impossible case at %s:%d.", MDL);
5104 return FTS_RESET;
5106 case FTS_RELEASED:
5107 switch (binding_state) {
5108 case FTS_FREE:
5109 case FTS_BACKUP:
5111 /* These are invalid state transitions - should we
5112 prevent them? */
5113 case FTS_EXPIRED:
5114 case FTS_ABANDONED:
5115 case FTS_RESET:
5116 case FTS_ACTIVE:
5117 case FTS_RELEASED:
5118 return binding_state;
5120 default:
5121 log_fatal ("Impossible case at %s:%d.", MDL);
5122 return FTS_RESET;
5124 case FTS_RESET:
5125 switch (binding_state) {
5126 case FTS_FREE:
5127 case FTS_BACKUP:
5128 /* Can't set a lease to free or backup until the
5129 peer agrees that it's expired. */
5130 if (tsfp > cur_time) {
5131 new_state = lease -> binding_state;
5132 goto out;
5134 return binding_state;
5136 case FTS_ACTIVE:
5137 case FTS_EXPIRED:
5138 case FTS_RELEASED:
5139 case FTS_ABANDONED:
5140 case FTS_RESET:
5141 return binding_state;
5143 default:
5144 log_fatal ("Impossible case at %s:%d.", MDL);
5145 return FTS_RESET;
5147 case FTS_BACKUP:
5148 switch (binding_state) {
5149 case FTS_ACTIVE:
5150 case FTS_ABANDONED:
5151 case FTS_EXPIRED:
5152 case FTS_RELEASED:
5153 case FTS_RESET:
5154 /* If the lease was in backup, and our peer
5155 is secondary, then it can make it active
5156 or abandoned. */
5157 if (state -> i_am == primary)
5158 return binding_state;
5160 /* Either the primary or the secondary can
5161 reasonably move a lease from the backup
5162 state to the free state. */
5163 case FTS_FREE:
5164 return binding_state;
5166 case FTS_BACKUP:
5167 new_state = lease -> binding_state;
5168 goto out;
5170 default:
5171 log_fatal ("Impossible case at %s:%d.", MDL);
5172 return FTS_RESET;
5175 default:
5176 log_fatal ("Impossible case at %s:%d.", MDL);
5177 return FTS_RESET;
5179 out:
5180 return new_state;
5183 /* Determine whether the state transition is okay when we're potentially
5184 in conflict with the peer. */
5185 binding_state_t
5186 conflict_binding_state_transition_check (struct lease *lease,
5187 dhcp_failover_state_t *state,
5188 binding_state_t binding_state,
5189 u_int32_t tsfp)
5191 binding_state_t new_state;
5193 /* If there is no transition, it's no problem. */
5194 if (binding_state == lease -> binding_state)
5195 new_state = binding_state;
5196 else {
5197 switch (lease -> binding_state) {
5198 /* If we think the lease is not in use, then the
5199 state into which the partner put it is just fine,
5200 whatever it is. */
5201 case FTS_FREE:
5202 case FTS_ABANDONED:
5203 case FTS_EXPIRED:
5204 case FTS_RELEASED:
5205 case FTS_RESET:
5206 case FTS_BACKUP:
5207 new_state = binding_state;
5208 break;
5210 /* If we think the lease *is* in use, then we're not
5211 going to take the partner's change if the partner
5212 thinks it's free. */
5213 case FTS_ACTIVE:
5214 switch (binding_state) {
5215 case FTS_FREE:
5216 case FTS_BACKUP:
5217 case FTS_ABANDONED:
5218 new_state = lease -> binding_state;
5219 break;
5221 case FTS_EXPIRED:
5222 case FTS_RELEASED:
5223 case FTS_RESET:
5224 if (lease -> ends > cur_time)
5225 new_state =
5226 lease -> binding_state;
5227 else
5228 new_state = binding_state;
5229 break;
5231 case FTS_ACTIVE:
5232 new_state = binding_state;
5233 break;
5235 default:
5236 log_fatal ("Impossible case at %s:%d.", MDL);
5237 return FTS_RESET;
5239 break;
5241 default:
5242 log_fatal ("Impossible case at %s:%d.", MDL);
5243 return FTS_RESET;
5246 return new_state;
5249 /* We can reallocate a lease under the following circumstances:
5251 (1) It belongs to us - it's FTS_FREE, and we're primary, or it's
5252 FTS_BACKUP, and we're secondary.
5253 (2) We're in partner_down, and the lease is not active, and we
5254 can be sure that the other server didn't make it active.
5255 We can only be sure that the server didn't make it active
5256 when we are in the partner_down state and one of the following
5257 two conditions holds:
5258 (a) in the case that the time sent from the peer is earlier than
5259 the time we entered the partner_down state, at least MCLT has
5260 gone by since we entered partner_down, or
5261 (b) in the case that the time sent from the peer is later than
5262 the time when we entered partner_down, the current time is
5263 later than the time sent from the peer by at least MCLT. */
5265 int lease_mine_to_reallocate (struct lease *lease)
5267 dhcp_failover_state_t *peer;
5269 if (lease && lease -> pool &&
5270 lease -> pool -> failover_peer) {
5271 peer = lease -> pool -> failover_peer;
5272 switch (lease -> binding_state) {
5273 case FTS_ACTIVE:
5274 return 0;
5276 case FTS_FREE:
5277 if (peer -> i_am == primary)
5278 return 1;
5279 if (peer -> service_state == service_partner_down &&
5280 (lease -> tsfp < peer -> me.stos
5281 ? peer -> me.stos + peer -> mclt < cur_time
5282 : lease -> tsfp + peer -> mclt < cur_time))
5283 return 1;
5284 return 0;
5286 case FTS_ABANDONED:
5287 case FTS_RESET:
5288 case FTS_RELEASED:
5289 case FTS_EXPIRED:
5290 if (peer -> service_state == service_partner_down &&
5291 (lease -> tsfp < peer -> me.stos
5292 ? peer -> me.stos + peer -> mclt < cur_time
5293 : lease -> tsfp + peer -> mclt < cur_time))
5294 return 1;
5295 return 0;
5296 case FTS_BACKUP:
5297 if (peer -> i_am == secondary)
5298 return 1;
5299 if (peer -> service_state == service_partner_down &&
5300 (lease -> tsfp < peer -> me.stos
5301 ? peer -> me.stos + peer -> mclt < cur_time
5302 : lease -> tsfp + peer -> mclt < cur_time))
5303 return 1;
5304 return 0;
5306 return 0;
5308 if (lease)
5309 return !(lease -> binding_state != FTS_FREE &&
5310 lease -> binding_state != FTS_BACKUP);
5311 else
5312 return 0;
5315 static isc_result_t failover_message_reference (failover_message_t **mp,
5316 failover_message_t *m,
5317 const char *file, int line)
5319 *mp = m;
5320 m -> refcnt++;
5321 return ISC_R_SUCCESS;
5324 static isc_result_t failover_message_dereference (failover_message_t **mp,
5325 const char *file, int line)
5327 failover_message_t *m;
5328 m = (*mp);
5329 m -> refcnt--;
5330 if (m -> refcnt == 0) {
5331 if (m -> next)
5332 failover_message_dereference (&m -> next,
5333 file, line);
5334 if (m -> chaddr.data)
5335 dfree (m -> chaddr.data, file, line);
5336 if (m -> client_identifier.data)
5337 dfree (m -> client_identifier.data, file, line);
5338 if (m -> hba.data)
5339 dfree (m -> hba.data, file, line);
5340 if (m -> message.data)
5341 dfree (m -> message.data, file, line);
5342 if (m -> reply_options.data)
5343 dfree (m -> reply_options.data, file, line);
5344 if (m -> request_options.data)
5345 dfree (m -> request_options.data, file, line);
5346 if (m -> vendor_class.data)
5347 dfree (m -> vendor_class.data, file, line);
5348 if (m -> vendor_options.data)
5349 dfree (m -> vendor_options.data, file, line);
5350 if (m -> ddns.data)
5351 dfree (m -> ddns.data, file, line);
5352 dfree (*mp, file, line);
5354 *mp = 0;
5355 return ISC_R_SUCCESS;
5358 OMAPI_OBJECT_ALLOC (dhcp_failover_state, dhcp_failover_state_t,
5359 dhcp_type_failover_state)
5360 OMAPI_OBJECT_ALLOC (dhcp_failover_listener, dhcp_failover_listener_t,
5361 dhcp_type_failover_listener)
5362 OMAPI_OBJECT_ALLOC (dhcp_failover_link, dhcp_failover_link_t,
5363 dhcp_type_failover_link)
5364 #endif /* defined (FAILOVER_PROTOCOL) */
5366 const char *binding_state_print (enum failover_state state)
5368 switch (state) {
5369 case FTS_FREE:
5370 return "free";
5371 break;
5373 case FTS_ACTIVE:
5374 return "active";
5375 break;
5377 case FTS_EXPIRED:
5378 return "expired";
5379 break;
5381 case FTS_RELEASED:
5382 return "released";
5383 break;
5385 case FTS_ABANDONED:
5386 return "abandoned";
5387 break;
5389 case FTS_RESET:
5390 return "reset";
5391 break;
5393 case FTS_BACKUP:
5394 return "backup";
5395 break;
5397 default:
5398 return "unknown";
5399 break;