revert between 56095 -> 55830 in arch
[AROS.git] / workbench / network / stacks / AROSTCP / dhcp / server / failover.c
blobbffda8ad18798d67bcc8d7249e8ad70c56321a94
1 /* failover.c
3 Failover protocol support code... */
5 /*
6 * Copyright (c) 2004-2005 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1999-2003 by Internet Software Consortium
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 * Internet Systems Consortium, Inc.
22 * 950 Charter Street
23 * Redwood City, CA 94063
24 * <info@isc.org>
25 * http://www.isc.org/
27 * This software has been written for Internet Systems Consortium
28 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29 * To learn more about Internet Systems Consortium, see
30 * ``http://www.isc.org/''. To learn more about Vixie Enterprises,
31 * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
32 * ``http://www.nominum.com''.
35 #ifndef lint
36 static char copyright[] =
37 "$Id$ Copyright (c) 2004-2005 Internet Systems Consortium. All rights reserved.\n";
38 #endif /* not lint */
40 #include "dhcpd.h"
41 #include "version.h"
42 #include <omapip/omapip_p.h>
44 #if defined (FAILOVER_PROTOCOL)
45 dhcp_failover_state_t *failover_states;
46 static isc_result_t do_a_failover_option (omapi_object_t *,
47 dhcp_failover_link_t *);
48 dhcp_failover_listener_t *failover_listeners;
50 static isc_result_t failover_message_reference (failover_message_t **,
51 failover_message_t *,
52 const char *file, int line);
53 static isc_result_t failover_message_dereference (failover_message_t **,
54 const char *file, int line);
56 void dhcp_failover_startup ()
58 dhcp_failover_state_t *state;
59 isc_result_t status;
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 /* Currently when we enter recover state, no matter
1950 * the reason, we send an UPDREQALL. So, it makes
1951 * the most sense to stick to that until something
1952 * better is done.
1953 * Furthermore, we only went to send the update
1954 * request if we are not in startup state.
1956 if (state -> me.state == recover)
1957 dhcp_failover_send_update_request_all (state);
1958 break;
1960 case shut_down:
1961 /* XXX We're not explicitly told what to do in this
1962 XXX case, but this transition is consistent with
1963 XXX what is elsewhere in the draft. */
1964 dhcp_failover_set_state (state, partner_down);
1965 break;
1967 /* We can't really do anything in this case. */
1968 case paused:
1969 break;
1971 /* We should have asked for an update already. */
1972 case recover_done:
1973 case recover_wait:
1974 break;
1976 case unknown_state:
1977 case startup:
1978 break;
1980 break;
1982 case potential_conflict:
1983 switch (new_state) {
1984 case normal:
1985 if (previous_state == potential_conflict &&
1986 state -> i_am == secondary)
1987 dhcp_failover_send_update_request (state);
1988 break;
1990 case recover_done:
1991 case recover_wait:
1992 case potential_conflict:
1993 case partner_down:
1994 case communications_interrupted:
1995 case resolution_interrupted:
1996 case paused:
1997 break;
1999 case recover:
2000 dhcp_failover_set_state (state, recover);
2001 break;
2003 case shut_down:
2004 dhcp_failover_set_state (state, partner_down);
2005 break;
2007 case unknown_state:
2008 case startup:
2009 break;
2011 break;
2013 case partner_down:
2014 /* Take no action if other server is starting up. */
2015 if (startupp)
2016 break;
2018 switch (new_state) {
2019 /* This is where we should be. */
2020 case recover:
2021 case recover_wait:
2022 break;
2024 case recover_done:
2025 dhcp_failover_set_state (state, normal);
2026 break;
2028 case normal:
2029 case potential_conflict:
2030 case partner_down:
2031 case communications_interrupted:
2032 case resolution_interrupted:
2033 dhcp_failover_set_state (state, potential_conflict);
2034 break;
2036 /* These don't change anything. */
2037 case shut_down:
2038 case paused:
2039 break;
2041 case unknown_state:
2042 case startup:
2043 break;
2045 break;
2047 case communications_interrupted:
2048 switch (new_state) {
2049 case paused:
2050 /* Stick with the status quo. */
2051 break;
2053 /* If we're in communications-interrupted and an
2054 amnesiac peer connects, go to the partner_down
2055 state immediately. */
2056 case recover:
2057 dhcp_failover_set_state (state, partner_down);
2058 break;
2060 case normal:
2061 case communications_interrupted:
2062 case recover_done:
2063 case recover_wait:
2064 /* XXX so we don't need to do this specially in
2065 XXX the CONNECT and CONNECTACK handlers. */
2066 dhcp_failover_send_updates (state);
2067 dhcp_failover_set_state (state, normal);
2068 break;
2070 case potential_conflict:
2071 case partner_down:
2072 case resolution_interrupted:
2073 dhcp_failover_set_state (state, potential_conflict);
2074 break;
2076 case shut_down:
2077 dhcp_failover_set_state (state, partner_down);
2078 break;
2080 case unknown_state:
2081 case startup:
2082 break;
2084 break;
2086 case resolution_interrupted:
2087 switch (new_state) {
2088 case normal:
2089 case recover:
2090 case potential_conflict:
2091 case partner_down:
2092 case communications_interrupted:
2093 case resolution_interrupted:
2094 case recover_done:
2095 case recover_wait:
2096 dhcp_failover_set_state (state, potential_conflict);
2097 break;
2099 case shut_down:
2100 dhcp_failover_set_state (state, partner_down);
2101 break;
2103 case paused:
2104 break;
2106 case unknown_state:
2107 case startup:
2108 break;
2110 break;
2112 case recover_done:
2113 switch (new_state) {
2114 case normal:
2115 case recover_done:
2116 dhcp_failover_set_state (state, normal);
2117 break;
2119 case potential_conflict:
2120 case partner_down:
2121 case communications_interrupted:
2122 case resolution_interrupted:
2123 case paused:
2124 case recover:
2125 case recover_wait:
2126 break;
2128 case shut_down:
2129 dhcp_failover_set_state (state, partner_down);
2130 break;
2132 case unknown_state:
2133 case startup:
2134 break;
2136 break;
2138 /* We are essentially dead in the water when we're in
2139 either shut_down or paused states, and do not do any
2140 automatic state transitions. */
2141 case shut_down:
2142 case paused:
2143 break;
2145 /* We still have to wait... */
2146 case recover_wait:
2147 break;
2149 case unknown_state:
2150 break;
2153 /* If we didn't make a transition out of startup as a result of
2154 the peer's state change, do it now as a result of the fact that
2155 we got a state change from the peer. */
2156 if (state -> me.state == startup && state -> saved_state != startup)
2157 dhcp_failover_set_state (state, state -> saved_state);
2159 /* For now, just set the service state based on the peer's state
2160 if necessary. */
2161 dhcp_failover_set_service_state (state);
2163 return ISC_R_SUCCESS;
2166 int dhcp_failover_pool_rebalance (dhcp_failover_state_t *state)
2168 int lts;
2169 int leases_queued = 0;
2170 struct lease *lp = (struct lease *)0;
2171 struct lease *next = (struct lease *)0;
2172 struct shared_network *s;
2173 struct pool *p;
2174 int polarity;
2175 binding_state_t peer_lease_state;
2176 binding_state_t my_lease_state;
2177 struct lease **lq;
2178 int tenper;
2180 if (state -> me.state != normal || state -> i_am == secondary)
2181 return 0;
2183 for (s = shared_networks; s; s = s -> next) {
2184 for (p = s -> pools; p; p = p -> next) {
2185 if (p -> failover_peer != state)
2186 continue;
2188 /* Right now we're giving the peer half of the free leases.
2189 If we have more leases than the peer (i.e., more than
2190 half), then the number of leases we have, less the number
2191 of leases the peer has, will be how many more leases we
2192 have than the peer has. So if we send half that number
2193 to the peer, we should be even. */
2194 if (p -> failover_peer -> i_am == primary) {
2195 lts = (p -> free_leases - p -> backup_leases) / 2;
2196 peer_lease_state = FTS_BACKUP;
2197 my_lease_state = FTS_FREE;
2198 lq = &p -> free;
2199 } else {
2200 lts = (p -> backup_leases - p -> free_leases) / 2;
2201 peer_lease_state = FTS_FREE;
2202 my_lease_state = FTS_BACKUP;
2203 lq = &p -> backup;
2206 tenper = (p -> backup_leases + p -> free_leases) / 10;
2207 if (tenper == 0)
2208 tenper = 1;
2209 if (lts > tenper) {
2210 log_info ("pool %lx %s total %d free %d %s %d lts %d",
2211 (unsigned long)p,
2212 (p -> shared_network ?
2213 p -> shared_network -> name : ""), p -> lease_count,
2214 p -> free_leases, "backup", p -> backup_leases, lts);
2216 lease_reference (&lp, *lq, MDL);
2218 while (lp && lts) {
2219 /* Remember the next lease in the list. */
2220 if (next)
2221 lease_dereference (&next, MDL);
2222 if (lp -> next)
2223 lease_reference (&next, lp -> next, MDL);
2225 --lts;
2226 ++leases_queued;
2227 lp -> next_binding_state = peer_lease_state;
2228 lp -> tstp = cur_time;
2229 lp -> starts = cur_time;
2231 if (!supersede_lease (lp, (struct lease *)0, 0, 1, 0)
2232 || !write_lease (lp))
2234 log_info ("can't commit lease %s on giveaway",
2235 piaddr (lp -> ip_addr));
2238 lease_dereference (&lp, MDL);
2239 if (next)
2240 lease_reference (&lp, next, MDL);
2242 if (next)
2243 lease_dereference (&next, MDL);
2244 if (lp)
2245 lease_dereference (&lp, MDL);
2248 if (lts > 1) {
2249 log_info ("lease imbalance - lts = %d", lts);
2253 commit_leases();
2254 dhcp_failover_send_poolresp (state, leases_queued);
2255 dhcp_failover_send_updates (state);
2256 return leases_queued;
2259 int dhcp_failover_pool_check (struct pool *pool)
2261 int lts;
2262 struct lease *lp;
2263 int tenper;
2265 if (!pool -> failover_peer ||
2266 pool -> failover_peer -> me.state != normal)
2267 return 0;
2269 if (pool -> failover_peer -> i_am == primary)
2270 lts = (pool -> backup_leases - pool -> free_leases) / 2;
2271 else
2272 lts = (pool -> free_leases - pool -> backup_leases) / 2;
2274 log_info ("pool %lx %s total %d free %d backup %d lts %d",
2275 (unsigned long)pool,
2276 pool -> shared_network ? pool -> shared_network -> name : "",
2277 pool -> lease_count,
2278 pool -> free_leases, pool -> backup_leases, lts);
2280 tenper = (pool -> backup_leases + pool -> free_leases) / 10;
2281 if (tenper == 0)
2282 tenper = 1;
2283 if (lts > tenper) {
2284 /* XXX What about multiple pools? */
2285 if (pool -> failover_peer -> i_am == secondary) {
2286 /* Ask the primary to send us leases. */
2287 dhcp_failover_send_poolreq (pool -> failover_peer);
2288 return 1;
2289 } else {
2290 /* Figure out how many leases to skip on the backup
2291 list. We skip the earliest leases on the list
2292 to reduce the chance of trying to steal a lease
2293 that the secondary is about to allocate. */
2294 int i = pool -> backup_leases - lts;
2295 log_info ("Taking %d leases from secondary.", lts);
2296 for (lp = pool -> backup; lp; lp = lp -> next) {
2297 /* Skip to the last leases on the free
2298 list, because they are less likely
2299 to already have been allocated. */
2300 if (i)
2301 --i;
2302 else {
2303 lp -> desired_binding_state = FTS_FREE;
2304 dhcp_failover_queue_update (lp, 1);
2305 --lts;
2308 if (lts)
2309 log_info ("failed to take %d leases.", lts);
2312 return 0;
2315 int dhcp_failover_state_pool_check (dhcp_failover_state_t *state)
2317 struct lease *lp;
2318 struct shared_network *s;
2319 struct pool *p;
2321 for (s = shared_networks; s; s = s -> next) {
2322 for (p = s -> pools; p; p = p -> next) {
2323 if (p -> failover_peer != state)
2324 continue;
2325 /* Only need to request rebalance on one pool. */
2326 if (dhcp_failover_pool_check (p))
2327 return 1;
2330 return 0;
2333 isc_result_t dhcp_failover_send_updates (dhcp_failover_state_t *state)
2335 struct lease *lp = (struct lease *)0;
2336 isc_result_t status;
2338 /* Can't update peer if we're not talking to it! */
2339 if (!state -> link_to_peer)
2340 return ISC_R_SUCCESS;
2342 while ((state -> partner.max_flying_updates >
2343 state -> cur_unacked_updates) && state -> update_queue_head) {
2344 /* Grab the head of the update queue. */
2345 lease_reference (&lp, state -> update_queue_head, MDL);
2347 /* Send the update to the peer. */
2348 status = dhcp_failover_send_bind_update (state, lp);
2349 if (status != ISC_R_SUCCESS) {
2350 lease_dereference (&lp, MDL);
2351 return status;
2353 lp -> flags &= ~ON_UPDATE_QUEUE;
2355 /* Take it off the head of the update queue and put the next
2356 item in the update queue at the head. */
2357 lease_dereference (&state -> update_queue_head, MDL);
2358 if (lp -> next_pending) {
2359 lease_reference (&state -> update_queue_head,
2360 lp -> next_pending, MDL);
2361 lease_dereference (&lp -> next_pending, MDL);
2362 } else {
2363 lease_dereference (&state -> update_queue_tail, MDL);
2366 if (state -> ack_queue_head) {
2367 lease_reference
2368 (&state -> ack_queue_tail -> next_pending,
2369 lp, MDL);
2370 lease_dereference (&state -> ack_queue_tail, MDL);
2371 } else {
2372 lease_reference (&state -> ack_queue_head, lp, MDL);
2374 #if defined (POINTER_DEBUG)
2375 if (lp -> next_pending) {
2376 log_error ("ack_queue_tail: lp -> next_pending");
2377 abort ();
2379 #endif
2380 lease_reference (&state -> ack_queue_tail, lp, MDL);
2381 lp -> flags |= ON_ACK_QUEUE;
2382 lease_dereference (&lp, MDL);
2384 /* Count the object as an unacked update. */
2385 state -> cur_unacked_updates++;
2387 return ISC_R_SUCCESS;
2390 /* Queue an update for a lease. Always returns 1 at this point - it's
2391 not an error for this to be called on a lease for which there's no
2392 failover peer. */
2394 int dhcp_failover_queue_update (struct lease *lease, int immediate)
2396 dhcp_failover_state_t *state;
2398 if (!lease -> pool ||
2399 !lease -> pool -> failover_peer)
2400 return 1;
2402 /* If it's already on the update queue, leave it there. */
2403 if (lease -> flags & ON_UPDATE_QUEUE)
2404 return 1;
2406 /* Get the failover state structure for this lease. */
2407 state = lease -> pool -> failover_peer;
2409 /* If it's on the ack queue, take it off. */
2410 if (lease -> flags & ON_ACK_QUEUE)
2411 dhcp_failover_ack_queue_remove (state, lease);
2413 if (state -> update_queue_head) {
2414 lease_reference (&state -> update_queue_tail -> next_pending,
2415 lease, MDL);
2416 lease_dereference (&state -> update_queue_tail, MDL);
2417 } else {
2418 lease_reference (&state -> update_queue_head, lease, MDL);
2420 #if defined (POINTER_DEBUG)
2421 if (lease -> next_pending) {
2422 log_error ("next pending on update queue lease.");
2423 #if defined (DEBUG_RC_HISTORY)
2424 dump_rc_history (lease);
2425 #endif
2426 abort ();
2428 #endif
2429 lease_reference (&state -> update_queue_tail, lease, MDL);
2430 lease -> flags |= ON_UPDATE_QUEUE;
2431 if (immediate)
2432 dhcp_failover_send_updates (state);
2433 return 1;
2436 int dhcp_failover_send_acks (dhcp_failover_state_t *state)
2438 failover_message_t *msg = (failover_message_t *)0;
2440 /* Must commit all leases prior to acking them. */
2441 if (!commit_leases ())
2442 return 0;
2444 while (state -> toack_queue_head) {
2445 failover_message_reference
2446 (&msg, state -> toack_queue_head, MDL);
2447 failover_message_dereference
2448 (&state -> toack_queue_head, MDL);
2449 if (msg -> next) {
2450 failover_message_reference
2451 (&state -> toack_queue_head, msg -> next, MDL);
2454 dhcp_failover_send_bind_ack (state, msg, 0, (const char *)0);
2456 failover_message_dereference (&msg, MDL);
2459 if (state -> toack_queue_tail)
2460 failover_message_dereference (&state -> toack_queue_tail, MDL);
2461 state -> pending_acks = 0;
2463 return 1;
2466 void dhcp_failover_toack_queue_timeout (void *vs)
2468 dhcp_failover_state_t *state = vs;
2470 #if defined (DEBUG_FAILOVER_TIMING)
2471 log_info ("dhcp_failover_toack_queue_timeout");
2472 #endif
2474 dhcp_failover_send_acks (state);
2477 /* Queue an ack for a message. There is currently no way to queue a
2478 negative ack -- these need to be sent directly. */
2480 int dhcp_failover_queue_ack (dhcp_failover_state_t *state,
2481 failover_message_t *msg)
2483 if (state -> toack_queue_head) {
2484 failover_message_reference
2485 (&state -> toack_queue_tail -> next, msg, MDL);
2486 failover_message_dereference (&state -> toack_queue_tail, MDL);
2487 } else {
2488 failover_message_reference (&state -> toack_queue_head,
2489 msg, MDL);
2491 failover_message_reference (&state -> toack_queue_tail, msg, MDL);
2493 state -> pending_acks++;
2495 /* Flush the toack queue whenever we exceed half the number of
2496 allowed unacked updates. */
2497 if (state -> pending_acks >= state -> partner.max_flying_updates / 2) {
2498 dhcp_failover_send_acks (state);
2501 /* Schedule a timeout to flush the ack queue. */
2502 if (state -> pending_acks > 0) {
2503 #if defined (DEBUG_FAILOVER_TIMING)
2504 log_info ("add_timeout +2 %s",
2505 "dhcp_failover_toack_queue_timeout");
2506 #endif
2507 add_timeout (cur_time + 2,
2508 dhcp_failover_toack_queue_timeout, state,
2509 (tvref_t)dhcp_failover_state_reference,
2510 (tvunref_t)dhcp_failover_state_dereference);
2513 return 1;
2516 void dhcp_failover_ack_queue_remove (dhcp_failover_state_t *state,
2517 struct lease *lease)
2519 struct lease *lp;
2521 if (!(lease -> flags & ON_ACK_QUEUE))
2522 return;
2524 if (state -> ack_queue_head == lease) {
2525 lease_dereference (&state -> ack_queue_head, MDL);
2526 if (lease -> next_pending) {
2527 lease_reference (&state -> ack_queue_head,
2528 lease -> next_pending, MDL);
2529 lease_dereference (&lease -> next_pending, MDL);
2530 } else {
2531 lease_dereference (&state -> ack_queue_tail, MDL);
2533 } else {
2534 for (lp = state -> ack_queue_head;
2535 lp && lp -> next_pending != lease;
2536 lp = lp -> next_pending)
2539 if (!lp)
2540 return;
2542 lease_dereference (&lp -> next_pending, MDL);
2543 if (lease -> next_pending) {
2544 lease_reference (&lp -> next_pending,
2545 lease -> next_pending, MDL);
2546 lease_dereference (&lease -> next_pending, MDL);
2547 } else {
2548 lease_dereference (&state -> ack_queue_tail, MDL);
2549 if (lp -> next_pending) {
2550 log_error ("state -> ack_queue_tail");
2551 abort ();
2553 lease_reference (&state -> ack_queue_tail, lp, MDL);
2557 lease -> flags &= ~ON_ACK_QUEUE;
2558 state -> cur_unacked_updates--;
2561 * When updating leases as a result of an ack, we defer the commit
2562 * for performance reasons. When there are no more acks pending,
2563 * do a commit.
2565 if (state -> cur_unacked_updates == 0) {
2566 commit_leases();
2570 isc_result_t dhcp_failover_state_set_value (omapi_object_t *h,
2571 omapi_object_t *id,
2572 omapi_data_string_t *name,
2573 omapi_typed_data_t *value)
2575 isc_result_t status;
2577 if (h -> type != dhcp_type_failover_state)
2578 return ISC_R_INVALIDARG;
2580 /* This list of successful returns is completely wrong, but the
2581 fastest way to make dhcpctl do something vaguely sane when
2582 you try to change the local state. */
2584 if (!omapi_ds_strcmp (name, "name")) {
2585 return ISC_R_SUCCESS;
2586 } else if (!omapi_ds_strcmp (name, "partner-address")) {
2587 return ISC_R_SUCCESS;
2588 } else if (!omapi_ds_strcmp (name, "local-address")) {
2589 return ISC_R_SUCCESS;
2590 } else if (!omapi_ds_strcmp (name, "partner-port")) {
2591 return ISC_R_SUCCESS;
2592 } else if (!omapi_ds_strcmp (name, "local-port")) {
2593 return ISC_R_SUCCESS;
2594 } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
2595 return ISC_R_SUCCESS;
2596 } else if (!omapi_ds_strcmp (name, "mclt")) {
2597 return ISC_R_SUCCESS;
2598 } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
2599 return ISC_R_SUCCESS;
2600 } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
2601 return ISC_R_SUCCESS;
2602 } else if (!omapi_ds_strcmp (name, "partner-state")) {
2603 return ISC_R_SUCCESS;
2604 } else if (!omapi_ds_strcmp (name, "local-state")) {
2605 unsigned long l;
2606 status = omapi_get_int_value (&l, value);
2607 if (status != ISC_R_SUCCESS)
2608 return status;
2609 return dhcp_failover_set_state ((dhcp_failover_state_t *)h, l);
2610 } else if (!omapi_ds_strcmp (name, "partner-stos")) {
2611 return ISC_R_SUCCESS;
2612 } else if (!omapi_ds_strcmp (name, "local-stos")) {
2613 return ISC_R_SUCCESS;
2614 } else if (!omapi_ds_strcmp (name, "hierarchy")) {
2615 return ISC_R_SUCCESS;
2616 } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
2617 return ISC_R_SUCCESS;
2618 } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
2619 return ISC_R_SUCCESS;
2620 } else if (!omapi_ds_strcmp (name, "skew")) {
2621 return ISC_R_SUCCESS;
2622 } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
2623 return ISC_R_SUCCESS;
2624 } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
2625 return ISC_R_SUCCESS;
2628 if (h -> inner && h -> inner -> type -> set_value)
2629 return (*(h -> inner -> type -> set_value))
2630 (h -> inner, id, name, value);
2631 return ISC_R_NOTFOUND;
2634 void dhcp_failover_keepalive (void *vs)
2636 dhcp_failover_state_t *state = vs;
2639 void dhcp_failover_reconnect (void *vs)
2641 dhcp_failover_state_t *state = vs;
2642 isc_result_t status;
2644 #if defined (DEBUG_FAILOVER_TIMING)
2645 log_info ("dhcp_failover_reconnect");
2646 #endif
2647 /* If we already connected the other way, let the connection
2648 recovery code initiate any retry that may be required. */
2649 if (state -> link_to_peer)
2650 return;
2652 status = dhcp_failover_link_initiate ((omapi_object_t *)state);
2653 if (status != ISC_R_SUCCESS && status != ISC_R_INCOMPLETE) {
2654 log_info ("failover peer %s: %s", state -> name,
2655 isc_result_totext (status));
2656 #if defined (DEBUG_FAILOVER_TIMING)
2657 log_info ("add_timeout +90 %s",
2658 "dhcp_failover_listener_restart");
2659 #endif
2660 add_timeout (cur_time + 90,
2661 dhcp_failover_listener_restart, state,
2662 (tvref_t)dhcp_failover_state_reference,
2663 (tvunref_t)dhcp_failover_state_dereference);
2667 void dhcp_failover_startup_timeout (void *vs)
2669 dhcp_failover_state_t *state = vs;
2670 isc_result_t status;
2672 #if defined (DEBUG_FAILOVER_TIMING)
2673 log_info ("dhcp_failover_startup_timeout");
2674 #endif
2676 dhcp_failover_state_transition (state, "disconnect");
2679 void dhcp_failover_link_startup_timeout (void *vl)
2681 dhcp_failover_link_t *link = vl;
2682 isc_result_t status;
2683 omapi_object_t *p;
2685 for (p = (omapi_object_t *)link; p -> inner; p = p -> inner)
2687 for (; p; p = p -> outer)
2688 if (p -> type == omapi_type_connection)
2689 break;
2690 if (p) {
2691 log_info ("failover: link startup timeout");
2692 omapi_disconnect (p, 1);
2696 void dhcp_failover_listener_restart (void *vs)
2698 dhcp_failover_state_t *state = vs;
2699 isc_result_t status;
2701 #if defined (DEBUG_FAILOVER_TIMING)
2702 log_info ("dhcp_failover_listener_restart");
2703 #endif
2705 status = dhcp_failover_listen ((omapi_object_t *)state);
2706 if (status != ISC_R_SUCCESS) {
2707 log_info ("failover peer %s: %s", state -> name,
2708 isc_result_totext (status));
2709 #if defined (DEBUG_FAILOVER_TIMING)
2710 log_info ("add_timeout +90 %s",
2711 "dhcp_failover_listener_restart");
2712 #endif
2713 add_timeout (cur_time + 90,
2714 dhcp_failover_listener_restart, state,
2715 (tvref_t)dhcp_failover_state_reference,
2716 (tvunref_t)dhcp_failover_state_dereference);
2720 isc_result_t dhcp_failover_state_get_value (omapi_object_t *h,
2721 omapi_object_t *id,
2722 omapi_data_string_t *name,
2723 omapi_value_t **value)
2725 dhcp_failover_state_t *s;
2726 struct option_cache *oc;
2727 struct data_string ds;
2728 isc_result_t status;
2730 if (h -> type != dhcp_type_failover_state)
2731 return ISC_R_INVALIDARG;
2732 s = (dhcp_failover_state_t *)h;
2734 if (!omapi_ds_strcmp (name, "name")) {
2735 if (s -> name)
2736 return omapi_make_string_value (value,
2737 name, s -> name, MDL);
2738 return ISC_R_NOTFOUND;
2739 } else if (!omapi_ds_strcmp (name, "partner-address")) {
2740 oc = s -> partner.address;
2741 getaddr:
2742 memset (&ds, 0, sizeof ds);
2743 if (!evaluate_option_cache (&ds, (struct packet *)0,
2744 (struct lease *)0,
2745 (struct client_state *)0,
2746 (struct option_state *)0,
2747 (struct option_state *)0,
2748 &global_scope, oc, MDL)) {
2749 return ISC_R_NOTFOUND;
2751 status = omapi_make_const_value (value,
2752 name, ds.data, ds.len, MDL);
2753 /* Disgusting kludge: */
2754 if (oc == s -> me.address && !s -> server_identifier.len)
2755 data_string_copy (&s -> server_identifier, &ds, MDL);
2756 data_string_forget (&ds, MDL);
2757 return status;
2758 } else if (!omapi_ds_strcmp (name, "local-address")) {
2759 oc = s -> me.address;
2760 goto getaddr;
2761 } else if (!omapi_ds_strcmp (name, "partner-port")) {
2762 return omapi_make_int_value (value, name,
2763 s -> partner.port, MDL);
2764 } else if (!omapi_ds_strcmp (name, "local-port")) {
2765 return omapi_make_int_value (value,
2766 name, s -> me.port, MDL);
2767 } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
2768 return omapi_make_uint_value (value, name,
2769 s -> me.max_flying_updates,
2770 MDL);
2771 } else if (!omapi_ds_strcmp (name, "mclt")) {
2772 return omapi_make_uint_value (value, name, s -> mclt, MDL);
2773 } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
2774 return omapi_make_int_value (value, name,
2775 s -> load_balance_max_secs, MDL);
2776 } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
2777 if (s -> hba)
2778 return omapi_make_const_value (value, name,
2779 s -> hba, 32, MDL);
2780 return ISC_R_NOTFOUND;
2781 } else if (!omapi_ds_strcmp (name, "partner-state")) {
2782 return omapi_make_uint_value (value, name,
2783 s -> partner.state, MDL);
2784 } else if (!omapi_ds_strcmp (name, "local-state")) {
2785 return omapi_make_uint_value (value, name,
2786 s -> me.state, MDL);
2787 } else if (!omapi_ds_strcmp (name, "partner-stos")) {
2788 return omapi_make_int_value (value, name,
2789 s -> partner.stos, MDL);
2790 } else if (!omapi_ds_strcmp (name, "local-stos")) {
2791 return omapi_make_int_value (value, name,
2792 s -> me.stos, MDL);
2793 } else if (!omapi_ds_strcmp (name, "hierarchy")) {
2794 return omapi_make_uint_value (value, name, s -> i_am, MDL);
2795 } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
2796 return omapi_make_int_value (value, name,
2797 s -> last_packet_sent, MDL);
2798 } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
2799 return omapi_make_int_value (value, name,
2800 s -> last_timestamp_received,
2801 MDL);
2802 } else if (!omapi_ds_strcmp (name, "skew")) {
2803 return omapi_make_int_value (value, name, s -> skew, MDL);
2804 } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
2805 return omapi_make_uint_value (value, name,
2806 s -> me.max_response_delay,
2807 MDL);
2808 } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
2809 return omapi_make_int_value (value, name,
2810 s -> cur_unacked_updates, MDL);
2813 if (h -> inner && h -> inner -> type -> get_value)
2814 return (*(h -> inner -> type -> get_value))
2815 (h -> inner, id, name, value);
2816 return ISC_R_NOTFOUND;
2819 isc_result_t dhcp_failover_state_destroy (omapi_object_t *h,
2820 const char *file, int line)
2822 dhcp_failover_state_t *s;
2824 if (h -> type != dhcp_type_failover_state)
2825 return ISC_R_INVALIDARG;
2826 s = (dhcp_failover_state_t *)h;
2828 if (s -> link_to_peer)
2829 dhcp_failover_link_dereference (&s -> link_to_peer, file, line);
2830 if (s -> name) {
2831 dfree (s -> name, MDL);
2832 s -> name = (char *)0;
2834 if (s -> partner.address)
2835 option_cache_dereference (&s -> partner.address, file, line);
2836 if (s -> me.address)
2837 option_cache_dereference (&s -> me.address, file, line);
2838 if (s -> hba) {
2839 dfree (s -> hba, file, line);
2840 s -> hba = (u_int8_t *)0;
2842 if (s -> update_queue_head)
2843 lease_dereference (&s -> update_queue_head, file, line);
2844 if (s -> update_queue_tail)
2845 lease_dereference (&s -> update_queue_tail, file, line);
2846 if (s -> ack_queue_head)
2847 lease_dereference (&s -> ack_queue_head, file, line);
2848 if (s -> ack_queue_tail)
2849 lease_dereference (&s -> ack_queue_tail, file, line);
2850 if (s -> send_update_done)
2851 lease_dereference (&s -> send_update_done, file, line);
2852 if (s -> toack_queue_head)
2853 failover_message_dereference (&s -> toack_queue_head,
2854 file, line);
2855 if (s -> toack_queue_tail)
2856 failover_message_dereference (&s -> toack_queue_tail,
2857 file, line);
2858 return ISC_R_SUCCESS;
2861 /* Write all the published values associated with the object through the
2862 specified connection. */
2864 isc_result_t dhcp_failover_state_stuff (omapi_object_t *c,
2865 omapi_object_t *id,
2866 omapi_object_t *h)
2868 dhcp_failover_state_t *s;
2869 omapi_connection_object_t *conn;
2870 isc_result_t status;
2872 if (c -> type != omapi_type_connection)
2873 return ISC_R_INVALIDARG;
2874 conn = (omapi_connection_object_t *)c;
2876 if (h -> type != dhcp_type_failover_state)
2877 return ISC_R_INVALIDARG;
2878 s = (dhcp_failover_state_t *)h;
2880 status = omapi_connection_put_name (c, "name");
2881 if (status != ISC_R_SUCCESS)
2882 return status;
2883 status = omapi_connection_put_string (c, s -> name);
2884 if (status != ISC_R_SUCCESS)
2885 return status;
2887 status = omapi_connection_put_name (c, "partner-address");
2888 if (status != ISC_R_SUCCESS)
2889 return status;
2890 status = omapi_connection_put_uint32 (c, sizeof s -> partner.address);
2891 if (status != ISC_R_SUCCESS)
2892 return status;
2893 status = omapi_connection_copyin (c, (u_int8_t *)&s -> partner.address,
2894 sizeof s -> partner.address);
2895 if (status != ISC_R_SUCCESS)
2896 return status;
2898 status = omapi_connection_put_name (c, "partner-port");
2899 if (status != ISC_R_SUCCESS)
2900 return status;
2901 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2902 if (status != ISC_R_SUCCESS)
2903 return status;
2904 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> partner.port);
2905 if (status != ISC_R_SUCCESS)
2906 return status;
2908 status = omapi_connection_put_name (c, "local-address");
2909 if (status != ISC_R_SUCCESS)
2910 return status;
2911 status = omapi_connection_put_uint32 (c, sizeof s -> me.address);
2912 if (status != ISC_R_SUCCESS)
2913 return status;
2914 status = omapi_connection_copyin (c, (u_int8_t *)&s -> me.address,
2915 sizeof s -> me.address);
2916 if (status != ISC_R_SUCCESS)
2917 return status;
2919 status = omapi_connection_put_name (c, "local-port");
2920 if (status != ISC_R_SUCCESS)
2921 return status;
2922 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2923 if (status != ISC_R_SUCCESS)
2924 return status;
2925 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.port);
2926 if (status != ISC_R_SUCCESS)
2927 return status;
2929 status = omapi_connection_put_name (c, "max-outstanding-updates");
2930 if (status != ISC_R_SUCCESS)
2931 return status;
2932 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2933 if (status != ISC_R_SUCCESS)
2934 return status;
2935 status = omapi_connection_put_uint32 (c,
2936 s -> me.max_flying_updates);
2937 if (status != ISC_R_SUCCESS)
2938 return status;
2940 status = omapi_connection_put_name (c, "mclt");
2941 if (status != ISC_R_SUCCESS)
2942 return status;
2943 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2944 if (status != ISC_R_SUCCESS)
2945 return status;
2946 status = omapi_connection_put_uint32 (c, s -> mclt);
2947 if (status != ISC_R_SUCCESS)
2948 return status;
2950 status = omapi_connection_put_name (c, "load-balance-max-secs");
2951 if (status != ISC_R_SUCCESS)
2952 return status;
2953 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2954 if (status != ISC_R_SUCCESS)
2955 return status;
2956 status = (omapi_connection_put_uint32
2957 (c, (u_int32_t)s -> load_balance_max_secs));
2958 if (status != ISC_R_SUCCESS)
2959 return status;
2962 if (s -> hba) {
2963 status = omapi_connection_put_name (c, "load-balance-hba");
2964 if (status != ISC_R_SUCCESS)
2965 return status;
2966 status = omapi_connection_put_uint32 (c, 32);
2967 if (status != ISC_R_SUCCESS)
2968 return status;
2969 status = omapi_connection_copyin (c, s -> hba, 32);
2970 if (status != ISC_R_SUCCESS)
2971 return status;
2974 status = omapi_connection_put_name (c, "partner-state");
2975 if (status != ISC_R_SUCCESS)
2976 return status;
2977 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2978 if (status != ISC_R_SUCCESS)
2979 return status;
2980 status = omapi_connection_put_uint32 (c, s -> partner.state);
2981 if (status != ISC_R_SUCCESS)
2982 return status;
2984 status = omapi_connection_put_name (c, "local-state");
2985 if (status != ISC_R_SUCCESS)
2986 return status;
2987 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2988 if (status != ISC_R_SUCCESS)
2989 return status;
2990 status = omapi_connection_put_uint32 (c, s -> me.state);
2991 if (status != ISC_R_SUCCESS)
2992 return status;
2994 status = omapi_connection_put_name (c, "partner-stos");
2995 if (status != ISC_R_SUCCESS)
2996 return status;
2997 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
2998 if (status != ISC_R_SUCCESS)
2999 return status;
3000 status = omapi_connection_put_uint32 (c,
3001 (u_int32_t)s -> partner.stos);
3002 if (status != ISC_R_SUCCESS)
3003 return status;
3005 status = omapi_connection_put_name (c, "local-stos");
3006 if (status != ISC_R_SUCCESS)
3007 return status;
3008 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3009 if (status != ISC_R_SUCCESS)
3010 return status;
3011 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.stos);
3012 if (status != ISC_R_SUCCESS)
3013 return status;
3015 status = omapi_connection_put_name (c, "hierarchy");
3016 if (status != ISC_R_SUCCESS)
3017 return status;
3018 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3019 if (status != ISC_R_SUCCESS)
3020 return status;
3021 status = omapi_connection_put_uint32 (c, s -> i_am);
3022 if (status != ISC_R_SUCCESS)
3023 return status;
3025 status = omapi_connection_put_name (c, "last-packet-sent");
3026 if (status != ISC_R_SUCCESS)
3027 return status;
3028 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3029 if (status != ISC_R_SUCCESS)
3030 return status;
3031 status = (omapi_connection_put_uint32
3032 (c, (u_int32_t)s -> last_packet_sent));
3033 if (status != ISC_R_SUCCESS)
3034 return status;
3036 status = omapi_connection_put_name (c, "last-timestamp-received");
3037 if (status != ISC_R_SUCCESS)
3038 return status;
3039 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3040 if (status != ISC_R_SUCCESS)
3041 return status;
3042 status = (omapi_connection_put_uint32
3043 (c, (u_int32_t)s -> last_timestamp_received));
3044 if (status != ISC_R_SUCCESS)
3045 return status;
3047 status = omapi_connection_put_name (c, "skew");
3048 if (status != ISC_R_SUCCESS)
3049 return status;
3050 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3051 if (status != ISC_R_SUCCESS)
3052 return status;
3053 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> skew);
3054 if (status != ISC_R_SUCCESS)
3055 return status;
3057 status = omapi_connection_put_name (c, "max-response-delay");
3058 if (status != ISC_R_SUCCESS)
3059 return status;
3060 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3061 if (status != ISC_R_SUCCESS)
3062 return status;
3063 status = (omapi_connection_put_uint32
3064 (c, (u_int32_t)s -> me.max_response_delay));
3065 if (status != ISC_R_SUCCESS)
3066 return status;
3068 status = omapi_connection_put_name (c, "cur-unacked-updates");
3069 if (status != ISC_R_SUCCESS)
3070 return status;
3071 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3072 if (status != ISC_R_SUCCESS)
3073 return status;
3074 status = (omapi_connection_put_uint32
3075 (c, (u_int32_t)s -> cur_unacked_updates));
3076 if (status != ISC_R_SUCCESS)
3077 return status;
3079 if (h -> inner && h -> inner -> type -> stuff_values)
3080 return (*(h -> inner -> type -> stuff_values)) (c, id,
3081 h -> inner);
3082 return ISC_R_SUCCESS;
3085 isc_result_t dhcp_failover_state_lookup (omapi_object_t **sp,
3086 omapi_object_t *id,
3087 omapi_object_t *ref)
3089 omapi_value_t *tv = (omapi_value_t *)0;
3090 isc_result_t status;
3091 dhcp_failover_state_t *s;
3093 if (!ref)
3094 return ISC_R_NOKEYS;
3096 /* First see if we were sent a handle. */
3097 status = omapi_get_value_str (ref, id, "handle", &tv);
3098 if (status == ISC_R_SUCCESS) {
3099 status = omapi_handle_td_lookup (sp, tv -> value);
3101 omapi_value_dereference (&tv, MDL);
3102 if (status != ISC_R_SUCCESS)
3103 return status;
3105 /* Don't return the object if the type is wrong. */
3106 if ((*sp) -> type != dhcp_type_failover_state) {
3107 omapi_object_dereference (sp, MDL);
3108 return ISC_R_INVALIDARG;
3112 /* Look the failover state up by peer name. */
3113 status = omapi_get_value_str (ref, id, "name", &tv);
3114 if (status == ISC_R_SUCCESS) {
3115 for (s = failover_states; s; s = s -> next) {
3116 unsigned l = strlen (s -> name);
3117 if (l == tv -> value -> u.buffer.len &&
3118 !memcmp (s -> name,
3119 tv -> value -> u.buffer.value, l))
3120 break;
3122 omapi_value_dereference (&tv, MDL);
3124 /* If we already have a lease, and it's not the same one,
3125 then the query was invalid. */
3126 if (*sp && *sp != (omapi_object_t *)s) {
3127 omapi_object_dereference (sp, MDL);
3128 return ISC_R_KEYCONFLICT;
3129 } else if (!s) {
3130 if (*sp)
3131 omapi_object_dereference (sp, MDL);
3132 return ISC_R_NOTFOUND;
3133 } else if (!*sp)
3134 /* XXX fix so that hash lookup itself creates
3135 XXX the reference. */
3136 omapi_object_reference (sp, (omapi_object_t *)s, MDL);
3139 /* If we get to here without finding a lease, no valid key was
3140 specified. */
3141 if (!*sp)
3142 return ISC_R_NOKEYS;
3143 return ISC_R_SUCCESS;
3146 isc_result_t dhcp_failover_state_create (omapi_object_t **sp,
3147 omapi_object_t *id)
3149 return ISC_R_NOTIMPLEMENTED;
3152 isc_result_t dhcp_failover_state_remove (omapi_object_t *sp,
3153 omapi_object_t *id)
3155 return ISC_R_NOTIMPLEMENTED;
3158 int dhcp_failover_state_match (dhcp_failover_state_t *state,
3159 u_int8_t *addr, unsigned addrlen)
3161 struct option_cache *oc;
3162 struct data_string ds;
3163 int i;
3165 memset (&ds, 0, sizeof ds);
3166 if (evaluate_option_cache (&ds, (struct packet *)0,
3167 (struct lease *)0,
3168 (struct client_state *)0,
3169 (struct option_state *)0,
3170 (struct option_state *)0,
3171 &global_scope,
3172 state -> partner.address, MDL)) {
3173 for (i = 0; i + addrlen - 1 < ds.len; i += addrlen) {
3174 if (!memcmp (&ds.data [i],
3175 addr, addrlen)) {
3176 data_string_forget (&ds, MDL);
3177 return 1;
3180 data_string_forget (&ds, MDL);
3182 return 0;
3185 const char *dhcp_failover_reject_reason_print (int reason)
3187 switch (reason) {
3188 case FTR_ILLEGAL_IP_ADDR:
3189 return "Illegal IP address (not part of any address pool).";
3191 case FTR_FATAL_CONFLICT:
3192 return "Fatal conflict exists: address in use by other client.";
3194 case FTR_MISSING_BINDINFO:
3195 return "Missing binding information.";
3197 case FTR_TIMEMISMATCH:
3198 return "Connection rejected, time mismatch too great.";
3200 case FTR_INVALID_MCLT:
3201 return "Connection rejected, invalid MCLT.";
3203 case FTR_MISC_REJECT:
3204 return "Connection rejected, unknown reason.";
3206 case FTR_DUP_CONNECTION:
3207 return "Connection rejected, duplicate connection.";
3209 case FTR_INVALID_PARTNER:
3210 return "Connection rejected, invalid failover partner.";
3212 case FTR_TLS_UNSUPPORTED:
3213 return "TLS not supported.";
3215 case FTR_TLS_UNCONFIGURED:
3216 return "TLS supported but not configured.";
3218 case FTR_TLS_REQUIRED:
3219 return "TLS required but not supported by partner.";
3221 case FTR_DIGEST_UNSUPPORTED:
3222 return "Message digest not supported.";
3224 case FTR_DIGEST_UNCONFIGURED:
3225 return "Message digest not configured.";
3227 case FTR_VERSION_MISMATCH:
3228 return "Protocol version mismatch.";
3230 case FTR_MISSING_BIND_INFO:
3231 return "Missing binding information.";
3233 case FTR_OUTDATED_BIND_INFO:
3234 return "Outdated binding information.";
3236 case FTR_LESS_CRIT_BIND_INFO:
3237 return "Less critical binding information.";
3239 case FTR_NO_TRAFFIC:
3240 return "No traffic within sufficient time.";
3242 case FTR_HBA_CONFLICT:
3243 return "Hash bucket assignment conflict.";
3245 default:
3246 case FTR_UNKNOWN:
3247 return "Unknown: Error occurred but does not match any reason code.";
3251 const char *dhcp_failover_state_name_print (enum failover_state state)
3253 switch (state) {
3254 default:
3255 case unknown_state:
3256 return "unknown-state";
3258 case partner_down:
3259 return "partner-down";
3261 case normal:
3262 return "normal";
3264 case communications_interrupted:
3265 return "communications-interrupted";
3267 case resolution_interrupted:
3268 return "resolution-interrupted";
3270 case potential_conflict:
3271 return "potential-conflict";
3273 case recover:
3274 return "recover";
3276 case recover_done:
3277 return "recover-done";
3279 case recover_wait:
3280 return "recover-wait";
3282 case shut_down:
3283 return "shutdown";
3285 case paused:
3286 return "paused";
3288 case startup:
3289 return "startup";
3293 const char *dhcp_failover_message_name (unsigned type)
3295 switch (type) {
3296 case FTM_POOLREQ:
3297 return "pool-request";
3299 case FTM_POOLRESP:
3300 return "pool-response";
3302 case FTM_BNDUPD:
3303 return "bind-update";
3305 case FTM_BNDACK:
3306 return "bind-ack";
3308 case FTM_CONNECT:
3309 return "connect";
3311 case FTM_CONNECTACK:
3312 return "connect-ack";
3314 case FTM_UPDREQ:
3315 return "update-request";
3317 case FTM_UPDDONE:
3318 return "update-done";
3320 case FTM_UPDREQALL:
3321 return "update-request-all";
3323 case FTM_STATE:
3324 return "state";
3326 case FTM_CONTACT:
3327 return "contact";
3329 case FTM_DISCONNECT:
3330 return "disconnect";
3332 default:
3333 return "<unknown message type>";
3337 const char *dhcp_failover_option_name (unsigned type)
3339 switch (type) {
3340 case FTO_BINDING_STATUS:
3341 return "binding-status";
3343 case FTO_ASSIGNED_IP_ADDRESS:
3344 return "assigned-ip-address";
3346 case FTO_SERVER_ADDR:
3347 return "server-addr";
3349 case FTO_ADDRESSES_TRANSFERRED:
3350 return "addresses-transferred";
3352 case FTO_CLIENT_IDENTIFIER:
3353 return "client-identifier";
3355 case FTO_CHADDR:
3356 return "chaddr";
3358 case FTO_DDNS:
3359 return "ddns";
3361 case FTO_REJECT_REASON:
3362 return "reject-reason";
3364 case FTO_MESSAGE:
3365 return "message";
3367 case FTO_MCLT:
3368 return "mclt";
3370 case FTO_VENDOR_CLASS:
3371 return "vendor-class";
3373 case FTO_LEASE_EXPIRY:
3374 return "lease-expiry";
3376 case FTO_POTENTIAL_EXPIRY:
3377 return "potential-expiry";
3379 case FTO_GRACE_EXPIRY:
3380 return "grace-expiry";
3382 case FTO_CLTT:
3383 return "cltt";
3385 case FTO_STOS:
3386 return "stos";
3388 case FTO_SERVER_STATE:
3389 return "server-state";
3391 case FTO_SERVER_FLAGS:
3392 return "server-flags";
3394 case FTO_VENDOR_OPTIONS:
3395 return "vendor-options";
3397 case FTO_MAX_UNACKED:
3398 return "max-unacked";
3400 case FTO_RECEIVE_TIMER:
3401 return "receive-timer";
3403 case FTO_HBA:
3404 return "hba";
3406 case FTO_MESSAGE_DIGEST:
3407 return "message-digest";
3409 case FTO_PROTOCOL_VERSION:
3410 return "protocol-version";
3412 case FTO_TLS_REQUEST:
3413 return "tls-request";
3415 case FTO_TLS_REPLY:
3416 return "tls-reply";
3418 case FTO_REQUEST_OPTIONS:
3419 return "request-options";
3421 case FTO_REPLY_OPTIONS:
3422 return "reply-options";
3424 default:
3425 return "<unknown option>";
3429 failover_option_t *dhcp_failover_option_printf (unsigned code,
3430 char *obuf,
3431 unsigned *obufix,
3432 unsigned obufmax,
3433 const char *fmt, ...)
3435 va_list va;
3436 char tbuf [256];
3438 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3439 * It is unclear what the effects of truncation here are, or
3440 * how that condition should be handled. It seems that this
3441 * function is used for formatting messages in the failover
3442 * command channel. For now the safest thing is for
3443 * overflow-truncation to cause a fatal log.
3445 va_start (va, fmt);
3446 if (vsnprintf (tbuf, sizeof tbuf, fmt, va) >= sizeof tbuf)
3447 log_fatal ("%s: vsnprintf would truncate",
3448 "dhcp_failover_make_option");
3449 va_end (va);
3451 return dhcp_failover_make_option (code, obuf, obufix, obufmax,
3452 strlen (tbuf), tbuf);
3455 failover_option_t *dhcp_failover_make_option (unsigned code,
3456 char *obuf, unsigned *obufix,
3457 unsigned obufmax, ...)
3459 va_list va;
3460 struct failover_option_info *info;
3461 int i;
3462 unsigned size, count;
3463 unsigned val;
3464 u_int8_t *iaddr;
3465 unsigned ilen = 0;
3466 u_int8_t *bval;
3467 char *txt = NULL;
3468 #if defined (DEBUG_FAILOVER_MESSAGES)
3469 char tbuf [256];
3470 #endif
3472 /* Note that the failover_option structure is used differently on
3473 input than on output - on input, count is an element count, and
3474 on output it's the number of bytes total in the option, including
3475 the option code and option length. */
3476 failover_option_t option, *op;
3479 /* Bogus option code? */
3480 if (code < 1 || code > FTO_MAX || ft_options [code].type == FT_UNDEF) {
3481 return &null_failover_option;
3483 info = &ft_options [code];
3485 va_start (va, obufmax);
3487 /* Get the number of elements and the size of the buffer we need
3488 to allocate. */
3489 if (info -> type == FT_DDNS || info -> type == FT_DDNS1) {
3490 count = info -> type == FT_DDNS ? 1 : 2;
3491 size = va_arg (va, int) + count;
3492 } else {
3493 /* Find out how many items in this list. */
3494 if (info -> num_present)
3495 count = info -> num_present;
3496 else
3497 count = va_arg (va, int);
3499 /* Figure out size. */
3500 switch (info -> type) {
3501 case FT_UINT8:
3502 case FT_BYTES:
3503 case FT_DIGEST:
3504 size = count;
3505 break;
3507 case FT_TEXT_OR_BYTES:
3508 case FT_TEXT:
3509 txt = va_arg (va, char *);
3510 size = count;
3511 break;
3513 case FT_IPADDR:
3514 ilen = va_arg (va, unsigned);
3515 size = count * ilen;
3516 break;
3518 case FT_UINT32:
3519 size = count * 4;
3520 break;
3522 case FT_UINT16:
3523 size = count * 2;
3524 break;
3526 default:
3527 /* shouldn't get here. */
3528 log_fatal ("bogus type in failover_make_option: %d",
3529 info -> type);
3530 return &null_failover_option;
3534 size += 4;
3536 /* Allocate a buffer for the option. */
3537 option.count = size;
3538 option.data = dmalloc (option.count, MDL);
3539 if (!option.data) {
3540 va_end (va);
3541 return &null_failover_option;
3544 /* Put in the option code and option length. */
3545 putUShort (option.data, code);
3546 putUShort (&option.data [2], size - 4);
3548 #if defined (DEBUG_FAILOVER_MESSAGES)
3549 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3550 * It is unclear what the effects of truncation here are, or
3551 * how that condition should be handled. It seems that this
3552 * message may be sent over the failover command channel.
3553 * For now the safest thing is for overflow-truncation to cause
3554 * a fatal log.
3556 if (snprintf (tbuf, sizeof tbuf, " (%s<%d>", info -> name,
3557 option.count) >= sizeof tbuf)
3558 log_fatal ("dhcp_failover_make_option: tbuf overflow");
3559 failover_print (obuf, obufix, obufmax, tbuf);
3560 #endif
3562 /* Now put in the data. */
3563 switch (info -> type) {
3564 case FT_UINT8:
3565 for (i = 0; i < count; i++) {
3566 val = va_arg (va, unsigned);
3567 #if defined (DEBUG_FAILOVER_MESSAGES)
3568 /* %Audit% Cannot exceed 24 bytes. %2004.06.17,Safe% */
3569 sprintf (tbuf, " %d", val);
3570 failover_print (obuf, obufix, obufmax, tbuf);
3571 #endif
3572 option.data [i + 4] = val;
3574 break;
3576 case FT_IPADDR:
3577 for (i = 0; i < count; i++) {
3578 iaddr = va_arg (va, u_int8_t *);
3579 if (ilen != 4) {
3580 dfree (option.data, MDL);
3581 log_error ("IP addrlen=%d, should be 4.",
3582 ilen);
3583 va_end (va);
3584 return &null_failover_option;
3587 #if defined (DEBUG_FAILOVER_MESSAGES)
3588 /*%Audit% Cannot exceed 17 bytes. %2004.06.17,Safe%*/
3589 sprintf (tbuf, " %u.%u.%u.%u",
3590 iaddr [0], iaddr [1], iaddr [2], iaddr [3]);
3591 failover_print (obuf, obufix, obufmax, tbuf);
3592 #endif
3593 memcpy (&option.data [4 + i * ilen], iaddr, ilen);
3595 break;
3597 case FT_UINT32:
3598 for (i = 0; i < count; i++) {
3599 val = va_arg (va, unsigned);
3600 #if defined (DEBUG_FAILOVER_MESSAGES)
3601 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3602 sprintf (tbuf, " %d", val);
3603 failover_print (obuf, obufix, obufmax, tbuf);
3604 #endif
3605 putULong (&option.data [4 + i * 4], val);
3607 break;
3609 case FT_BYTES:
3610 case FT_DIGEST:
3611 bval = va_arg (va, u_int8_t *);
3612 #if defined (DEBUG_FAILOVER_MESSAGES)
3613 for (i = 0; i < count; i++) {
3614 /* 23 bytes plus nul, safe. */
3615 sprintf (tbuf, " %d", bval [i]);
3616 failover_print (obuf, obufix, obufmax, tbuf);
3618 #endif
3619 memcpy (&option.data [4], bval, count);
3620 break;
3622 /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
3623 terminated. Note that the caller should be careful not
3624 to provide a format and data that amount to more than 256
3625 bytes of data, since it will cause a fatal error. */
3626 case FT_TEXT_OR_BYTES:
3627 case FT_TEXT:
3628 #if defined (DEBUG_FAILOVER_MESSAGES)
3629 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3630 * It is unclear what the effects of truncation here are, or
3631 * how that condition should be handled. It seems that this
3632 * function is used for formatting messages in the failover
3633 * command channel. For now the safest thing is for
3634 * overflow-truncation to cause a fatal log.
3636 if (snprintf (tbuf, sizeof tbuf, "\"%s\"", txt) >= sizeof tbuf)
3637 log_fatal ("dhcp_failover_make_option: tbuf overflow");
3638 failover_print (obuf, obufix, obufmax, tbuf);
3639 #endif
3640 memcpy (&option.data [4], txt, count);
3641 break;
3643 case FT_DDNS:
3644 case FT_DDNS1:
3645 option.data [4] = va_arg (va, unsigned);
3646 if (count == 2)
3647 option.data [5] = va_arg (va, unsigned);
3648 bval = va_arg (va, u_int8_t *);
3649 memcpy (&option.data [4 + count], bval, size - count - 4);
3650 #if defined (DEBUG_FAILOVER_MESSAGES)
3651 for (i = 4; i < size; i++) {
3652 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3653 sprintf (tbuf, " %d", option.data [i]);
3654 failover_print (obuf, obufix, obufmax, tbuf);
3656 #endif
3657 break;
3659 case FT_UINT16:
3660 for (i = 0; i < count; i++) {
3661 val = va_arg (va, u_int32_t);
3662 #if defined (DEBUG_FAILOVER_MESSAGES)
3663 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3664 sprintf (tbuf, " %d", val);
3665 failover_print (obuf, obufix, obufmax, tbuf);
3666 #endif
3667 putUShort (&option.data [4 + i * 2], val);
3669 break;
3671 case FT_UNDEF:
3672 default:
3673 break;
3676 #if defined DEBUG_FAILOVER_MESSAGES
3677 failover_print (obuf, obufix, obufmax, ")");
3678 #endif
3679 va_end (va);
3681 /* Now allocate a place to store what we just set up. */
3682 op = dmalloc (sizeof (failover_option_t), MDL);
3683 if (!op) {
3684 dfree (option.data, MDL);
3685 return &null_failover_option;
3688 *op = option;
3689 return op;
3692 /* Send a failover message header. */
3694 isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link,
3695 omapi_object_t *connection,
3696 int msg_type, ...)
3698 unsigned count = 0;
3699 unsigned size = 0;
3700 int bad_option = 0;
3701 int opix = 0;
3702 va_list list;
3703 failover_option_t *option;
3704 unsigned char *opbuf;
3705 isc_result_t status = ISC_R_SUCCESS;
3706 unsigned char cbuf;
3708 /* Run through the argument list once to compute the length of
3709 the option portion of the message. */
3710 va_start (list, msg_type);
3711 while ((option = va_arg (list, failover_option_t *))) {
3712 if (option != &skip_failover_option)
3713 size += option -> count;
3714 if (option == &null_failover_option)
3715 bad_option = 1;
3717 va_end (list);
3719 /* Allocate an option buffer, unless we got an error. */
3720 if (!bad_option && size) {
3721 opbuf = dmalloc (size, MDL);
3722 if (!opbuf)
3723 status = ISC_R_NOMEMORY;
3724 } else
3725 opbuf = (unsigned char *)0;
3727 va_start (list, msg_type);
3728 while ((option = va_arg (list, failover_option_t *))) {
3729 if (option == &skip_failover_option)
3730 continue;
3731 if (!bad_option && opbuf)
3732 memcpy (&opbuf [opix],
3733 option -> data, option -> count);
3734 if (option != &null_failover_option &&
3735 option != &skip_failover_option) {
3736 opix += option -> count;
3737 dfree (option -> data, MDL);
3738 dfree (option, MDL);
3742 if (bad_option)
3743 return ISC_R_INVALIDARG;
3745 /* Now send the message header. */
3747 /* Message length. */
3748 status = omapi_connection_put_uint16 (connection, size + 12);
3749 if (status != ISC_R_SUCCESS)
3750 goto err;
3752 /* Message type. */
3753 cbuf = msg_type;
3754 status = omapi_connection_copyin (connection, &cbuf, 1);
3755 if (status != ISC_R_SUCCESS)
3756 goto err;
3758 /* Payload offset. */
3759 cbuf = 12;
3760 status = omapi_connection_copyin (connection, &cbuf, 1);
3761 if (status != ISC_R_SUCCESS)
3762 goto err;
3764 /* Current time. */
3765 status = omapi_connection_put_uint32 (connection, (u_int32_t)cur_time);
3766 if (status != ISC_R_SUCCESS)
3767 goto err;
3769 /* Transaction ID. */
3770 status = omapi_connection_put_uint32 (connection, link -> xid++);
3771 if (status != ISC_R_SUCCESS)
3772 goto err;
3775 /* Payload. */
3776 if (opbuf) {
3777 status = omapi_connection_copyin (connection, opbuf, size);
3778 if (status != ISC_R_SUCCESS)
3779 goto err;
3780 dfree (opbuf, MDL);
3782 if (link -> state_object &&
3783 link -> state_object -> link_to_peer == link) {
3784 #if defined (DEBUG_FAILOVER_TIMING)
3785 log_info ("add_timeout +%d %s",
3786 (int)(link -> state_object ->
3787 partner.max_response_delay) / 3,
3788 "dhcp_failover_send_contact");
3789 #endif
3790 add_timeout (cur_time +
3791 (int)(link -> state_object ->
3792 partner.max_response_delay) / 3,
3793 dhcp_failover_send_contact, link -> state_object,
3794 (tvref_t)dhcp_failover_state_reference,
3795 (tvunref_t)dhcp_failover_state_dereference);
3797 return status;
3799 err:
3800 if (opbuf)
3801 dfree (opbuf, MDL);
3802 log_info ("dhcp_failover_put_message: something went wrong.");
3803 omapi_disconnect (connection, 1);
3804 return status;
3807 void dhcp_failover_timeout (void *vstate)
3809 dhcp_failover_state_t *state = vstate;
3810 dhcp_failover_link_t *link;
3811 isc_result_t status;
3813 #if defined (DEBUG_FAILOVER_TIMING)
3814 log_info ("dhcp_failover_timeout");
3815 #endif
3817 if (!state || state -> type != dhcp_type_failover_state)
3818 return;
3819 link = state -> link_to_peer;
3820 if (!link ||
3821 !link -> outer ||
3822 link -> outer -> type != omapi_type_connection)
3823 return;
3825 log_error ("timeout waiting for failover peer %s", state -> name);
3827 /* If we haven't gotten a timely response, blow away the connection.
3828 This will cause the state to change automatically. */
3829 omapi_disconnect (link -> outer, 1);
3832 void dhcp_failover_send_contact (void *vstate)
3834 dhcp_failover_state_t *state = vstate;
3835 dhcp_failover_link_t *link;
3836 isc_result_t status;
3838 #if defined (DEBUG_FAILOVER_MESSAGES)
3839 char obuf [64];
3840 unsigned obufix = 0;
3842 # define FMA obuf, &obufix, sizeof obuf
3843 failover_print (FMA, "(contact");
3844 #else
3845 # define FMA (char *)0, (unsigned *)0, 0
3846 #endif
3848 #if defined (DEBUG_FAILOVER_TIMING)
3849 log_info ("dhcp_failover_send_contact");
3850 #endif
3852 if (!state || state -> type != dhcp_type_failover_state)
3853 return;
3854 link = state -> link_to_peer;
3855 if (!link ||
3856 !link -> outer ||
3857 link -> outer -> type != omapi_type_connection)
3858 return;
3860 status = (dhcp_failover_put_message
3861 (link, link -> outer,
3862 FTM_CONTACT,
3863 (failover_option_t *)0));
3865 #if defined (DEBUG_FAILOVER_MESSAGES)
3866 if (status != ISC_R_SUCCESS)
3867 failover_print (FMA, " (failed)");
3868 failover_print (FMA, ")");
3869 if (obufix) {
3870 log_debug ("%s", obuf);
3872 #endif
3873 return;
3876 isc_result_t dhcp_failover_send_state (dhcp_failover_state_t *state)
3878 dhcp_failover_link_t *link;
3879 isc_result_t status;
3881 #if defined (DEBUG_FAILOVER_MESSAGES)
3882 char obuf [64];
3883 unsigned obufix = 0;
3885 # define FMA obuf, &obufix, sizeof obuf
3886 failover_print (FMA, "(state");
3887 #else
3888 # define FMA (char *)0, (unsigned *)0, 0
3889 #endif
3891 if (!state || state -> type != dhcp_type_failover_state)
3892 return ISC_R_INVALIDARG;
3893 link = state -> link_to_peer;
3894 if (!link ||
3895 !link -> outer ||
3896 link -> outer -> type != omapi_type_connection)
3897 return ISC_R_INVALIDARG;
3899 status = (dhcp_failover_put_message
3900 (link, link -> outer,
3901 FTM_STATE,
3902 dhcp_failover_make_option (FTO_SERVER_STATE, FMA,
3903 (state -> me.state == startup
3904 ? state -> saved_state
3905 : state -> me.state)),
3906 dhcp_failover_make_option
3907 (FTO_SERVER_FLAGS, FMA,
3908 (state -> service_state == service_startup
3909 ? FTF_STARTUP : 0)),
3910 dhcp_failover_make_option (FTO_STOS, FMA, state -> me.stos),
3911 (failover_option_t *)0));
3913 #if defined (DEBUG_FAILOVER_MESSAGES)
3914 if (status != ISC_R_SUCCESS)
3915 failover_print (FMA, " (failed)");
3916 failover_print (FMA, ")");
3917 if (obufix) {
3918 log_debug ("%s", obuf);
3920 #endif
3921 return ISC_R_SUCCESS;
3924 /* Send a connect message. */
3926 isc_result_t dhcp_failover_send_connect (omapi_object_t *l)
3928 dhcp_failover_link_t *link;
3929 dhcp_failover_state_t *state;
3930 isc_result_t status;
3931 char hba [32];
3932 #if defined (DEBUG_FAILOVER_MESSAGES)
3933 char obuf [64];
3934 unsigned obufix = 0;
3936 # define FMA obuf, &obufix, sizeof obuf
3937 failover_print (FMA, "(connect");
3938 #else
3939 # define FMA (char *)0, (unsigned *)0, 0
3940 #endif
3942 if (!l || l -> type != dhcp_type_failover_link)
3943 return ISC_R_INVALIDARG;
3944 link = (dhcp_failover_link_t *)l;
3945 state = link -> state_object;
3946 if (!l -> outer || l -> outer -> type != omapi_type_connection)
3947 return ISC_R_INVALIDARG;
3949 status =
3950 (dhcp_failover_put_message
3951 (link, l -> outer,
3952 FTM_CONNECT,
3953 dhcp_failover_make_option (FTO_SERVER_ADDR, FMA,
3954 state -> server_identifier.len,
3955 state -> server_identifier.data),
3956 dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
3957 state -> me.max_flying_updates),
3958 dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
3959 state -> me.max_response_delay),
3960 dhcp_failover_option_printf (FTO_VENDOR_CLASS, FMA,
3961 "isc-%s", DHCP_VERSION),
3962 dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
3963 DHCP_FAILOVER_VERSION),
3964 dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
3965 0, 0),
3966 dhcp_failover_make_option (FTO_MCLT, FMA,
3967 state -> mclt),
3968 (state -> hba
3969 ? dhcp_failover_make_option (FTO_HBA, FMA, 32, state -> hba)
3970 : &skip_failover_option),
3971 (failover_option_t *)0));
3973 #if defined (DEBUG_FAILOVER_MESSAGES)
3974 if (status != ISC_R_SUCCESS)
3975 failover_print (FMA, " (failed)");
3976 failover_print (FMA, ")");
3977 if (obufix) {
3978 log_debug ("%s", obuf);
3980 #endif
3981 return status;
3984 isc_result_t dhcp_failover_send_connectack (omapi_object_t *l,
3985 dhcp_failover_state_t *state,
3986 int reason, const char *errmsg)
3988 dhcp_failover_link_t *link;
3989 isc_result_t status;
3990 #if defined (DEBUG_FAILOVER_MESSAGES)
3991 char obuf [64];
3992 unsigned obufix = 0;
3994 # define FMA obuf, &obufix, sizeof obuf
3995 failover_print (FMA, "(connectack");
3996 #else
3997 # define FMA (char *)0, (unsigned *)0, 0
3998 #endif
4000 if (!l || l -> type != dhcp_type_failover_link)
4001 return ISC_R_INVALIDARG;
4002 link = (dhcp_failover_link_t *)l;
4003 if (!l -> outer || l -> outer -> type != omapi_type_connection)
4004 return ISC_R_INVALIDARG;
4006 status =
4007 (dhcp_failover_put_message
4008 (link, l -> outer,
4009 FTM_CONNECTACK,
4010 (state
4011 ? (dhcp_failover_make_option
4012 (FTO_SERVER_ADDR, FMA,
4013 state -> server_identifier.len,
4014 state -> server_identifier.data))
4015 : &skip_failover_option),
4016 (state
4017 ? dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
4018 state -> me.max_flying_updates)
4019 : &skip_failover_option),
4020 (state
4021 ? dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
4022 state -> me.max_response_delay)
4023 : &skip_failover_option),
4024 dhcp_failover_option_printf (FTO_VENDOR_CLASS, FMA,
4025 "isc-%s", DHCP_VERSION),
4026 dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
4027 DHCP_FAILOVER_VERSION),
4028 dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
4029 0, 0),
4030 (reason
4031 ? dhcp_failover_make_option (FTO_REJECT_REASON,
4032 FMA, reason)
4033 : &skip_failover_option),
4034 (errmsg
4035 ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4036 strlen (errmsg), errmsg)
4037 : &skip_failover_option),
4038 (failover_option_t *)0));
4040 #if defined (DEBUG_FAILOVER_MESSAGES)
4041 if (status != ISC_R_SUCCESS)
4042 failover_print (FMA, " (failed)");
4043 failover_print (FMA, ")");
4044 if (obufix) {
4045 log_debug ("%s", obuf);
4047 #endif
4048 return status;
4051 isc_result_t dhcp_failover_send_disconnect (omapi_object_t *l,
4052 int reason,
4053 const char *message)
4055 dhcp_failover_link_t *link;
4056 dhcp_failover_state_t *state;
4057 isc_result_t status;
4058 #if defined (DEBUG_FAILOVER_MESSAGES)
4059 char obuf [64];
4060 unsigned obufix = 0;
4062 # define FMA obuf, &obufix, sizeof obuf
4063 failover_print (FMA, "(disconnect");
4064 #else
4065 # define FMA (char *)0, (unsigned *)0, 0
4066 #endif
4068 if (!l || l -> type != dhcp_type_failover_link)
4069 return ISC_R_INVALIDARG;
4070 link = (dhcp_failover_link_t *)l;
4071 state = link -> state_object;
4072 if (!l -> outer || l -> outer -> type != omapi_type_connection)
4073 return ISC_R_INVALIDARG;
4075 if (!message && reason)
4076 message = dhcp_failover_reject_reason_print (reason);
4078 status = (dhcp_failover_put_message
4079 (link, l -> outer,
4080 FTM_DISCONNECT,
4081 dhcp_failover_make_option (FTO_REJECT_REASON,
4082 FMA, reason),
4083 (message
4084 ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4085 strlen (message), message)
4086 : &skip_failover_option),
4087 (failover_option_t *)0));
4089 #if defined (DEBUG_FAILOVER_MESSAGES)
4090 if (status != ISC_R_SUCCESS)
4091 failover_print (FMA, " (failed)");
4092 failover_print (FMA, ")");
4093 if (obufix) {
4094 log_debug ("%s", obuf);
4096 #endif
4097 return status;
4100 /* Send a Bind Update message. */
4102 isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state,
4103 struct lease *lease)
4105 dhcp_failover_link_t *link;
4106 isc_result_t status;
4107 #if defined (DEBUG_FAILOVER_MESSAGES)
4108 char obuf [64];
4109 unsigned obufix = 0;
4111 # define FMA obuf, &obufix, sizeof obuf
4112 failover_print (FMA, "(bndupd");
4113 #else
4114 # define FMA (char *)0, (unsigned *)0, 0
4115 #endif
4117 if (!state -> link_to_peer ||
4118 state -> link_to_peer -> type != dhcp_type_failover_link)
4119 return ISC_R_INVALIDARG;
4120 link = (dhcp_failover_link_t *)state -> link_to_peer;
4122 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4123 return ISC_R_INVALIDARG;
4125 /* Send the update. */
4126 status = (dhcp_failover_put_message
4127 (link, link -> outer,
4128 FTM_BNDUPD,
4129 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
4130 lease -> ip_addr.len,
4131 lease -> ip_addr.iabuf),
4132 dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
4133 lease -> desired_binding_state),
4134 lease -> uid_len
4135 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
4136 lease -> uid_len,
4137 lease -> uid)
4138 : &skip_failover_option,
4139 lease -> hardware_addr.hlen
4140 ? dhcp_failover_make_option (FTO_CHADDR, FMA,
4141 lease -> hardware_addr.hlen,
4142 lease -> hardware_addr.hbuf)
4143 : &skip_failover_option,
4144 dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
4145 lease -> ends),
4146 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
4147 lease -> tstp),
4148 dhcp_failover_make_option (FTO_STOS, FMA,
4149 lease -> starts),
4150 dhcp_failover_make_option (FTO_CLTT, FMA,
4151 lease -> cltt),
4152 &skip_failover_option, /* XXX DDNS */
4153 &skip_failover_option, /* XXX request options */
4154 &skip_failover_option, /* XXX reply options */
4155 (failover_option_t *)0));
4157 #if defined (DEBUG_FAILOVER_MESSAGES)
4158 if (status != ISC_R_SUCCESS)
4159 failover_print (FMA, " (failed)");
4160 failover_print (FMA, ")");
4161 if (obufix) {
4162 log_debug ("%s", obuf);
4164 #endif
4165 return status;
4168 /* Send a Bind ACK message. */
4170 isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *state,
4171 failover_message_t *msg,
4172 int reason, const char *message)
4174 dhcp_failover_link_t *link;
4175 isc_result_t status;
4176 #if defined (DEBUG_FAILOVER_MESSAGES)
4177 char obuf [64];
4178 unsigned obufix = 0;
4180 # define FMA obuf, &obufix, sizeof obuf
4181 failover_print (FMA, "(bndack");
4182 #else
4183 # define FMA (char *)0, (unsigned *)0, 0
4184 #endif
4186 if (!state -> link_to_peer ||
4187 state -> link_to_peer -> type != dhcp_type_failover_link)
4188 return ISC_R_INVALIDARG;
4189 link = (dhcp_failover_link_t *)state -> link_to_peer;
4191 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4192 return ISC_R_INVALIDARG;
4194 if (!message && reason)
4195 message = dhcp_failover_reject_reason_print (reason);
4197 /* Send the update. */
4198 status = (dhcp_failover_put_message
4199 (link, link -> outer,
4200 FTM_BNDACK,
4201 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
4202 sizeof msg -> assigned_addr,
4203 &msg -> assigned_addr),
4204 dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
4205 msg -> binding_status),
4206 (msg -> options_present & FTB_CLIENT_IDENTIFIER)
4207 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
4208 msg -> client_identifier.count,
4209 msg -> client_identifier.data)
4210 : &skip_failover_option,
4211 (msg -> options_present & FTB_CHADDR)
4212 ? dhcp_failover_make_option (FTO_CHADDR, FMA,
4213 msg -> chaddr.count,
4214 msg -> chaddr.data)
4215 : &skip_failover_option,
4216 dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
4217 msg -> expiry),
4218 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
4219 msg -> potential_expiry),
4220 dhcp_failover_make_option (FTO_STOS, FMA,
4221 msg -> stos),
4222 dhcp_failover_make_option (FTO_CLTT, FMA,
4223 msg -> client_ltt),
4224 reason
4225 ? dhcp_failover_make_option (FTO_REJECT_REASON,
4226 FMA, reason)
4227 : &skip_failover_option,
4228 (message
4229 ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4230 strlen (message), message)
4231 : &skip_failover_option),
4232 &skip_failover_option, /* XXX DDNS */
4233 &skip_failover_option, /* XXX request options */
4234 &skip_failover_option, /* XXX reply options */
4235 (failover_option_t *)0));
4237 #if defined (DEBUG_FAILOVER_MESSAGES)
4238 if (status != ISC_R_SUCCESS)
4239 failover_print (FMA, " (failed)");
4240 failover_print (FMA, ")");
4241 if (obufix) {
4242 log_debug ("%s", obuf);
4244 #endif
4245 return status;
4248 isc_result_t dhcp_failover_send_poolreq (dhcp_failover_state_t *state)
4250 dhcp_failover_link_t *link;
4251 isc_result_t status;
4252 #if defined (DEBUG_FAILOVER_MESSAGES)
4253 char obuf [64];
4254 unsigned obufix = 0;
4256 # define FMA obuf, &obufix, sizeof obuf
4257 failover_print (FMA, "(poolreq");
4258 #else
4259 # define FMA (char *)0, (unsigned *)0, 0
4260 #endif
4262 if (!state -> link_to_peer ||
4263 state -> link_to_peer -> type != dhcp_type_failover_link)
4264 return ISC_R_INVALIDARG;
4265 link = (dhcp_failover_link_t *)state -> link_to_peer;
4267 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4268 return ISC_R_INVALIDARG;
4270 status = (dhcp_failover_put_message
4271 (link, link -> outer,
4272 FTM_POOLREQ,
4273 (failover_option_t *)0));
4275 #if defined (DEBUG_FAILOVER_MESSAGES)
4276 if (status != ISC_R_SUCCESS)
4277 failover_print (FMA, " (failed)");
4278 failover_print (FMA, ")");
4279 if (obufix) {
4280 log_debug ("%s", obuf);
4282 #endif
4283 return status;
4286 isc_result_t dhcp_failover_send_poolresp (dhcp_failover_state_t *state,
4287 int leases)
4289 dhcp_failover_link_t *link;
4290 isc_result_t status;
4291 #if defined (DEBUG_FAILOVER_MESSAGES)
4292 char obuf [64];
4293 unsigned obufix = 0;
4295 # define FMA obuf, &obufix, sizeof obuf
4296 failover_print (FMA, "(poolresp");
4297 #else
4298 # define FMA (char *)0, (unsigned *)0, 0
4299 #endif
4301 if (!state -> link_to_peer ||
4302 state -> link_to_peer -> type != dhcp_type_failover_link)
4303 return ISC_R_INVALIDARG;
4304 link = (dhcp_failover_link_t *)state -> link_to_peer;
4306 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4307 return ISC_R_INVALIDARG;
4309 status = (dhcp_failover_put_message
4310 (link, link -> outer,
4311 FTM_POOLRESP,
4312 dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED, FMA,
4313 leases),
4314 (failover_option_t *)0));
4316 #if defined (DEBUG_FAILOVER_MESSAGES)
4317 if (status != ISC_R_SUCCESS)
4318 failover_print (FMA, " (failed)");
4319 failover_print (FMA, ")");
4320 if (obufix) {
4321 log_debug ("%s", obuf);
4323 #endif
4324 return status;
4327 isc_result_t dhcp_failover_send_update_request (dhcp_failover_state_t *state)
4329 dhcp_failover_link_t *link;
4330 isc_result_t status;
4331 #if defined (DEBUG_FAILOVER_MESSAGES)
4332 char obuf [64];
4333 unsigned obufix = 0;
4335 # define FMA obuf, &obufix, sizeof obuf
4336 failover_print (FMA, "(updreq");
4337 #else
4338 # define FMA (char *)0, (unsigned *)0, 0
4339 #endif
4341 if (!state -> link_to_peer ||
4342 state -> link_to_peer -> type != dhcp_type_failover_link)
4343 return ISC_R_INVALIDARG;
4344 link = (dhcp_failover_link_t *)state -> link_to_peer;
4346 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4347 return ISC_R_INVALIDARG;
4349 if (state -> curUPD)
4350 return ISC_R_ALREADYRUNNING;
4352 status = (dhcp_failover_put_message
4353 (link, link -> outer,
4354 FTM_UPDREQ,
4355 (failover_option_t *)0));
4357 if (status == ISC_R_SUCCESS)
4358 state -> curUPD = FTM_UPDREQ;
4360 #if defined (DEBUG_FAILOVER_MESSAGES)
4361 if (status != ISC_R_SUCCESS)
4362 failover_print (FMA, " (failed)");
4363 failover_print (FMA, ")");
4364 if (obufix) {
4365 log_debug ("%s", obuf);
4367 #endif
4368 log_info ("Sent update request message to %s", state -> name);
4369 return status;
4372 isc_result_t dhcp_failover_send_update_request_all (dhcp_failover_state_t
4373 *state)
4375 dhcp_failover_link_t *link;
4376 isc_result_t status;
4377 #if defined (DEBUG_FAILOVER_MESSAGES)
4378 char obuf [64];
4379 unsigned obufix = 0;
4381 # define FMA obuf, &obufix, sizeof obuf
4382 failover_print (FMA, "(updreqall");
4383 #else
4384 # define FMA (char *)0, (unsigned *)0, 0
4385 #endif
4387 if (!state -> link_to_peer ||
4388 state -> link_to_peer -> type != dhcp_type_failover_link)
4389 return ISC_R_INVALIDARG;
4390 link = (dhcp_failover_link_t *)state -> link_to_peer;
4392 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4393 return ISC_R_INVALIDARG;
4395 /* If there is an UPDREQ in progress, then upgrade to UPDREQALL. */
4396 if (state -> curUPD && (state -> curUPD != FTM_UPDREQ))
4397 return ISC_R_ALREADYRUNNING;
4399 status = (dhcp_failover_put_message
4400 (link, link -> outer,
4401 FTM_UPDREQALL,
4402 (failover_option_t *)0));
4404 if (status == ISC_R_SUCCESS)
4405 state -> curUPD = FTM_UPDREQALL;
4407 #if defined (DEBUG_FAILOVER_MESSAGES)
4408 if (status != ISC_R_SUCCESS)
4409 failover_print (FMA, " (failed)");
4410 failover_print (FMA, ")");
4411 if (obufix) {
4412 log_debug ("%s", obuf);
4414 #endif
4415 log_info ("Sent update request all message to %s", state -> name);
4416 return status;
4419 isc_result_t dhcp_failover_send_update_done (dhcp_failover_state_t *state)
4421 dhcp_failover_link_t *link;
4422 isc_result_t status;
4423 #if defined (DEBUG_FAILOVER_MESSAGES)
4424 char obuf [64];
4425 unsigned obufix = 0;
4427 # define FMA obuf, &obufix, sizeof obuf
4428 failover_print (FMA, "(upddone");
4429 #else
4430 # define FMA (char *)0, (unsigned *)0, 0
4431 #endif
4433 if (!state -> link_to_peer ||
4434 state -> link_to_peer -> type != dhcp_type_failover_link)
4435 return ISC_R_INVALIDARG;
4436 link = (dhcp_failover_link_t *)state -> link_to_peer;
4438 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4439 return ISC_R_INVALIDARG;
4441 status = (dhcp_failover_put_message
4442 (link, link -> outer,
4443 FTM_UPDDONE,
4444 (failover_option_t *)0));
4446 #if defined (DEBUG_FAILOVER_MESSAGES)
4447 if (status != ISC_R_SUCCESS)
4448 failover_print (FMA, " (failed)");
4449 failover_print (FMA, ")");
4450 if (obufix) {
4451 log_debug ("%s", obuf);
4453 #endif
4455 log_info ("Sent update done message to %s", state -> name);
4457 /* There may be uncommitted leases at this point (since
4458 dhcp_failover_process_bind_ack() doesn't commit leases);
4459 commit the lease file. */
4460 commit_leases();
4462 return status;
4465 isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *state,
4466 failover_message_t *msg)
4468 struct lease *lt, *lease;
4469 struct iaddr ia;
4470 int reason = FTR_MISC_REJECT;
4471 const char *message;
4472 int new_binding_state;
4474 ia.len = sizeof msg -> assigned_addr;
4475 memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
4477 lease = (struct lease *)0;
4478 lt = (struct lease *)0;
4479 if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
4480 message = "unknown IP address";
4481 reason = FTR_ILLEGAL_IP_ADDR;
4482 goto bad;
4485 /* XXX check for conflicts. */
4487 /* Install the new info. */
4488 if (!lease_copy (&lt, lease, MDL)) {
4489 message = "no memory";
4490 goto bad;
4493 if (msg -> options_present & FTB_CHADDR) {
4494 if (msg->binding_status == FTS_ABANDONED) {
4495 message = "BNDUPD to ABANDONED with a CHADDR";
4496 goto bad;
4498 if (msg -> chaddr.count > sizeof lt -> hardware_addr.hbuf) {
4499 message = "chaddr to long";
4500 goto bad;
4502 lt -> hardware_addr.hlen = msg -> chaddr.count;
4503 memcpy (lt -> hardware_addr.hbuf, msg -> chaddr.data,
4504 msg -> chaddr.count);
4505 } else if (msg->binding_status == FTS_ACTIVE ||
4506 msg->binding_status == FTS_EXPIRED ||
4507 msg->binding_status == FTS_RELEASED) {
4508 message = "BNDUPD without CHADDR";
4509 goto bad;
4510 } else if (msg->binding_status == FTS_ABANDONED) {
4511 lt->hardware_addr.hlen = 0;
4512 if (lt->scope)
4513 binding_scope_dereference(&lt->scope, MDL);
4516 /* There is no explicit message content to indicate that the client
4517 * supplied no client-identifier. So if we don't hear of a value,
4518 * we discard the last one.
4520 if (msg->options_present & FTB_CLIENT_IDENTIFIER) {
4521 if (msg->binding_status == FTS_ABANDONED) {
4522 message = "BNDUPD to ABANDONED with client-id";
4523 goto bad;
4526 lt->uid_len = msg->client_identifier.count;
4528 /* Allocate the lt->uid buffer if we haven't already, or
4529 * re-allocate the lt-uid buffer if we have one that is not
4530 * large enough. Otherwise, just use the extant buffer.
4532 if (!lt->uid || lt->uid == lt->uid_buf ||
4533 lt->uid_len > lt->uid_max) {
4534 if (lt->uid && lt->uid != lt->uid_buf)
4535 dfree(lt->uid, MDL);
4537 if (lt->uid_len > sizeof(lt->uid_buf)) {
4538 lt->uid_max = lt->uid_len;
4539 lt->uid = dmalloc(lt->uid_len, MDL);
4540 if (!lt->uid) {
4541 message = "no memory";
4542 goto bad;
4544 } else {
4545 lt->uid_max = sizeof(lt->uid_buf);
4546 lt->uid = lt->uid_buf;
4549 memcpy (lt -> uid,
4550 msg -> client_identifier.data, lt -> uid_len);
4551 } else if (lt->uid && msg->binding_status != FTS_RESET &&
4552 msg->binding_status != FTS_FREE &&
4553 msg->binding_status != FTS_BACKUP) {
4554 if (lt->uid != lt->uid_buf)
4555 dfree (lt->uid, MDL);
4556 lt->uid = NULL;
4557 lt->uid_max = lt->uid_len = 0;
4560 /* If the lease was expired, also remove the stale binding scope. */
4561 if (lt->scope && lt->ends < cur_time)
4562 binding_scope_dereference(&lt->scope, MDL);
4564 /* XXX Times may need to be adjusted based on clock skew! */
4565 if (msg -> options_present & FTB_STOS) {
4566 lt -> starts = msg -> stos;
4568 if (msg -> options_present & FTB_LEASE_EXPIRY) {
4569 lt -> ends = msg -> expiry;
4571 if (msg -> options_present & FTB_CLTT) {
4572 lt -> cltt = msg -> client_ltt;
4574 if (msg -> options_present & FTB_POTENTIAL_EXPIRY) {
4575 lt -> tsfp = msg -> potential_expiry;
4578 if (msg -> options_present & FTB_BINDING_STATUS) {
4579 #if defined (DEBUG_LEASE_STATE_TRANSITIONS)
4580 log_info ("processing state transition for %s: %s to %s",
4581 piaddr (lease -> ip_addr),
4582 binding_state_print (lease -> binding_state),
4583 binding_state_print (msg -> binding_status));
4584 #endif
4586 /* If we're in normal state, make sure the state transition
4587 we got is valid. */
4588 if (state -> me.state == normal) {
4589 new_binding_state =
4590 (normal_binding_state_transition_check
4591 (lease, state, msg -> binding_status,
4592 msg -> potential_expiry));
4593 /* XXX if the transition the peer asked for isn't
4594 XXX allowed, maybe we should make the transition
4595 XXX into potential-conflict at this point. */
4596 } else {
4597 new_binding_state =
4598 (conflict_binding_state_transition_check
4599 (lease, state, msg -> binding_status,
4600 msg -> potential_expiry));
4602 if (new_binding_state != msg -> binding_status) {
4603 char outbuf [100];
4605 if (snprintf (outbuf, sizeof outbuf,
4606 "%s: invalid state transition: %s to %s",
4607 piaddr (lease -> ip_addr),
4608 binding_state_print (lease -> binding_state),
4609 binding_state_print (msg -> binding_status))
4610 >= sizeof outbuf)
4611 log_fatal ("%s: impossible outbuf overflow",
4612 "dhcp_failover_process_bind_update");
4614 dhcp_failover_send_bind_ack (state, msg,
4615 FTR_FATAL_CONFLICT,
4616 outbuf);
4617 goto out;
4619 if (new_binding_state == FTS_EXPIRED ||
4620 new_binding_state == FTS_RELEASED ||
4621 new_binding_state == FTS_RESET)
4622 lt -> next_binding_state = FTS_FREE;
4623 else
4624 lt -> next_binding_state = new_binding_state;
4625 msg -> binding_status = lt -> next_binding_state;
4628 /* Try to install the new information. */
4629 if (!supersede_lease (lease, lt, 0, 0, 0) ||
4630 !write_lease (lease)) {
4631 message = "database update failed";
4632 bad:
4633 dhcp_failover_send_bind_ack (state, msg, reason, message);
4634 } else {
4636 dhcp_failover_queue_ack (state, msg);
4639 out:
4640 if (lt)
4641 lease_dereference (&lt, MDL);
4642 if (lease)
4643 lease_dereference (&lease, MDL);
4645 return ISC_R_SUCCESS;
4648 isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *state,
4649 failover_message_t *msg)
4651 struct lease *lt = (struct lease *)0;
4652 struct lease *lease = (struct lease *)0;
4653 struct iaddr ia;
4654 const char *message = "no memory";
4656 ia.len = sizeof msg -> assigned_addr;
4657 memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
4659 if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
4660 message = "no such lease";
4661 goto bad;
4664 /* XXX check for conflicts. */
4665 if (msg -> options_present & FTB_REJECT_REASON) {
4666 log_error ("bind update on %s from %s rejected: %.*s",
4667 piaddr (ia), state -> name,
4668 (int)((msg -> options_present & FTB_MESSAGE)
4669 ? msg -> message.count
4670 : strlen (dhcp_failover_reject_reason_print
4671 (msg -> reject_reason))),
4672 (msg -> options_present & FTB_MESSAGE)
4673 ? (const char *)(msg -> message.data)
4674 : (dhcp_failover_reject_reason_print
4675 (msg -> reject_reason)));
4676 goto unqueue;
4679 /* XXX Times may need to be adjusted based on clock skew! */
4680 if (msg -> options_present & FTB_POTENTIAL_EXPIRY) {
4681 /* XXX it could be a problem to do this directly if the
4682 XXX lease is sorted by tsfp. */
4683 if ((lease -> binding_state == FTS_EXPIRED ||
4684 lease -> binding_state == FTS_RESET ||
4685 lease -> binding_state == FTS_RELEASED) &&
4686 (msg -> options_present & FTB_BINDING_STATUS) &&
4687 msg -> binding_status == FTS_FREE)
4689 lease -> tsfp = msg -> potential_expiry;
4690 lease -> next_binding_state = FTS_FREE;
4691 supersede_lease (lease, (struct lease *)0, 0, 0, 0);
4692 write_lease (lease);
4693 if (state -> me.state == normal)
4694 commit_leases ();
4695 } else {
4696 lease -> tsfp = msg -> potential_expiry;
4697 if ((lease -> desired_binding_state !=
4698 lease -> binding_state) &&
4699 (msg -> options_present & FTB_BINDING_STATUS) &&
4700 (msg -> binding_status ==
4701 lease -> desired_binding_state)) {
4702 lease -> next_binding_state =
4703 lease -> desired_binding_state;
4704 supersede_lease (lease,
4705 (struct lease *)0, 0, 0, 0);
4707 write_lease (lease);
4708 /* Commit the lease only after a two-second timeout,
4709 so that if we get a bunch of acks in quick
4710 successtion (e.g., when stealing leases from the
4711 secondary), we do not do an immediate commit for
4712 each one. */
4713 add_timeout (cur_time + 2,
4714 commit_leases_timeout, (void *)0, 0, 0);
4716 } else if (lease -> desired_binding_state != lease -> binding_state &&
4717 (msg -> options_present & FTB_BINDING_STATUS) &&
4718 msg -> binding_status == lease -> desired_binding_state) {
4719 lease -> next_binding_state = lease -> desired_binding_state;
4720 supersede_lease (lease, (struct lease *)0, 0, 0, 0);
4721 write_lease (lease);
4722 add_timeout (cur_time + 2, commit_leases_timeout,
4723 (void *)0, 0, 0);
4726 unqueue:
4727 dhcp_failover_ack_queue_remove (state, lease);
4729 /* If we are supposed to send an update done after we send
4730 this lease, go ahead and send it. */
4731 if (state -> send_update_done == lease) {
4732 lease_dereference (&state -> send_update_done, MDL);
4733 dhcp_failover_send_update_done (state);
4736 /* If there are updates pending, we've created space to send at
4737 least one. */
4738 dhcp_failover_send_updates (state);
4740 out:
4741 lease_dereference (&lease, MDL);
4742 if (lt)
4743 lease_dereference (&lt, MDL);
4745 return ISC_R_SUCCESS;
4747 bad:
4748 log_info ("bind update on %s from %s: %s.",
4749 piaddr (ia), state -> name, message);
4750 goto out;
4753 isc_result_t dhcp_failover_generate_update_queue (dhcp_failover_state_t *state,
4754 int everythingp)
4756 struct shared_network *s;
4757 struct pool *p;
4758 struct lease *l, *n;
4759 int i;
4760 struct lease **lptr [5];
4761 #define FREE_LEASES 0
4762 #define ACTIVE_LEASES 1
4763 #define EXPIRED_LEASES 2
4764 #define ABANDONED_LEASES 3
4765 #define BACKUP_LEASES 4
4767 /* First remove everything from the update and ack queues. */
4768 l = n = (struct lease *)0;
4769 if (state -> update_queue_head) {
4770 lease_reference (&l, state -> update_queue_head, MDL);
4771 lease_dereference (&state -> update_queue_head, MDL);
4772 do {
4773 l -> flags &= ~ON_UPDATE_QUEUE;
4774 if (l -> next_pending) {
4775 lease_reference (&n,
4776 l -> next_pending, MDL);
4777 lease_dereference (&l -> next_pending, MDL);
4779 lease_dereference (&l, MDL);
4780 if (n) {
4781 lease_reference (&l, n, MDL);
4782 lease_dereference (&n, MDL);
4784 } while (l);
4785 lease_dereference (&state -> update_queue_tail, MDL);
4788 if (state -> ack_queue_head) {
4789 lease_reference (&l, state -> ack_queue_head, MDL);
4790 lease_dereference (&state -> ack_queue_head, MDL);
4791 do {
4792 l -> flags &= ~ON_ACK_QUEUE;
4793 if (l -> next_pending) {
4794 lease_reference (&n,
4795 l -> next_pending, MDL);
4796 lease_dereference (&l -> next_pending, MDL);
4798 lease_dereference (&l, MDL);
4799 if (n) {
4800 lease_reference (&l, n, MDL);
4801 lease_dereference (&n, MDL);
4803 } while (l);
4804 lease_dereference (&state -> ack_queue_tail, MDL);
4806 if (state -> send_update_done)
4807 lease_dereference (&state -> send_update_done, MDL);
4808 state -> cur_unacked_updates = 0;
4810 /* Loop through each pool in each shared network and call the
4811 expiry routine on the pool. */
4812 for (s = shared_networks; s; s = s -> next) {
4813 for (p = s -> pools; p; p = p -> next) {
4814 lptr [FREE_LEASES] = &p -> free;
4815 lptr [ACTIVE_LEASES] = &p -> active;
4816 lptr [EXPIRED_LEASES] = &p -> expired;
4817 lptr [ABANDONED_LEASES] = &p -> abandoned;
4818 lptr [BACKUP_LEASES] = &p -> backup;
4820 for (i = FREE_LEASES; i <= BACKUP_LEASES; i++) {
4821 for (l = *(lptr [i]); l; l = l -> next) {
4822 if (p -> failover_peer == state &&
4823 ((everythingp &&
4824 (l -> starts != MIN_TIME ||
4825 l -> ends != MIN_TIME)) ||
4826 l -> tstp > l -> tsfp)) {
4827 l -> desired_binding_state = l -> binding_state;
4828 dhcp_failover_queue_update (l, 0);
4834 return ISC_R_SUCCESS;
4837 isc_result_t
4838 dhcp_failover_process_update_request (dhcp_failover_state_t *state,
4839 failover_message_t *msg)
4841 /* Generate a fresh update queue. */
4842 dhcp_failover_generate_update_queue (state, 0);
4844 /* If there's anything on the update queue (there shouldn't be
4845 anything on the ack queue), trigger an update done message
4846 when we get an ack for that lease. */
4847 if (state -> update_queue_tail) {
4848 lease_reference (&state -> send_update_done,
4849 state -> update_queue_tail, MDL);
4850 dhcp_failover_send_updates (state);
4851 log_info ("Update request from %s: sending update",
4852 state -> name);
4853 } else {
4854 /* Otherwise, there are no updates to send, so we can
4855 just send an UPDDONE message immediately. */
4856 dhcp_failover_send_update_done (state);
4857 log_info ("Update request from %s: nothing pending",
4858 state -> name);
4861 return ISC_R_SUCCESS;
4864 isc_result_t
4865 dhcp_failover_process_update_request_all (dhcp_failover_state_t *state,
4866 failover_message_t *msg)
4868 /* Generate a fresh update queue that includes every lease. */
4869 dhcp_failover_generate_update_queue (state, 1);
4871 if (state -> update_queue_tail) {
4872 lease_reference (&state -> send_update_done,
4873 state -> update_queue_tail, MDL);
4874 dhcp_failover_send_updates (state);
4875 log_info ("Update request all from %s: sending update",
4876 state -> name);
4877 } else {
4878 /* This should really never happen, but it could happen
4879 on a server that currently has no leases configured. */
4880 dhcp_failover_send_update_done (state);
4881 log_info ("Update request all from %s: nothing pending",
4882 state -> name);
4885 return ISC_R_SUCCESS;
4888 isc_result_t
4889 dhcp_failover_process_update_done (dhcp_failover_state_t *state,
4890 failover_message_t *msg)
4892 log_info ("failover peer %s: peer update completed.",
4893 state -> name);
4895 state -> curUPD = 0;
4897 switch (state -> me.state) {
4898 case unknown_state:
4899 case partner_down:
4900 case normal:
4901 case communications_interrupted:
4902 case resolution_interrupted:
4903 case shut_down:
4904 case paused:
4905 case recover_done:
4906 case startup:
4907 case recover_wait:
4908 break; /* shouldn't happen. */
4910 /* We got the UPDDONE, so we can go into normal state! */
4911 case potential_conflict:
4912 dhcp_failover_set_state (state, normal);
4913 break;
4915 case recover:
4916 /* Wait for MCLT to expire before moving to recover_done,
4917 except that if both peers come up in recover, there is
4918 no point in waiting for MCLT to expire - this probably
4919 indicates the initial startup of a newly-configured
4920 failover pair. */
4921 if (state -> me.stos + state -> mclt > cur_time &&
4922 state -> partner.state != recover &&
4923 state -> partner.state != recover_done) {
4924 dhcp_failover_set_state (state, recover_wait);
4925 #if defined (DEBUG_FAILOVER_TIMING)
4926 log_info ("add_timeout +%d %s",
4927 (int)(cur_time -
4928 state -> me.stos + state -> mclt),
4929 "dhcp_failover_recover_done");
4930 #endif
4931 add_timeout ((int)(state -> me.stos + state -> mclt),
4932 dhcp_failover_recover_done,
4933 state,
4934 (tvref_t)omapi_object_reference,
4935 (tvunref_t)
4936 omapi_object_dereference);
4937 } else
4938 dhcp_failover_recover_done (state);
4941 return ISC_R_SUCCESS;
4944 void dhcp_failover_recover_done (void *sp)
4946 dhcp_failover_state_t *state = sp;
4948 #if defined (DEBUG_FAILOVER_TIMING)
4949 log_info ("dhcp_failover_recover_done");
4950 #endif
4952 dhcp_failover_set_state (state, recover_done);
4955 #if defined (DEBUG_FAILOVER_MESSAGES)
4956 /* Print hunks of failover messages, doing line breaks as appropriate.
4957 Note that this assumes syslog is being used, rather than, e.g., the
4958 Windows NT logging facility, where just dumping the whole message in
4959 one hunk would be more appropriate. */
4961 void failover_print (char *obuf,
4962 unsigned *obufix, unsigned obufmax, const char *s)
4964 int len = strlen (s);
4966 while (len + *obufix + 1 >= obufmax) {
4967 log_debug ("%s", obuf);
4968 if (!*obufix) {
4969 log_debug ("%s", s);
4970 *obufix = 0;
4971 return;
4973 *obufix = 0;
4975 strcpy (&obuf [*obufix], s);
4976 *obufix += len;
4978 #endif /* defined (DEBUG_FAILOVER_MESSAGES) */
4980 /* Taken from draft-ietf-dhc-loadb-01.txt: */
4981 /* A "mixing table" of 256 distinct values, in pseudo-random order. */
4982 unsigned char loadb_mx_tbl[256] = {
4983 251, 175, 119, 215, 81, 14, 79, 191, 103, 49,
4984 181, 143, 186, 157, 0, 232, 31, 32, 55, 60,
4985 152, 58, 17, 237, 174, 70, 160, 144, 220, 90,
4986 57, 223, 59, 3, 18, 140, 111, 166, 203, 196,
4987 134, 243, 124, 95, 222, 179, 197, 65, 180, 48,
4988 36, 15, 107, 46, 233, 130, 165, 30, 123, 161,
4989 209, 23, 97, 16, 40, 91, 219, 61, 100, 10,
4990 210, 109, 250, 127, 22, 138, 29, 108, 244, 67,
4991 207, 9, 178, 204, 74, 98, 126, 249, 167, 116,
4992 34, 77, 193, 200, 121, 5, 20, 113, 71, 35,
4993 128, 13, 182, 94, 25, 226, 227, 199, 75, 27,
4994 41, 245, 230, 224, 43, 225, 177, 26, 155, 150,
4995 212, 142, 218, 115, 241, 73, 88, 105, 39, 114,
4996 62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
4997 154, 122, 12, 84, 82, 163, 44, 139, 228, 236,
4998 205, 242, 217, 11, 187, 146, 159, 64, 86, 239,
4999 195, 42, 106, 198, 118, 112, 184, 172, 87, 2,
5000 173, 117, 176, 229, 247, 253, 137, 185, 99, 164,
5001 102, 147, 45, 66, 231, 52, 141, 211, 194, 206,
5002 246, 238, 56, 110, 78, 248, 63, 240, 189, 93,
5003 92, 51, 53, 183, 19, 171, 72, 50, 33, 104,
5004 101, 69, 8, 252, 83, 120, 76, 135, 85, 54,
5005 202, 125, 188, 213, 96, 235, 136, 208, 162, 129,
5006 190, 132, 156, 38, 47, 1, 7, 254, 24, 4,
5007 216, 131, 89, 21, 28, 133, 37, 153, 149, 80,
5008 170, 68, 6, 169, 234, 151 };
5010 static unsigned char loadb_p_hash (const unsigned char *, unsigned);
5011 static unsigned char loadb_p_hash (const unsigned char *key, unsigned len)
5013 unsigned char hash = len;
5014 int i;
5015 for(i = len; i > 0; )
5016 hash = loadb_mx_tbl [hash ^ (key [--i])];
5017 return hash;
5020 int load_balance_mine (struct packet *packet, dhcp_failover_state_t *state)
5022 struct option_cache *oc;
5023 struct data_string ds;
5024 unsigned char hbaix;
5025 int hm;
5027 if (state -> load_balance_max_secs < ntohs (packet -> raw -> secs)) {
5028 return 1;
5031 /* If we don't have a hash bucket array, we can't tell if this
5032 one's ours, so we assume it's not. */
5033 if (!state -> hba)
5034 return 0;
5036 oc = lookup_option (&dhcp_universe, packet -> options,
5037 DHO_DHCP_CLIENT_IDENTIFIER);
5038 memset (&ds, 0, sizeof ds);
5039 if (oc &&
5040 evaluate_option_cache (&ds, packet, (struct lease *)0,
5041 (struct client_state *)0,
5042 packet -> options, (struct option_state *)0,
5043 &global_scope, oc, MDL)) {
5044 hbaix = loadb_p_hash (ds.data, ds.len);
5045 } else {
5046 hbaix = loadb_p_hash (packet -> raw -> chaddr,
5047 packet -> raw -> hlen);
5049 hm = (state -> hba [hbaix / 8] & (1 << (hbaix & 3)));
5050 if (state -> i_am == primary)
5051 return hm;
5052 else
5053 return !hm;
5056 /* This deals with what to do with bind updates when
5057 we're in the normal state
5059 Note that tsfp had better be set from the latest bind update
5060 _before_ this function is called! */
5062 binding_state_t
5063 normal_binding_state_transition_check (struct lease *lease,
5064 dhcp_failover_state_t *state,
5065 binding_state_t binding_state,
5066 u_int32_t tsfp)
5068 binding_state_t new_state;
5070 /* If there is no transition, it's no problem. */
5071 if (binding_state == lease -> binding_state)
5072 return binding_state;
5074 switch (lease -> binding_state) {
5075 case FTS_FREE:
5076 case FTS_ABANDONED:
5077 switch (binding_state) {
5078 case FTS_ACTIVE:
5079 case FTS_ABANDONED:
5080 case FTS_BACKUP:
5081 case FTS_EXPIRED:
5082 case FTS_RELEASED:
5083 case FTS_RESET:
5084 /* If the lease was free, and our peer is primary,
5085 then it can make it active, or abandoned, or
5086 backup. Abandoned is treated like free in
5087 this case. */
5088 if (state -> i_am == secondary)
5089 return binding_state;
5091 /* Otherwise, it can't legitimately do any sort of
5092 state transition. Because the lease was free,
5093 and the error has already been made, we allow the
5094 peer to change its state anyway, but log a warning
5095 message in hopes that the error will be fixed. */
5096 case FTS_FREE: /* for compiler */
5097 new_state = binding_state;
5098 goto out;
5100 default:
5101 log_fatal ("Impossible case at %s:%d.", MDL);
5102 return FTS_RESET;
5104 case FTS_ACTIVE:
5105 /* The secondary can't change the state of an active
5106 lease. */
5107 if (state -> i_am == primary) {
5108 /* Except that the client may send the DHCPRELEASE
5109 to the secondary, and we have to accept that. */
5110 if (binding_state == FTS_RELEASED)
5111 return binding_state;
5112 new_state = lease -> binding_state;
5113 goto out;
5116 /* So this is only for transitions made by the primary: */
5117 switch (binding_state) {
5118 case FTS_FREE:
5119 case FTS_BACKUP:
5120 /* Can't set a lease to free or backup until the
5121 peer agrees that it's expired. */
5122 if (tsfp > cur_time) {
5123 new_state = lease -> binding_state;
5124 goto out;
5126 return binding_state;
5128 case FTS_EXPIRED:
5129 /* XXX 65 should be the clock skew between the peers
5130 XXX plus a fudge factor. This code will result
5131 XXX in problems if MCLT is really short or the
5132 XXX max-lease-time is really short (less than the
5133 XXX fudge factor. */
5134 if (lease -> ends - 65 > cur_time) {
5135 new_state = lease -> binding_state;
5136 goto out;
5139 case FTS_RELEASED:
5140 case FTS_ABANDONED:
5141 case FTS_RESET:
5142 case FTS_ACTIVE:
5143 return binding_state;
5145 default:
5146 log_fatal ("Impossible case at %s:%d.", MDL);
5147 return FTS_RESET;
5149 break;
5150 case FTS_EXPIRED:
5151 switch (binding_state) {
5152 case FTS_BACKUP:
5153 case FTS_FREE:
5154 /* Can't set a lease to free or backup until the
5155 peer agrees that it's expired. */
5156 if (tsfp > cur_time) {
5157 new_state = lease -> binding_state;
5158 goto out;
5160 return binding_state;
5162 case FTS_ACTIVE:
5163 case FTS_RELEASED:
5164 case FTS_ABANDONED:
5165 case FTS_RESET:
5166 case FTS_EXPIRED:
5167 return binding_state;
5169 default:
5170 log_fatal ("Impossible case at %s:%d.", MDL);
5171 return FTS_RESET;
5173 case FTS_RELEASED:
5174 switch (binding_state) {
5175 case FTS_FREE:
5176 case FTS_BACKUP:
5178 /* These are invalid state transitions - should we
5179 prevent them? */
5180 case FTS_EXPIRED:
5181 case FTS_ABANDONED:
5182 case FTS_RESET:
5183 case FTS_ACTIVE:
5184 case FTS_RELEASED:
5185 return binding_state;
5187 default:
5188 log_fatal ("Impossible case at %s:%d.", MDL);
5189 return FTS_RESET;
5191 case FTS_RESET:
5192 switch (binding_state) {
5193 case FTS_FREE:
5194 case FTS_BACKUP:
5195 /* Can't set a lease to free or backup until the
5196 peer agrees that it's expired. */
5197 if (tsfp > cur_time) {
5198 new_state = lease -> binding_state;
5199 goto out;
5201 return binding_state;
5203 case FTS_ACTIVE:
5204 case FTS_EXPIRED:
5205 case FTS_RELEASED:
5206 case FTS_ABANDONED:
5207 case FTS_RESET:
5208 return binding_state;
5210 default:
5211 log_fatal ("Impossible case at %s:%d.", MDL);
5212 return FTS_RESET;
5214 case FTS_BACKUP:
5215 switch (binding_state) {
5216 case FTS_ACTIVE:
5217 case FTS_ABANDONED:
5218 case FTS_EXPIRED:
5219 case FTS_RELEASED:
5220 case FTS_RESET:
5221 /* If the lease was in backup, and our peer
5222 is secondary, then it can make it active
5223 or abandoned. */
5224 if (state -> i_am == primary)
5225 return binding_state;
5227 /* Either the primary or the secondary can
5228 reasonably move a lease from the backup
5229 state to the free state. */
5230 case FTS_FREE:
5231 return binding_state;
5233 case FTS_BACKUP:
5234 new_state = lease -> binding_state;
5235 goto out;
5237 default:
5238 log_fatal ("Impossible case at %s:%d.", MDL);
5239 return FTS_RESET;
5242 default:
5243 log_fatal ("Impossible case at %s:%d.", MDL);
5244 return FTS_RESET;
5246 out:
5247 return new_state;
5250 /* Determine whether the state transition is okay when we're potentially
5251 in conflict with the peer. */
5252 binding_state_t
5253 conflict_binding_state_transition_check (struct lease *lease,
5254 dhcp_failover_state_t *state,
5255 binding_state_t binding_state,
5256 u_int32_t tsfp)
5258 binding_state_t new_state;
5260 /* If there is no transition, it's no problem. */
5261 if (binding_state == lease -> binding_state)
5262 new_state = binding_state;
5263 else {
5264 switch (lease -> binding_state) {
5265 /* If we think the lease is not in use, then the
5266 state into which the partner put it is just fine,
5267 whatever it is. */
5268 case FTS_FREE:
5269 case FTS_ABANDONED:
5270 case FTS_EXPIRED:
5271 case FTS_RELEASED:
5272 case FTS_RESET:
5273 case FTS_BACKUP:
5274 new_state = binding_state;
5275 break;
5277 /* If we think the lease *is* in use, then we're not
5278 going to take the partner's change if the partner
5279 thinks it's free. */
5280 case FTS_ACTIVE:
5281 switch (binding_state) {
5282 case FTS_FREE:
5283 case FTS_BACKUP:
5284 case FTS_ABANDONED:
5285 new_state = lease -> binding_state;
5286 break;
5288 case FTS_EXPIRED:
5289 case FTS_RELEASED:
5290 case FTS_RESET:
5291 if (lease -> ends > cur_time)
5292 new_state =
5293 lease -> binding_state;
5294 else
5295 new_state = binding_state;
5296 break;
5298 case FTS_ACTIVE:
5299 new_state = binding_state;
5300 break;
5302 default:
5303 log_fatal ("Impossible case at %s:%d.", MDL);
5304 return FTS_RESET;
5306 break;
5308 default:
5309 log_fatal ("Impossible case at %s:%d.", MDL);
5310 return FTS_RESET;
5313 return new_state;
5316 /* We can reallocate a lease under the following circumstances:
5318 (1) It belongs to us - it's FTS_FREE, and we're primary, or it's
5319 FTS_BACKUP, and we're secondary.
5320 (2) We're in partner_down, and the lease is not active, and we
5321 can be sure that the other server didn't make it active.
5322 We can only be sure that the server didn't make it active
5323 when we are in the partner_down state and one of the following
5324 two conditions holds:
5325 (a) in the case that the time sent from the peer is earlier than
5326 the time we entered the partner_down state, at least MCLT has
5327 gone by since we entered partner_down, or
5328 (b) in the case that the time sent from the peer is later than
5329 the time when we entered partner_down, the current time is
5330 later than the time sent from the peer by at least MCLT. */
5332 int lease_mine_to_reallocate (struct lease *lease)
5334 dhcp_failover_state_t *peer;
5336 if (lease && lease -> pool &&
5337 lease -> pool -> failover_peer) {
5338 peer = lease -> pool -> failover_peer;
5339 switch (lease -> binding_state) {
5340 case FTS_ACTIVE:
5341 return 0;
5343 case FTS_FREE:
5344 if (peer -> i_am == primary)
5345 return 1;
5346 if (peer -> service_state == service_partner_down &&
5347 (lease -> tsfp < peer -> me.stos
5348 ? peer -> me.stos + peer -> mclt < cur_time
5349 : lease -> tsfp + peer -> mclt < cur_time))
5350 return 1;
5351 return 0;
5353 case FTS_ABANDONED:
5354 case FTS_RESET:
5355 case FTS_RELEASED:
5356 case FTS_EXPIRED:
5357 if (peer -> service_state == service_partner_down &&
5358 (lease -> tsfp < peer -> me.stos
5359 ? peer -> me.stos + peer -> mclt < cur_time
5360 : lease -> tsfp + peer -> mclt < cur_time))
5361 return 1;
5362 return 0;
5363 case FTS_BACKUP:
5364 if (peer -> i_am == secondary)
5365 return 1;
5366 if (peer -> service_state == service_partner_down &&
5367 (lease -> tsfp < peer -> me.stos
5368 ? peer -> me.stos + peer -> mclt < cur_time
5369 : lease -> tsfp + peer -> mclt < cur_time))
5370 return 1;
5371 return 0;
5373 return 0;
5375 if (lease)
5376 return !(lease -> binding_state != FTS_FREE &&
5377 lease -> binding_state != FTS_BACKUP);
5378 else
5379 return 0;
5382 static isc_result_t failover_message_reference (failover_message_t **mp,
5383 failover_message_t *m,
5384 const char *file, int line)
5386 *mp = m;
5387 m -> refcnt++;
5388 return ISC_R_SUCCESS;
5391 static isc_result_t failover_message_dereference (failover_message_t **mp,
5392 const char *file, int line)
5394 failover_message_t *m;
5395 m = (*mp);
5396 m -> refcnt--;
5397 if (m -> refcnt == 0) {
5398 if (m -> next)
5399 failover_message_dereference (&m -> next,
5400 file, line);
5401 if (m -> chaddr.data)
5402 dfree (m -> chaddr.data, file, line);
5403 if (m -> client_identifier.data)
5404 dfree (m -> client_identifier.data, file, line);
5405 if (m -> hba.data)
5406 dfree (m -> hba.data, file, line);
5407 if (m -> message.data)
5408 dfree (m -> message.data, file, line);
5409 if (m -> reply_options.data)
5410 dfree (m -> reply_options.data, file, line);
5411 if (m -> request_options.data)
5412 dfree (m -> request_options.data, file, line);
5413 if (m -> vendor_class.data)
5414 dfree (m -> vendor_class.data, file, line);
5415 if (m -> vendor_options.data)
5416 dfree (m -> vendor_options.data, file, line);
5417 if (m -> ddns.data)
5418 dfree (m -> ddns.data, file, line);
5419 dfree (*mp, file, line);
5421 *mp = 0;
5422 return ISC_R_SUCCESS;
5425 OMAPI_OBJECT_ALLOC (dhcp_failover_state, dhcp_failover_state_t,
5426 dhcp_type_failover_state)
5427 OMAPI_OBJECT_ALLOC (dhcp_failover_listener, dhcp_failover_listener_t,
5428 dhcp_type_failover_listener)
5429 OMAPI_OBJECT_ALLOC (dhcp_failover_link, dhcp_failover_link_t,
5430 dhcp_type_failover_link)
5431 #endif /* defined (FAILOVER_PROTOCOL) */
5433 const char *binding_state_print (enum failover_state state)
5435 switch (state) {
5436 case FTS_FREE:
5437 return "free";
5438 break;
5440 case FTS_ACTIVE:
5441 return "active";
5442 break;
5444 case FTS_EXPIRED:
5445 return "expired";
5446 break;
5448 case FTS_RELEASED:
5449 return "released";
5450 break;
5452 case FTS_ABANDONED:
5453 return "abandoned";
5454 break;
5456 case FTS_RESET:
5457 return "reset";
5458 break;
5460 case FTS_BACKUP:
5461 return "backup";
5462 break;
5464 default:
5465 return "unknown";
5466 break;