3 Failover protocol support code... */
6 * Copyright (c) 2004-2008 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.
23 * Redwood City, CA 94063
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''.
36 #include <omapip/omapip_p.h>
38 #if defined (FAILOVER_PROTOCOL)
39 dhcp_failover_state_t
*failover_states
;
40 static isc_result_t
do_a_failover_option (omapi_object_t
*,
41 dhcp_failover_link_t
*);
42 dhcp_failover_listener_t
*failover_listeners
;
44 static isc_result_t
failover_message_reference (failover_message_t
**,
46 const char *file
, int line
);
47 static isc_result_t
failover_message_dereference (failover_message_t
**,
48 const char *file
, int line
);
50 static void dhcp_failover_pool_balance(dhcp_failover_state_t
*state
);
51 static void dhcp_failover_pool_reqbalance(dhcp_failover_state_t
*state
);
52 static int dhcp_failover_pool_dobalance(dhcp_failover_state_t
*state
,
53 isc_boolean_t
*sendreq
);
54 static INLINE
int secondary_not_hoarding(dhcp_failover_state_t
*state
,
58 void dhcp_failover_startup ()
60 dhcp_failover_state_t
*state
;
64 for (state
= failover_states
; state
; state
= state
-> next
) {
65 dhcp_failover_state_transition (state
, "startup");
67 if (state
-> pool_count
== 0) {
68 log_error ("failover peer declaration with no %s",
70 log_error ("In order to use failover, you MUST %s",
71 "refer to your main failover declaration");
72 log_error ("in each pool declaration. You MUST %s",
73 "NOT use range declarations outside");
74 log_fatal ("of pool declarations.");
76 /* In case the peer is already running, immediately try
77 to establish a connection with it. */
78 status
= dhcp_failover_link_initiate ((omapi_object_t
*)state
);
79 if (status
!= ISC_R_SUCCESS
&& status
!= ISC_R_INCOMPLETE
) {
80 #if defined (DEBUG_FAILOVER_TIMING)
81 log_info ("add_timeout +90 dhcp_failover_reconnect");
83 tv
. tv_sec
= cur_time
+ 90;
86 dhcp_failover_reconnect
, state
,
88 dhcp_failover_state_reference
,
90 dhcp_failover_state_dereference
);
91 log_error ("failover peer %s: %s", state
-> name
,
92 isc_result_totext (status
));
95 status
= (dhcp_failover_listen
96 ((omapi_object_t
*)state
));
97 if (status
!= ISC_R_SUCCESS
) {
98 #if defined (DEBUG_FAILOVER_TIMING)
99 log_info ("add_timeout +90 %s",
100 "dhcp_failover_listener_restart");
102 tv
. tv_sec
= cur_time
+ 90;
105 dhcp_failover_listener_restart
,
107 (tvref_t
)omapi_object_reference
,
108 (tvunref_t
)omapi_object_dereference
);
113 int dhcp_failover_write_all_states ()
115 dhcp_failover_state_t
*state
;
117 for (state
= failover_states
; state
; state
= state
-> next
) {
118 if (!write_failover_state (state
))
124 isc_result_t
enter_failover_peer (peer
)
125 dhcp_failover_state_t
*peer
;
127 dhcp_failover_state_t
*dup
= (dhcp_failover_state_t
*)0;
130 status
= find_failover_peer (&dup
, peer
-> name
, MDL
);
131 if (status
== ISC_R_NOTFOUND
) {
132 if (failover_states
) {
133 dhcp_failover_state_reference (&peer
-> next
,
134 failover_states
, MDL
);
135 dhcp_failover_state_dereference (&failover_states
,
138 dhcp_failover_state_reference (&failover_states
, peer
, MDL
);
139 return ISC_R_SUCCESS
;
141 dhcp_failover_state_dereference (&dup
, MDL
);
142 if (status
== ISC_R_SUCCESS
)
147 isc_result_t
find_failover_peer (peer
, name
, file
, line
)
148 dhcp_failover_state_t
**peer
;
153 dhcp_failover_state_t
*p
;
155 for (p
= failover_states
; p
; p
= p
-> next
)
156 if (!strcmp (name
, p
-> name
))
159 return dhcp_failover_state_reference (peer
, p
, file
, line
);
160 return ISC_R_NOTFOUND
;
163 /* The failover protocol has three objects associated with it. For
164 each failover partner declaration in the dhcpd.conf file, primary
165 or secondary, there is a failover_state object. For any primary or
166 secondary state object that has a connection to its peer, there is
167 also a failover_link object, which has its own input state separate
168 from the failover protocol state for managing the actual bytes
169 coming in off the wire. Finally, there will be one listener object
170 for every distinct port number associated with a secondary
171 failover_state object. Normally all secondary failover_state
172 objects are expected to listen on the same port number, so there
173 need be only one listener object, but if different port numbers are
174 specified for each failover object, there could be as many as one
175 listener object for each secondary failover_state object. */
177 /* This, then, is the implementation of the failover link object. */
179 isc_result_t
dhcp_failover_link_initiate (omapi_object_t
*h
)
182 dhcp_failover_link_t
*obj
;
183 dhcp_failover_state_t
*state
;
186 struct data_string ds
;
187 omapi_addr_list_t
*addrs
= (omapi_addr_list_t
*)0;
188 omapi_addr_t local_addr
;
190 /* Find the failover state in the object chain. */
191 for (o
= h
; o
-> outer
; o
= o
-> outer
)
193 for (; o
; o
= o
-> inner
) {
194 if (o
-> type
== dhcp_type_failover_state
)
198 return ISC_R_INVALIDARG
;
199 state
= (dhcp_failover_state_t
*)o
;
201 obj
= (dhcp_failover_link_t
*)0;
202 status
= dhcp_failover_link_allocate (&obj
, MDL
);
203 if (status
!= ISC_R_SUCCESS
)
205 option_cache_reference (&obj
-> peer_address
,
206 state
-> partner
.address
, MDL
);
207 obj
-> peer_port
= state
-> partner
.port
;
208 dhcp_failover_state_reference (&obj
-> state_object
, state
, MDL
);
210 memset (&ds
, 0, sizeof ds
);
211 if (!evaluate_option_cache (&ds
, (struct packet
*)0, (struct lease
*)0,
212 (struct client_state
*)0,
213 (struct option_state
*)0,
214 (struct option_state
*)0,
215 &global_scope
, obj
-> peer_address
, MDL
)) {
216 dhcp_failover_link_dereference (&obj
, MDL
);
217 return ISC_R_UNEXPECTED
;
220 /* Make an omapi address list out of a buffer containing zero or more
222 status
= omapi_addr_list_new (&addrs
, ds
.len
/ 4, MDL
);
223 if (status
!= ISC_R_SUCCESS
) {
224 dhcp_failover_link_dereference (&obj
, MDL
);
228 for (i
= 0; i
< addrs
-> count
; i
++) {
229 addrs
-> addresses
[i
].addrtype
= AF_INET
;
230 addrs
-> addresses
[i
].addrlen
= sizeof (struct in_addr
);
231 memcpy (addrs
-> addresses
[i
].address
,
232 &ds
.data
[i
* 4], sizeof (struct in_addr
));
233 addrs
-> addresses
[i
].port
= obj
-> peer_port
;
235 data_string_forget (&ds
, MDL
);
237 /* Now figure out the local address that we're supposed to use. */
238 if (!state
-> me
.address
||
239 !evaluate_option_cache (&ds
, (struct packet
*)0,
241 (struct client_state
*)0,
242 (struct option_state
*)0,
243 (struct option_state
*)0,
244 &global_scope
, state
-> me
.address
,
246 memset (&local_addr
, 0, sizeof local_addr
);
247 local_addr
.addrtype
= AF_INET
;
248 local_addr
.addrlen
= sizeof (struct in_addr
);
249 if (!state
-> server_identifier
.len
) {
250 log_fatal ("failover peer %s: no local address.",
254 if (ds
.len
!= sizeof (struct in_addr
)) {
255 log_error("failover peer %s: 'address' parameter "
256 "fails to resolve to an IPv4 address",
258 data_string_forget (&ds
, MDL
);
259 dhcp_failover_link_dereference (&obj
, MDL
);
260 omapi_addr_list_dereference (&addrs
, MDL
);
261 return ISC_R_INVALIDARG
;
263 local_addr
.addrtype
= AF_INET
;
264 local_addr
.addrlen
= ds
.len
;
265 memcpy (local_addr
.address
, ds
.data
, ds
.len
);
266 if (!state
-> server_identifier
.len
)
267 data_string_copy (&state
-> server_identifier
,
269 data_string_forget (&ds
, MDL
);
270 local_addr
.port
= 0; /* Let the O.S. choose. */
273 status
= omapi_connect_list ((omapi_object_t
*)obj
,
275 omapi_addr_list_dereference (&addrs
, MDL
);
277 dhcp_failover_link_dereference (&obj
, MDL
);
281 isc_result_t
dhcp_failover_link_signal (omapi_object_t
*h
,
282 const char *name
, va_list ap
)
285 dhcp_failover_link_t
*link
;
287 dhcp_failover_state_t
*s
, *state
= (dhcp_failover_state_t
*)0;
292 if (h
-> type
!= dhcp_type_failover_link
) {
293 /* XXX shouldn't happen. Put an assert here? */
294 return ISC_R_UNEXPECTED
;
296 link
= (dhcp_failover_link_t
*)h
;
298 if (!strcmp (name
, "connect")) {
299 if (link
-> state_object
-> i_am
== primary
) {
300 status
= dhcp_failover_send_connect (h
);
301 if (status
!= ISC_R_SUCCESS
) {
302 log_info ("dhcp_failover_send_connect: %s",
303 isc_result_totext (status
));
304 omapi_disconnect (h
-> outer
, 1);
307 status
= ISC_R_SUCCESS
;
308 /* Allow the peer fifteen seconds to send us a
310 #if defined (DEBUG_FAILOVER_TIMING)
311 log_info ("add_timeout +15 %s",
312 "dhcp_failover_link_startup_timeout");
314 tv
. tv_sec
= cur_time
+ 15;
317 dhcp_failover_link_startup_timeout
,
319 (tvref_t
)dhcp_failover_link_reference
,
320 (tvunref_t
)dhcp_failover_link_dereference
);
324 if (!strcmp (name
, "disconnect")) {
325 if (link
-> state_object
) {
326 dhcp_failover_state_reference (&state
,
327 link
-> state_object
, MDL
);
328 link
-> state
= dhcp_flink_disconnected
;
330 /* Make the transition. */
331 if (state
-> link_to_peer
== link
) {
332 dhcp_failover_state_transition (link
-> state_object
,
335 /* Start trying to reconnect. */
336 #if defined (DEBUG_FAILOVER_TIMING)
337 log_info ("add_timeout +5 %s",
338 "dhcp_failover_reconnect");
340 tv
. tv_sec
= cur_time
+ 5;
342 add_timeout (&tv
, dhcp_failover_reconnect
,
344 (tvref_t
)dhcp_failover_state_reference
,
345 (tvunref_t
)dhcp_failover_state_dereference
);
347 dhcp_failover_state_dereference (&state
, MDL
);
349 return ISC_R_SUCCESS
;
352 if (!strcmp (name
, "status")) {
353 if (link
-> state_object
) {
356 status
= va_arg(ap
, isc_result_t
);
358 if ((status
== ISC_R_HOSTUNREACH
) || (status
== ISC_R_TIMEDOUT
)) {
359 dhcp_failover_state_reference (&state
,
360 link
-> state_object
, MDL
);
361 link
-> state
= dhcp_flink_disconnected
;
363 /* Make the transition. */
364 dhcp_failover_state_transition (link
-> state_object
,
367 /* Start trying to reconnect. */
368 #if defined (DEBUG_FAILOVER_TIMING)
369 log_info ("add_timeout +5 %s",
370 "dhcp_failover_reconnect");
372 tv
. tv_sec
= cur_time
+ 5;
374 add_timeout (&tv
, dhcp_failover_reconnect
,
376 (tvref_t
)dhcp_failover_state_reference
,
377 (tvunref_t
)dhcp_failover_state_dereference
);
379 dhcp_failover_state_dereference (&state
, MDL
);
381 return ISC_R_SUCCESS
;
384 /* Not a signal we recognize? */
385 if (strcmp (name
, "ready")) {
386 if (h
-> inner
&& h
-> inner
-> type
-> signal_handler
)
387 return (*(h
-> inner
-> type
-> signal_handler
))
388 (h
-> inner
, name
, ap
);
389 return ISC_R_NOTFOUND
;
392 if (!h
-> outer
|| h
-> outer
-> type
!= omapi_type_connection
)
393 return ISC_R_INVALIDARG
;
396 /* We get here because we requested that we be woken up after
397 some number of bytes were read, and that number of bytes
398 has in fact been read. */
399 switch (link
-> state
) {
400 case dhcp_flink_start
:
401 link
-> state
= dhcp_flink_message_length_wait
;
402 if ((omapi_connection_require (c
, 2)) != ISC_R_SUCCESS
)
404 case dhcp_flink_message_length_wait
:
406 link
-> state
= dhcp_flink_message_wait
;
407 link
-> imsg
= dmalloc (sizeof (failover_message_t
), MDL
);
409 status
= ISC_R_NOMEMORY
;
412 failover_message_dereference (&link
->imsg
,
415 link
-> state
= dhcp_flink_disconnected
;
416 log_info ("message length wait: %s",
417 isc_result_totext (status
));
418 omapi_disconnect (c
, 1);
419 /* XXX just blow away the protocol state now?
420 XXX or will disconnect blow it away? */
421 return ISC_R_UNEXPECTED
;
423 memset (link
-> imsg
, 0, sizeof (failover_message_t
));
424 link
-> imsg
-> refcnt
= 1;
425 /* Get the length: */
426 omapi_connection_get_uint16 (c
, &link
-> imsg_len
);
427 link
-> imsg_count
= 0; /* Bytes read. */
429 /* Ensure the message is of valid length. */
430 if (link
->imsg_len
< DHCP_FAILOVER_MIN_MESSAGE_SIZE
||
431 link
->imsg_len
> DHCP_FAILOVER_MAX_MESSAGE_SIZE
) {
432 status
= ISC_R_UNEXPECTED
;
433 goto dhcp_flink_fail
;
436 if ((omapi_connection_require (c
, link
-> imsg_len
- 2U)) !=
439 case dhcp_flink_message_wait
:
440 /* Read in the message. At this point we have the
441 entire message in the input buffer. For each
442 incoming value ID, set a bit in the bitmask
443 indicating that we've gotten it. Maybe flag an
444 error message if the bit is already set. Once
445 we're done reading, we can check the bitmask to
446 make sure that the required fields for each message
447 have been included. */
449 link
-> imsg_count
+= 2; /* Count the length as read. */
451 /* Get message type. */
452 omapi_connection_copyout (&link
-> imsg
-> type
, c
, 1);
453 link
-> imsg_count
++;
455 /* Get message payload offset. */
456 omapi_connection_copyout (&link
-> imsg_payoff
, c
, 1);
457 link
-> imsg_count
++;
459 /* Get message time. */
460 omapi_connection_get_uint32 (c
, &link
-> imsg
-> time
);
461 link
-> imsg_count
+= 4;
463 /* Get transaction ID. */
464 omapi_connection_get_uint32 (c
, &link
-> imsg
-> xid
);
465 link
-> imsg_count
+= 4;
467 #if defined (DEBUG_FAILOVER_MESSAGES)
468 # if !defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
469 if (link
->imsg
->type
== FTM_CONTACT
)
472 log_info ("link: message %s payoff %d time %ld xid %ld",
473 dhcp_failover_message_name (link
-> imsg
-> type
),
475 (unsigned long)link
-> imsg
-> time
,
476 (unsigned long)link
-> imsg
-> xid
);
477 # if !defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
481 /* Skip over any portions of the message header that we
483 if (link
-> imsg_payoff
- link
-> imsg_count
) {
484 omapi_connection_copyout ((unsigned char *)0, c
,
485 (link
-> imsg_payoff
-
486 link
-> imsg_count
));
487 link
-> imsg_count
= link
-> imsg_payoff
;
490 /* Now start sucking options off the wire. */
491 while (link
-> imsg_count
< link
-> imsg_len
) {
492 status
= do_a_failover_option (c
, link
);
493 if (status
!= ISC_R_SUCCESS
)
494 goto dhcp_flink_fail
;
497 /* If it's a connect message, try to associate it with
499 /* XXX this should be authenticated! */
500 if (link
-> imsg
-> type
== FTM_CONNECT
) {
504 if (!(link
->imsg
->options_present
&
505 FTB_RELATIONSHIP_NAME
)) {
506 errmsg
= "missing relationship-name";
507 reason
= FTR_INVALID_PARTNER
;
511 /* See if we can find a failover_state object that
512 matches this connection. This message should only
513 be received by a secondary from a primary. */
514 for (s
= failover_states
; s
; s
= s
-> next
) {
515 if (dhcp_failover_state_match_by_name(s
,
516 &link
->imsg
->relationship_name
))
520 /* If we can't find a failover protocol state
521 for this remote host, drop the connection */
523 errmsg
= "unknown failover relationship name";
524 reason
= FTR_INVALID_PARTNER
;
527 /* XXX Send a refusal message first?
528 XXX Look in protocol spec for guidance. */
532 slen
= strlen(sname
);
533 } else if (link
->imsg
->options_present
&
534 FTB_RELATIONSHIP_NAME
) {
535 sname
= (char *)link
->imsg
->
536 relationship_name
.data
;
537 slen
= link
->imsg
->relationship_name
.count
;
540 slen
= strlen(sname
);
543 log_error("Failover CONNECT from %.*s: %s",
544 slen
, sname
, errmsg
);
545 dhcp_failover_send_connectack
546 ((omapi_object_t
*)link
, state
,
548 log_info ("failover: disconnect: %s", errmsg
);
549 omapi_disconnect (c
, 0);
550 link
-> state
= dhcp_flink_disconnected
;
551 return ISC_R_SUCCESS
;
554 if ((cur_time
> link
-> imsg
-> time
&&
555 cur_time
- link
-> imsg
-> time
> 60) ||
556 (cur_time
< link
-> imsg
-> time
&&
557 link
-> imsg
-> time
- cur_time
> 60)) {
558 errmsg
= "time offset too large";
559 reason
= FTR_TIMEMISMATCH
;
563 if (!(link
-> imsg
-> options_present
& FTB_HBA
) ||
564 link
-> imsg
-> hba
.count
!= 32) {
565 errmsg
= "invalid HBA";
566 reason
= FTR_HBA_CONFLICT
; /* XXX */
570 dfree (state
-> hba
, MDL
);
571 state
-> hba
= dmalloc (32, MDL
);
573 errmsg
= "no memory";
574 reason
= FTR_MISC_REJECT
;
577 memcpy (state
-> hba
, link
-> imsg
-> hba
.data
, 32);
579 if (!link
-> state_object
)
580 dhcp_failover_state_reference
581 (&link
-> state_object
, state
, MDL
);
582 if (!link
-> peer_address
)
583 option_cache_reference
584 (&link
-> peer_address
,
585 state
-> partner
.address
, MDL
);
588 /* If we don't have a state object at this point, it's
589 some kind of bogus situation, so just drop the
591 if (!link
-> state_object
) {
592 log_info ("failover: connect: no matching state.");
593 omapi_disconnect (c
, 1);
594 link
-> state
= dhcp_flink_disconnected
;
595 return ISC_R_INVALIDARG
;
598 /* Once we have the entire message, and we've validated
599 it as best we can here, pass it to the parent. */
600 omapi_signal ((omapi_object_t
*)link
-> state_object
,
602 link
-> state
= dhcp_flink_message_length_wait
;
603 failover_message_dereference (&link
-> imsg
, MDL
);
604 /* XXX This is dangerous because we could get into a tight
605 XXX loop reading input without servicing any other stuff.
606 XXX There needs to be a way to relinquish control but
607 XXX get it back immediately if there's no other work to
609 if ((omapi_connection_require (c
, 2)) == ISC_R_SUCCESS
)
614 log_fatal("Impossible case at %s:%d.", MDL
);
617 return ISC_R_SUCCESS
;
620 static isc_result_t
do_a_failover_option (c
, link
)
622 dhcp_failover_link_t
*link
;
624 u_int16_t option_code
;
625 u_int16_t option_len
;
631 if (link
-> imsg_count
+ 2 > link
-> imsg_len
) {
632 log_error ("FAILOVER: message overflow at option code.");
633 return ISC_R_PROTOCOLERROR
;
636 /* Get option code. */
637 omapi_connection_get_uint16 (c
, &option_code
);
638 link
-> imsg_count
+= 2;
640 if (link
-> imsg_count
+ 2 > link
-> imsg_len
) {
641 log_error ("FAILOVER: message overflow at length.");
642 return ISC_R_PROTOCOLERROR
;
645 /* Get option length. */
646 omapi_connection_get_uint16 (c
, &option_len
);
647 link
-> imsg_count
+= 2;
649 if (link
-> imsg_count
+ option_len
> link
-> imsg_len
) {
650 log_error ("FAILOVER: message overflow at data.");
651 return ISC_R_PROTOCOLERROR
;
654 /* If it's an unknown code, skip over it. */
655 if ((option_code
> FTO_MAX
) ||
656 (ft_options
[option_code
].type
== FT_UNDEF
)) {
657 #if defined (DEBUG_FAILOVER_MESSAGES)
658 log_debug (" option code %d (%s) len %d (not recognized)",
660 dhcp_failover_option_name (option_code
),
663 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
664 link
-> imsg_count
+= option_len
;
665 return ISC_R_SUCCESS
;
668 /* If it's the digest, do it now. */
669 if (ft_options
[option_code
].type
== FT_DIGEST
) {
670 link
-> imsg_count
+= option_len
;
671 if (link
-> imsg_count
!= link
-> imsg_len
) {
672 log_error ("FAILOVER: digest not at end of message");
673 return ISC_R_PROTOCOLERROR
;
675 #if defined (DEBUG_FAILOVER_MESSAGES)
676 log_debug (" option %s len %d",
677 ft_options
[option_code
].name
, option_len
);
679 /* For now, just dump it. */
680 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
681 return ISC_R_SUCCESS
;
684 /* Only accept an option once. */
685 if (link
-> imsg
-> options_present
& ft_options
[option_code
].bit
) {
686 log_error ("FAILOVER: duplicate option %s",
687 ft_options
[option_code
].name
);
688 return ISC_R_PROTOCOLERROR
;
691 /* Make sure the option is appropriate for this type of message.
692 Really, any option is generally allowed for any message, and the
693 cases where this is not true are too complicated to represent in
694 this way - what this code is doing is to just avoid saving the
695 value of an option we don't have any way to use, which allows
696 us to make the failover_message structure smaller. */
697 if (ft_options
[option_code
].bit
&&
698 !(fto_allowed
[link
-> imsg
-> type
] &
699 ft_options
[option_code
].bit
)) {
700 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
701 link
-> imsg_count
+= option_len
;
702 return ISC_R_SUCCESS
;
705 /* Figure out how many elements, how big they are, and where
707 if (ft_options
[option_code
].num_present
) {
708 /* If this option takes a fixed number of elements,
709 we expect the space for them to be preallocated,
710 and we can just read the data in. */
712 op
= ((unsigned char *)link
-> imsg
) +
713 ft_options
[option_code
].offset
;
714 op_size
= ft_sizes
[ft_options
[option_code
].type
];
715 op_count
= ft_options
[option_code
].num_present
;
717 if (option_len
!= op_size
* op_count
) {
718 log_error ("FAILOVER: option size (%d:%d), option %s",
720 (ft_sizes
[ft_options
[option_code
].type
] *
721 ft_options
[option_code
].num_present
),
722 ft_options
[option_code
].name
);
723 return ISC_R_PROTOCOLERROR
;
726 failover_option_t
*fo
;
728 /* FT_DDNS* are special - one or two bytes of status
729 followed by the client FQDN. */
730 if (ft_options
[option_code
].type
== FT_DDNS1
||
731 ft_options
[option_code
].type
== FT_DDNS1
) {
734 (((char *)link
-> imsg
) +
735 ft_options
[option_code
].offset
));
737 op_count
= (ft_options
[option_code
].type
== FT_DDNS1
740 omapi_connection_copyout (&ddns
-> codes
[0],
742 link
-> imsg_count
+= op_count
;
744 ddns
-> codes
[1] = 0;
746 op_count
= option_len
- op_count
;
748 ddns
-> length
= op_count
;
749 ddns
-> data
= dmalloc (op_count
, MDL
);
751 log_error ("FAILOVER: no memory getting%s(%d)",
752 " DNS data ", op_count
);
754 /* Actually, NO_MEMORY, but if we lose here
755 we have to drop the connection. */
756 return ISC_R_PROTOCOLERROR
;
758 omapi_connection_copyout (ddns
-> data
, c
, op_count
);
762 /* A zero for num_present means that any number of
763 elements can appear, so we have to figure out how
764 many we got from the length of the option, and then
765 fill out a failover_option structure describing the
767 op_size
= ft_sizes
[ft_options
[option_code
].type
];
769 /* Make sure that option data length is a multiple of the
770 size of the data type being sent. */
771 if (op_size
> 1 && option_len
% op_size
) {
772 log_error ("FAILOVER: option_len %d not %s%d",
773 option_len
, "multiple of ", op_size
);
774 return ISC_R_PROTOCOLERROR
;
777 op_count
= option_len
/ op_size
;
779 fo
= ((failover_option_t
*)
780 (((char *)link
-> imsg
) +
781 ft_options
[option_code
].offset
));
783 fo
-> count
= op_count
;
784 fo
-> data
= dmalloc (option_len
, MDL
);
786 log_error ("FAILOVER: no memory getting %s (%d)",
787 "option data", op_count
);
789 return ISC_R_PROTOCOLERROR
;
794 /* For single-byte message values and multi-byte values that
795 don't need swapping, just read them in all at once. */
796 if (op_size
== 1 || ft_options
[option_code
].type
== FT_IPADDR
) {
797 omapi_connection_copyout ((unsigned char *)op
, c
, option_len
);
798 link
-> imsg_count
+= option_len
;
801 * As of 3.1.0, many option codes were changed to conform to
802 * draft revision 12 (which alphabetized, then renumbered all
803 * the option codes without preserving the version option code
804 * nor bumping its value). As it turns out, the message codes
805 * for CONNECT and CONNECTACK turn out the same, so it tries
806 * its darndest to connect, and falls short (when TLS_REQUEST
807 * comes up size 2 rather than size 1 as draft revision 12 also
810 * The VENDOR_CLASS code in 3.0.x was 11, which is now the HBA
811 * code. Both work out to be arbitrarily long text-or-byte
812 * strings, so they pass parsing.
814 * Note that it is possible (or intentional), if highly
815 * improbable, for the HBA bit array to exactly match
816 * isc-V3.0.x. Warning here is not an issue; if it really is
817 * 3.0.x, there will be a protocol error later on. If it isn't
818 * actually 3.0.x, then I guess the lucky user will have to
819 * live with a weird warning.
821 if ((option_code
== 11) && (option_len
> 9) &&
822 (strncmp((const char *)op
, "isc-V3.0.", 9) == 0)) {
823 log_error("WARNING: failover as of versions 3.1.0 and "
824 "on are not reverse compatible with "
831 /* For values that require swapping, read them in one at a time
832 using routines that swap bytes. */
833 for (i
= 0; i
< op_count
; i
++) {
834 switch (ft_options
[option_code
].type
) {
836 omapi_connection_get_uint32 (c
, (u_int32_t
*)op
);
838 link
-> imsg_count
+= 4;
842 omapi_connection_get_uint16 (c
, (u_int16_t
*)op
);
844 link
-> imsg_count
+= 2;
848 /* Everything else should have been handled
850 log_error ("FAILOVER: option %s: bad type %d",
851 ft_options
[option_code
].name
,
852 ft_options
[option_code
].type
);
853 return ISC_R_PROTOCOLERROR
;
857 /* Remember that we got this option. */
858 link
-> imsg
-> options_present
|= ft_options
[option_code
].bit
;
859 return ISC_R_SUCCESS
;
862 isc_result_t
dhcp_failover_link_set_value (omapi_object_t
*h
,
864 omapi_data_string_t
*name
,
865 omapi_typed_data_t
*value
)
867 if (h
-> type
!= omapi_type_protocol
)
868 return ISC_R_INVALIDARG
;
870 /* Never valid to set these. */
871 if (!omapi_ds_strcmp (name
, "link-port") ||
872 !omapi_ds_strcmp (name
, "link-name") ||
873 !omapi_ds_strcmp (name
, "link-state"))
876 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
877 return (*(h
-> inner
-> type
-> set_value
))
878 (h
-> inner
, id
, name
, value
);
879 return ISC_R_NOTFOUND
;
882 isc_result_t
dhcp_failover_link_get_value (omapi_object_t
*h
,
884 omapi_data_string_t
*name
,
885 omapi_value_t
**value
)
887 dhcp_failover_link_t
*link
;
889 if (h
-> type
!= omapi_type_protocol
)
890 return ISC_R_INVALIDARG
;
891 link
= (dhcp_failover_link_t
*)h
;
893 if (!omapi_ds_strcmp (name
, "link-port")) {
894 return omapi_make_int_value (value
, name
,
895 (int)link
-> peer_port
, MDL
);
896 } else if (!omapi_ds_strcmp (name
, "link-state")) {
897 if (link
-> state
< 0 ||
898 link
-> state
>= dhcp_flink_state_max
)
899 return omapi_make_string_value (value
, name
,
900 "invalid link state",
902 return omapi_make_string_value
904 dhcp_flink_state_names
[link
-> state
], MDL
);
907 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
908 return (*(h
-> inner
-> type
-> get_value
))
909 (h
-> inner
, id
, name
, value
);
910 return ISC_R_NOTFOUND
;
913 isc_result_t
dhcp_failover_link_destroy (omapi_object_t
*h
,
914 const char *file
, int line
)
916 dhcp_failover_link_t
*link
;
917 if (h
-> type
!= dhcp_type_failover_link
)
918 return ISC_R_INVALIDARG
;
919 link
= (dhcp_failover_link_t
*)h
;
921 if (link
-> peer_address
)
922 option_cache_dereference (&link
-> peer_address
, file
, line
);
924 failover_message_dereference (&link
-> imsg
, file
, line
);
925 if (link
-> state_object
)
926 dhcp_failover_state_dereference (&link
-> state_object
,
928 return ISC_R_SUCCESS
;
931 /* Write all the published values associated with the object through the
932 specified connection. */
934 isc_result_t
dhcp_failover_link_stuff_values (omapi_object_t
*c
,
938 dhcp_failover_link_t
*link
;
941 if (l
-> type
!= dhcp_type_failover_link
)
942 return ISC_R_INVALIDARG
;
943 link
= (dhcp_failover_link_t
*)l
;
945 status
= omapi_connection_put_name (c
, "link-port");
946 if (status
!= ISC_R_SUCCESS
)
948 status
= omapi_connection_put_uint32 (c
, sizeof (int));
949 if (status
!= ISC_R_SUCCESS
)
951 status
= omapi_connection_put_uint32 (c
, link
-> peer_port
);
952 if (status
!= ISC_R_SUCCESS
)
955 status
= omapi_connection_put_name (c
, "link-state");
956 if (status
!= ISC_R_SUCCESS
)
958 if (link
-> state
< 0 ||
959 link
-> state
>= dhcp_flink_state_max
)
960 status
= omapi_connection_put_string (c
, "invalid link state");
962 status
= (omapi_connection_put_string
963 (c
, dhcp_flink_state_names
[link
-> state
]));
964 if (status
!= ISC_R_SUCCESS
)
967 if (link
-> inner
&& link
-> inner
-> type
-> stuff_values
)
968 return (*(link
-> inner
-> type
-> stuff_values
)) (c
, id
,
970 return ISC_R_SUCCESS
;
973 /* Set up a listener for the omapi protocol. The handle stored points to
974 a listener object, not a protocol object. */
976 isc_result_t
dhcp_failover_listen (omapi_object_t
*h
)
979 dhcp_failover_listener_t
*obj
, *l
;
980 omapi_value_t
*value
= (omapi_value_t
*)0;
981 omapi_addr_t local_addr
;
984 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
985 "local-port", &value
);
986 if (status
!= ISC_R_SUCCESS
)
988 if (!value
-> value
) {
989 omapi_value_dereference (&value
, MDL
);
990 return ISC_R_INVALIDARG
;
993 status
= omapi_get_int_value (&port
, value
-> value
);
994 omapi_value_dereference (&value
, MDL
);
995 if (status
!= ISC_R_SUCCESS
)
997 local_addr
.port
= port
;
999 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
1000 "local-address", &value
);
1001 if (status
!= ISC_R_SUCCESS
)
1003 if (!value
-> value
) {
1005 omapi_value_dereference (&value
, MDL
);
1006 return ISC_R_INVALIDARG
;
1009 if (value
-> value
-> type
!= omapi_datatype_data
||
1010 value
-> value
-> u
.buffer
.len
!= sizeof (struct in_addr
))
1013 memcpy (local_addr
.address
, value
-> value
-> u
.buffer
.value
,
1014 value
-> value
-> u
.buffer
.len
);
1015 local_addr
.addrlen
= value
-> value
-> u
.buffer
.len
;
1016 local_addr
.addrtype
= AF_INET
;
1018 omapi_value_dereference (&value
, MDL
);
1020 /* Are we already listening on this port and address? */
1021 for (l
= failover_listeners
; l
; l
= l
-> next
) {
1022 if (l
-> address
.port
== local_addr
.port
&&
1023 l
-> address
.addrtype
== local_addr
.addrtype
&&
1024 l
-> address
.addrlen
== local_addr
.addrlen
&&
1025 !memcmp (l
-> address
.address
, local_addr
.address
,
1026 local_addr
.addrlen
))
1029 /* Already listening. */
1031 return ISC_R_SUCCESS
;
1033 obj
= (dhcp_failover_listener_t
*)0;
1034 status
= dhcp_failover_listener_allocate (&obj
, MDL
);
1035 if (status
!= ISC_R_SUCCESS
)
1037 obj
-> address
= local_addr
;
1039 status
= omapi_listen_addr ((omapi_object_t
*)obj
, &obj
-> address
, 1);
1040 if (status
!= ISC_R_SUCCESS
)
1043 status
= omapi_object_reference (&h
-> outer
,
1044 (omapi_object_t
*)obj
, MDL
);
1045 if (status
!= ISC_R_SUCCESS
) {
1046 dhcp_failover_listener_dereference (&obj
, MDL
);
1049 status
= omapi_object_reference (&obj
-> inner
, h
, MDL
);
1050 if (status
!= ISC_R_SUCCESS
) {
1051 dhcp_failover_listener_dereference (&obj
, MDL
);
1055 /* Put this listener on the list. */
1056 if (failover_listeners
) {
1057 dhcp_failover_listener_reference (&obj
-> next
,
1058 failover_listeners
, MDL
);
1059 dhcp_failover_listener_dereference (&failover_listeners
, MDL
);
1061 dhcp_failover_listener_reference (&failover_listeners
, obj
, MDL
);
1063 return dhcp_failover_listener_dereference (&obj
, MDL
);
1066 /* Signal handler for protocol listener - if we get a connect signal,
1067 create a new protocol connection, otherwise pass the signal down. */
1069 isc_result_t
dhcp_failover_listener_signal (omapi_object_t
*o
,
1070 const char *name
, va_list ap
)
1072 isc_result_t status
;
1073 omapi_connection_object_t
*c
;
1074 dhcp_failover_link_t
*obj
;
1075 dhcp_failover_listener_t
*p
;
1076 dhcp_failover_state_t
*s
, *state
= (dhcp_failover_state_t
*)0;
1078 if (!o
|| o
-> type
!= dhcp_type_failover_listener
)
1079 return ISC_R_INVALIDARG
;
1080 p
= (dhcp_failover_listener_t
*)o
;
1082 /* Not a signal we recognize? */
1083 if (strcmp (name
, "connect")) {
1084 if (p
-> inner
&& p
-> inner
-> type
-> signal_handler
)
1085 return (*(p
-> inner
-> type
-> signal_handler
))
1086 (p
-> inner
, name
, ap
);
1087 return ISC_R_NOTFOUND
;
1090 c
= va_arg (ap
, omapi_connection_object_t
*);
1091 if (!c
|| c
-> type
!= omapi_type_connection
)
1092 return ISC_R_INVALIDARG
;
1094 /* See if we can find a failover_state object that
1095 matches this connection. */
1096 for (s
= failover_states
; s
; s
= s
-> next
) {
1097 if (dhcp_failover_state_match
1098 (s
, (u_int8_t
*)&c
-> remote_addr
.sin_addr
,
1099 sizeof c
-> remote_addr
.sin_addr
)) {
1105 log_info ("failover: listener: no matching state");
1106 return omapi_disconnect ((omapi_object_t
*)c
, 1);
1109 obj
= (dhcp_failover_link_t
*)0;
1110 status
= dhcp_failover_link_allocate (&obj
, MDL
);
1111 if (status
!= ISC_R_SUCCESS
)
1113 obj
-> peer_port
= ntohs (c
-> remote_addr
.sin_port
);
1115 status
= omapi_object_reference (&obj
-> outer
,
1116 (omapi_object_t
*)c
, MDL
);
1117 if (status
!= ISC_R_SUCCESS
) {
1119 dhcp_failover_link_dereference (&obj
, MDL
);
1120 log_info ("failover: listener: picayune failure.");
1121 omapi_disconnect ((omapi_object_t
*)c
, 1);
1125 status
= omapi_object_reference (&c
-> inner
,
1126 (omapi_object_t
*)obj
, MDL
);
1127 if (status
!= ISC_R_SUCCESS
)
1130 status
= dhcp_failover_state_reference (&obj
-> state_object
,
1132 if (status
!= ISC_R_SUCCESS
)
1135 omapi_signal_in ((omapi_object_t
*)obj
, "connect");
1137 return dhcp_failover_link_dereference (&obj
, MDL
);
1140 isc_result_t
dhcp_failover_listener_set_value (omapi_object_t
*h
,
1142 omapi_data_string_t
*name
,
1143 omapi_typed_data_t
*value
)
1145 if (h
-> type
!= dhcp_type_failover_listener
)
1146 return ISC_R_INVALIDARG
;
1148 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
1149 return (*(h
-> inner
-> type
-> set_value
))
1150 (h
-> inner
, id
, name
, value
);
1151 return ISC_R_NOTFOUND
;
1154 isc_result_t
dhcp_failover_listener_get_value (omapi_object_t
*h
,
1156 omapi_data_string_t
*name
,
1157 omapi_value_t
**value
)
1159 if (h
-> type
!= dhcp_type_failover_listener
)
1160 return ISC_R_INVALIDARG
;
1162 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
1163 return (*(h
-> inner
-> type
-> get_value
))
1164 (h
-> inner
, id
, name
, value
);
1165 return ISC_R_NOTFOUND
;
1168 isc_result_t
dhcp_failover_listener_destroy (omapi_object_t
*h
,
1169 const char *file
, int line
)
1171 dhcp_failover_listener_t
*l
;
1173 if (h
-> type
!= dhcp_type_failover_listener
)
1174 return ISC_R_INVALIDARG
;
1175 l
= (dhcp_failover_listener_t
*)h
;
1177 dhcp_failover_listener_dereference (&l
-> next
, file
, line
);
1179 return ISC_R_SUCCESS
;
1182 /* Write all the published values associated with the object through the
1183 specified connection. */
1185 isc_result_t
dhcp_failover_listener_stuff (omapi_object_t
*c
,
1189 if (p
-> type
!= dhcp_type_failover_listener
)
1190 return ISC_R_INVALIDARG
;
1192 if (p
-> inner
&& p
-> inner
-> type
-> stuff_values
)
1193 return (*(p
-> inner
-> type
-> stuff_values
)) (c
, id
,
1195 return ISC_R_SUCCESS
;
1198 /* Set up master state machine for the failover protocol. */
1200 isc_result_t
dhcp_failover_register (omapi_object_t
*h
)
1202 isc_result_t status
;
1203 dhcp_failover_state_t
*obj
;
1205 omapi_value_t
*value
= (omapi_value_t
*)0;
1207 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
1208 "local-port", &value
);
1209 if (status
!= ISC_R_SUCCESS
)
1211 if (!value
-> value
) {
1212 omapi_value_dereference (&value
, MDL
);
1213 return ISC_R_INVALIDARG
;
1216 status
= omapi_get_int_value (&port
, value
-> value
);
1217 omapi_value_dereference (&value
, MDL
);
1218 if (status
!= ISC_R_SUCCESS
)
1221 obj
= (dhcp_failover_state_t
*)0;
1222 dhcp_failover_state_allocate (&obj
, MDL
);
1223 obj
-> me
.port
= port
;
1225 status
= omapi_listen ((omapi_object_t
*)obj
, port
, 1);
1226 if (status
!= ISC_R_SUCCESS
) {
1227 dhcp_failover_state_dereference (&obj
, MDL
);
1231 status
= omapi_object_reference (&h
-> outer
, (omapi_object_t
*)obj
,
1233 if (status
!= ISC_R_SUCCESS
) {
1234 dhcp_failover_state_dereference (&obj
, MDL
);
1237 status
= omapi_object_reference (&obj
-> inner
, h
, MDL
);
1238 dhcp_failover_state_dereference (&obj
, MDL
);
1242 /* Signal handler for protocol state machine. */
1244 isc_result_t
dhcp_failover_state_signal (omapi_object_t
*o
,
1245 const char *name
, va_list ap
)
1247 isc_result_t status
;
1248 dhcp_failover_state_t
*state
;
1249 dhcp_failover_link_t
*link
;
1252 if (!o
|| o
-> type
!= dhcp_type_failover_state
)
1253 return ISC_R_INVALIDARG
;
1254 state
= (dhcp_failover_state_t
*)o
;
1256 /* Not a signal we recognize? */
1257 if (strcmp (name
, "disconnect") &&
1258 strcmp (name
, "message")) {
1259 if (state
-> inner
&& state
-> inner
-> type
-> signal_handler
)
1260 return (*(state
-> inner
-> type
-> signal_handler
))
1261 (state
-> inner
, name
, ap
);
1262 return ISC_R_NOTFOUND
;
1265 /* Handle connect signals by seeing what state we're in
1266 and potentially doing a state transition. */
1267 if (!strcmp (name
, "disconnect")) {
1268 link
= va_arg (ap
, dhcp_failover_link_t
*);
1270 dhcp_failover_link_dereference (&state
-> link_to_peer
, MDL
);
1271 dhcp_failover_state_transition (state
, "disconnect");
1272 if (state
-> i_am
== primary
) {
1273 #if defined (DEBUG_FAILOVER_TIMING)
1274 log_info ("add_timeout +90 %s",
1275 "dhcp_failover_reconnect");
1277 tv
. tv_sec
= cur_time
+ 90;
1279 add_timeout (&tv
, dhcp_failover_reconnect
,
1281 (tvref_t
)dhcp_failover_state_reference
,
1283 dhcp_failover_state_dereference
);
1285 } else if (!strcmp (name
, "message")) {
1286 link
= va_arg (ap
, dhcp_failover_link_t
*);
1288 if (link
-> imsg
-> type
== FTM_CONNECT
) {
1289 /* If we already have a link to the peer, it must be
1291 XXX Is this the right thing to do?
1292 XXX Probably not - what if both peers start at
1293 XXX the same time? */
1294 if (state
-> link_to_peer
) {
1295 dhcp_failover_send_connectack
1296 ((omapi_object_t
*)link
, state
,
1298 "already connected");
1299 omapi_disconnect (link
-> outer
, 1);
1300 return ISC_R_SUCCESS
;
1302 if (!(link
-> imsg
-> options_present
& FTB_MCLT
)) {
1303 dhcp_failover_send_connectack
1304 ((omapi_object_t
*)link
, state
,
1306 "no MCLT provided");
1307 omapi_disconnect (link
-> outer
, 1);
1308 return ISC_R_SUCCESS
;
1311 dhcp_failover_link_reference (&state
-> link_to_peer
,
1313 status
= (dhcp_failover_send_connectack
1314 ((omapi_object_t
*)link
, state
, 0, 0));
1315 if (status
!= ISC_R_SUCCESS
) {
1316 dhcp_failover_link_dereference
1317 (&state
-> link_to_peer
, MDL
);
1318 log_info ("dhcp_failover_send_connectack: %s",
1319 isc_result_totext (status
));
1320 omapi_disconnect (link
-> outer
, 1);
1321 return ISC_R_SUCCESS
;
1323 if (link
-> imsg
-> options_present
& FTB_MAX_UNACKED
)
1324 state
-> partner
.max_flying_updates
=
1325 link
-> imsg
-> max_unacked
;
1326 if (link
-> imsg
-> options_present
& FTB_RECEIVE_TIMER
)
1327 state
-> partner
.max_response_delay
=
1328 link
-> imsg
-> receive_timer
;
1329 state
-> mclt
= link
-> imsg
-> mclt
;
1330 dhcp_failover_send_state (state
);
1331 cancel_timeout (dhcp_failover_link_startup_timeout
,
1333 } else if (link
-> imsg
-> type
== FTM_CONNECTACK
) {
1338 cancel_timeout (dhcp_failover_link_startup_timeout
,
1341 if (!(link
->imsg
->options_present
&
1342 FTB_RELATIONSHIP_NAME
)) {
1343 errmsg
= "missing relationship-name";
1344 reason
= FTR_INVALID_PARTNER
;
1348 if (link
->imsg
->options_present
& FTB_REJECT_REASON
) {
1349 /* XXX: add message option to text output. */
1350 log_error ("Failover CONNECT to %s rejected: %s",
1351 state
? state
->name
: "unknown",
1352 (dhcp_failover_reject_reason_print
1353 (link
-> imsg
-> reject_reason
)));
1354 /* XXX print message from peer if peer sent message. */
1355 omapi_disconnect (link
-> outer
, 1);
1356 return ISC_R_SUCCESS
;
1359 if (!dhcp_failover_state_match_by_name(state
,
1360 &link
->imsg
->relationship_name
)) {
1361 /* XXX: Overflow results in log truncation, safe. */
1362 snprintf(errbuf
, sizeof(errbuf
), "remote failover "
1363 "relationship name %.*s does not match",
1364 (int)link
->imsg
->relationship_name
.count
,
1365 link
->imsg
->relationship_name
.data
);
1367 reason
= FTR_INVALID_PARTNER
;
1369 log_error("Failover CONNECTACK from %s: %s",
1370 state
->name
, errmsg
);
1371 dhcp_failover_send_disconnect ((omapi_object_t
*)link
,
1373 omapi_disconnect (link
-> outer
, 0);
1374 return ISC_R_SUCCESS
;
1377 if (state
-> link_to_peer
) {
1378 errmsg
= "already connected";
1379 reason
= FTR_DUP_CONNECTION
;
1383 if ((cur_time
> link
-> imsg
-> time
&&
1384 cur_time
- link
-> imsg
-> time
> 60) ||
1385 (cur_time
< link
-> imsg
-> time
&&
1386 link
-> imsg
-> time
- cur_time
> 60)) {
1387 errmsg
= "time offset too large";
1388 reason
= FTR_TIMEMISMATCH
;
1392 dhcp_failover_link_reference (&state
-> link_to_peer
,
1395 /* XXX This is probably the right thing to do, but
1396 XXX for release three, to make the smallest possible
1397 XXX change, we are doing this when the peer state
1398 XXX changes instead. */
1399 if (state
-> me
.state
== startup
)
1400 dhcp_failover_set_state (state
,
1401 state
-> saved_state
);
1404 dhcp_failover_send_state (state
);
1406 if (link
-> imsg
-> options_present
& FTB_MAX_UNACKED
)
1407 state
-> partner
.max_flying_updates
=
1408 link
-> imsg
-> max_unacked
;
1409 if (link
-> imsg
-> options_present
& FTB_RECEIVE_TIMER
)
1410 state
-> partner
.max_response_delay
=
1411 link
-> imsg
-> receive_timer
;
1412 #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
1413 log_info ("add_timeout +%d %s",
1414 (int)state
-> partner
.max_response_delay
/ 3,
1415 "dhcp_failover_send_contact");
1417 tv
. tv_sec
= cur_time
+
1418 (int)state
-> partner
.max_response_delay
/ 3;
1421 dhcp_failover_send_contact
, state
,
1422 (tvref_t
)dhcp_failover_state_reference
,
1423 (tvunref_t
)dhcp_failover_state_dereference
);
1424 #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
1425 log_info ("add_timeout +%d %s",
1426 (int)state
-> me
.max_response_delay
,
1427 "dhcp_failover_timeout");
1429 tv
. tv_sec
= cur_time
+
1430 (int)state
-> me
.max_response_delay
;
1433 dhcp_failover_timeout
, state
,
1434 (tvref_t
)dhcp_failover_state_reference
,
1435 (tvunref_t
)dhcp_failover_state_dereference
);
1436 } else if (link
-> imsg
-> type
== FTM_DISCONNECT
) {
1437 if (link
-> imsg
-> reject_reason
) {
1438 log_error ("Failover DISCONNECT from %s: %s",
1439 state
? state
->name
: "unknown",
1440 (dhcp_failover_reject_reason_print
1441 (link
-> imsg
-> reject_reason
)));
1443 omapi_disconnect (link
-> outer
, 1);
1444 } else if (link
-> imsg
-> type
== FTM_BNDUPD
) {
1445 dhcp_failover_process_bind_update (state
,
1447 } else if (link
-> imsg
-> type
== FTM_BNDACK
) {
1448 dhcp_failover_process_bind_ack (state
, link
-> imsg
);
1449 } else if (link
-> imsg
-> type
== FTM_UPDREQ
) {
1450 dhcp_failover_process_update_request (state
,
1452 } else if (link
-> imsg
-> type
== FTM_UPDREQALL
) {
1453 dhcp_failover_process_update_request_all
1454 (state
, link
-> imsg
);
1455 } else if (link
-> imsg
-> type
== FTM_UPDDONE
) {
1456 dhcp_failover_process_update_done (state
,
1458 } else if (link
-> imsg
-> type
== FTM_POOLREQ
) {
1459 dhcp_failover_pool_reqbalance(state
);
1460 } else if (link
-> imsg
-> type
== FTM_POOLRESP
) {
1461 log_info ("pool response: %ld leases",
1463 link
-> imsg
-> addresses_transferred
);
1464 } else if (link
-> imsg
-> type
== FTM_STATE
) {
1465 dhcp_failover_peer_state_changed (state
,
1469 /* Add a timeout so that if the partner doesn't send
1470 another message for the maximum transmit idle time
1471 plus a grace of one second, we close the
1473 if (state
-> link_to_peer
&&
1474 state
-> link_to_peer
== link
&&
1475 state
-> link_to_peer
-> state
!= dhcp_flink_disconnected
)
1477 #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
1478 log_info ("add_timeout +%d %s",
1479 (int)state
-> me
.max_response_delay
,
1480 "dhcp_failover_timeout");
1482 tv
. tv_sec
= cur_time
+
1483 (int)state
-> me
.max_response_delay
;
1486 dhcp_failover_timeout
, state
,
1487 (tvref_t
)dhcp_failover_state_reference
,
1488 (tvunref_t
)dhcp_failover_state_dereference
);
1493 /* Handle all the events we care about... */
1494 return ISC_R_SUCCESS
;
1497 isc_result_t
dhcp_failover_state_transition (dhcp_failover_state_t
*state
,
1500 isc_result_t status
;
1502 /* XXX Check these state transitions against the spec! */
1503 if (!strcmp (name
, "disconnect")) {
1504 if (state
-> link_to_peer
) {
1505 log_info ("peer %s: disconnected", state
-> name
);
1506 if (state
-> link_to_peer
-> state_object
)
1507 dhcp_failover_state_dereference
1508 (&state
-> link_to_peer
-> state_object
, MDL
);
1509 dhcp_failover_link_dereference (&state
-> link_to_peer
,
1512 cancel_timeout (dhcp_failover_send_contact
, state
);
1513 cancel_timeout (dhcp_failover_timeout
, state
);
1514 cancel_timeout (dhcp_failover_startup_timeout
, state
);
1516 switch (state
-> me
.state
== startup
?
1517 state
-> saved_state
: state
-> me
.state
) {
1518 /* In these situations, we remain in the current
1519 * state, or if in startup enter those states.
1521 case communications_interrupted
:
1528 case resolution_interrupted
:
1530 /* Already in the right state? */
1531 if (state
-> me
.state
== startup
)
1532 return (dhcp_failover_set_state
1533 (state
, state
-> saved_state
));
1534 return ISC_R_SUCCESS
;
1536 case potential_conflict
:
1537 return dhcp_failover_set_state
1538 (state
, resolution_interrupted
);
1541 return dhcp_failover_set_state
1542 (state
, communications_interrupted
);
1545 return dhcp_failover_set_state
1546 (state
, resolution_interrupted
);
1549 log_fatal("Impossible case at %s:%d.", MDL
);
1550 break; /* can't happen. */
1552 } else if (!strcmp (name
, "connect")) {
1553 switch (state
-> me
.state
) {
1554 case communications_interrupted
:
1555 status
= dhcp_failover_set_state (state
, normal
);
1556 dhcp_failover_send_updates (state
);
1559 case resolution_interrupted
:
1560 return dhcp_failover_set_state (state
,
1561 potential_conflict
);
1565 case potential_conflict
:
1574 return dhcp_failover_send_state (state
);
1577 log_fatal("Impossible case at %s:%d.", MDL
);
1580 } else if (!strcmp (name
, "startup")) {
1581 dhcp_failover_set_state (state
, startup
);
1582 return ISC_R_SUCCESS
;
1583 } else if (!strcmp (name
, "connect-timeout")) {
1584 switch (state
-> me
.state
) {
1585 case communications_interrupted
:
1587 case resolution_interrupted
:
1592 return ISC_R_SUCCESS
;
1599 return dhcp_failover_set_state
1600 (state
, communications_interrupted
);
1602 case potential_conflict
:
1603 return dhcp_failover_set_state
1604 (state
, resolution_interrupted
);
1607 log_fatal("Impossible case at %s:%d.", MDL
);
1611 return ISC_R_INVALIDARG
;
1614 isc_result_t
dhcp_failover_set_service_state (dhcp_failover_state_t
*state
)
1616 switch (state
-> me
.state
) {
1618 state
-> service_state
= not_responding
;
1619 state
-> nrr
= " (my state unknown)";
1623 state
-> service_state
= service_partner_down
;
1628 state
-> service_state
= cooperating
;
1632 case communications_interrupted
:
1633 state
-> service_state
= not_cooperating
;
1637 case resolution_interrupted
:
1638 case potential_conflict
:
1640 state
-> service_state
= not_responding
;
1641 state
-> nrr
= " (resolving conflicts)";
1645 state
-> service_state
= not_responding
;
1646 state
-> nrr
= " (recovering)";
1650 state
-> service_state
= not_responding
;
1651 state
-> nrr
= " (shut down)";
1655 state
-> service_state
= not_responding
;
1656 state
-> nrr
= " (paused)";
1660 state
-> service_state
= not_responding
;
1661 state
-> nrr
= " (recover wait)";
1665 state
-> service_state
= not_responding
;
1666 state
-> nrr
= " (recover done)";
1670 state
-> service_state
= service_startup
;
1671 state
-> nrr
= " (startup)";
1675 log_fatal("Impossible case at %s:%d.\n", MDL
);
1679 /* Some peer states can require us not to respond, even if our
1681 /* XXX hm. I suspect this isn't true anymore. */
1682 if (state
-> service_state
!= not_responding
) {
1683 switch (state
-> partner
.state
) {
1685 state
-> service_state
= not_responding
;
1686 state
-> nrr
= " (peer demands: recovering)";
1689 case potential_conflict
:
1691 case resolution_interrupted
:
1692 state
-> service_state
= not_responding
;
1693 state
-> nrr
= " (peer demands: resolving conflicts)";
1696 /* Other peer states don't affect our behaviour. */
1702 return ISC_R_SUCCESS
;
1705 isc_result_t
dhcp_failover_set_state (dhcp_failover_state_t
*state
,
1706 enum failover_state new_state
)
1708 enum failover_state saved_state
;
1711 struct shared_network
*s
;
1715 /* If we're in certain states where we're sending updates, and the peer
1716 * state changes, we need to re-schedule any pending updates just to
1717 * be on the safe side. This results in retransmission.
1719 switch (state
-> me
.state
) {
1721 case potential_conflict
:
1723 if (state
-> ack_queue_tail
) {
1726 /* Zap the flags. */
1727 for (lp
= state
-> ack_queue_head
; lp
; lp
= lp
-> next_pending
)
1728 lp
-> flags
= ((lp
-> flags
& ~ON_ACK_QUEUE
) |
1731 /* Now hook the ack queue to the beginning of the update
1733 if (state
-> update_queue_head
) {
1734 lease_reference (&state
-> ack_queue_tail
-> next_pending
,
1735 state
-> update_queue_head
, MDL
);
1736 lease_dereference (&state
-> update_queue_head
, MDL
);
1738 lease_reference (&state
-> update_queue_head
,
1739 state
-> ack_queue_head
, MDL
);
1740 if (!state
-> update_queue_tail
) {
1741 #if defined (POINTER_DEBUG)
1742 if (state
-> ack_queue_tail
-> next_pending
) {
1743 log_error ("next pending on ack queue tail.");
1747 lease_reference (&state
-> update_queue_tail
,
1748 state
-> ack_queue_tail
, MDL
);
1750 lease_dereference (&state
-> ack_queue_tail
, MDL
);
1751 lease_dereference (&state
-> ack_queue_head
, MDL
);
1752 state
-> cur_unacked_updates
= 0;
1754 /* We will re-queue a timeout later, if applicable. */
1755 cancel_timeout (dhcp_failover_keepalive
, state
);
1762 /* Tentatively make the transition. */
1763 saved_state
= state
-> me
.state
;
1764 saved_stos
= state
-> me
.stos
;
1766 /* Keep the old stos if we're going into recover_wait or if we're
1767 coming into or out of startup. */
1768 if (new_state
!= recover_wait
&& new_state
!= startup
&&
1769 saved_state
!= startup
)
1770 state
-> me
.stos
= cur_time
;
1772 /* If we're in shutdown, peer is in partner_down, and we're moving
1773 to recover, we can skip waiting for MCLT to expire. This happens
1774 when a server is moved administratively into shutdown prior to
1775 actually shutting down. Of course, if there are any updates
1776 pending we can't actually do this. */
1777 if (new_state
== recover
&& saved_state
== shut_down
&&
1778 state
-> partner
.state
== partner_down
&&
1779 !state
-> update_queue_head
&& !state
-> ack_queue_head
)
1780 state
-> me
.stos
= cur_time
- state
-> mclt
;
1782 state
-> me
.state
= new_state
;
1783 if (new_state
== startup
&& saved_state
!= startup
)
1784 state
-> saved_state
= saved_state
;
1786 /* If we can't record the new state, we can't make a state transition. */
1787 if (!write_failover_state (state
) || !commit_leases ()) {
1788 log_error ("Unable to record current failover state for %s",
1790 state
-> me
.state
= saved_state
;
1791 state
-> me
.stos
= saved_stos
;
1792 return ISC_R_IOERROR
;
1795 log_info ("failover peer %s: I move from %s to %s",
1796 state
-> name
, dhcp_failover_state_name_print (saved_state
),
1797 dhcp_failover_state_name_print (state
-> me
.state
));
1799 /* If we were in startup and we just left it, cancel the timeout. */
1800 if (new_state
!= startup
&& saved_state
== startup
)
1801 cancel_timeout (dhcp_failover_startup_timeout
, state
);
1803 /* Set our service state. */
1804 dhcp_failover_set_service_state (state
);
1806 /* Tell the peer about it. */
1807 if (state
-> link_to_peer
)
1808 dhcp_failover_send_state (state
);
1810 switch (new_state
) {
1812 /* Upon entering normal state, the server is expected to retransmit
1813 * all pending binding updates. This is a good opportunity to
1814 * rebalance the pool (potentially making new pending updates),
1815 * which also schedules the next pool rebalance.
1817 dhcp_failover_pool_balance(state
);
1818 dhcp_failover_generate_update_queue(state
, 0);
1820 if (state
->update_queue_tail
!= NULL
) {
1821 dhcp_failover_send_updates(state
);
1822 log_info("Sending updates to %s.", state
->name
);
1827 case potential_conflict
:
1828 if (state
-> i_am
== primary
)
1829 dhcp_failover_send_update_request (state
);
1833 #if defined (DEBUG_FAILOVER_TIMING)
1834 log_info ("add_timeout +15 %s",
1835 "dhcp_failover_startup_timeout");
1837 tv
. tv_sec
= cur_time
+ 15;
1840 dhcp_failover_startup_timeout
,
1842 (tvref_t
)omapi_object_reference
,
1844 omapi_object_dereference
);
1847 /* If we come back in recover_wait and there's still waiting
1848 to do, set a timeout. */
1850 if (state
-> me
.stos
+ state
-> mclt
> cur_time
) {
1851 #if defined (DEBUG_FAILOVER_TIMING)
1852 log_info ("add_timeout +%d %s",
1854 state
-> me
.stos
+ state
-> mclt
),
1855 "dhcp_failover_startup_timeout");
1857 tv
. tv_sec
= (int)(state
-> me
.stos
+ state
-> mclt
);
1860 dhcp_failover_recover_done
,
1862 (tvref_t
)omapi_object_reference
,
1864 omapi_object_dereference
);
1866 dhcp_failover_recover_done (state
);
1870 /* XXX: We're supposed to calculate if updreq or updreqall is
1871 * needed. In theory, we should only have to updreqall if we
1872 * are positive we lost our stable storage.
1874 if (state
-> link_to_peer
)
1875 dhcp_failover_send_update_request_all (state
);
1879 /* For every expired lease, set a timeout for it to become free. */
1880 for (s
= shared_networks
; s
; s
= s
-> next
) {
1881 for (p
= s
-> pools
; p
; p
= p
-> next
) {
1882 if (p
-> failover_peer
== state
) {
1883 for (l
= p
->expired
; l
; l
= l
->next
) {
1884 l
->tsfp
= state
->me
.stos
+ state
->mclt
;
1885 l
->sort_time
= (l
->tsfp
> l
->ends
) ?
1889 (p
->expired
->sort_time
< p
->next_event_time
)) {
1891 p
->next_event_time
= p
->expired
->sort_time
;
1892 #if defined (DEBUG_FAILOVER_TIMING)
1893 log_info ("add_timeout +%d %s",
1894 (int)(cur_time
- p
->next_event_time
),
1897 tv
.tv_sec
= p
->next_event_time
;
1899 add_timeout(&tv
, pool_timer
, p
,
1900 (tvref_t
)pool_reference
,
1901 (tvunref_t
)pool_dereference
);
1913 return ISC_R_SUCCESS
;
1916 isc_result_t
dhcp_failover_peer_state_changed (dhcp_failover_state_t
*state
,
1917 failover_message_t
*msg
)
1919 enum failover_state previous_state
= state
-> partner
.state
;
1920 enum failover_state new_state
;
1923 new_state
= msg
-> server_state
;
1924 startupp
= (msg
-> server_flags
& FTF_SERVER_STARTUP
) ? 1 : 0;
1926 if (state
-> partner
.state
== new_state
&& state
-> me
.state
) {
1927 switch (state
-> me
.state
) {
1929 dhcp_failover_set_state (state
, state
-> saved_state
);
1930 return ISC_R_SUCCESS
;
1934 case potential_conflict
:
1939 return ISC_R_SUCCESS
;
1941 /* If we get a peer state change when we're
1942 disconnected, we always process it. */
1944 case communications_interrupted
:
1945 case resolution_interrupted
:
1951 log_fatal("Impossible case at %s:%d.", MDL
);
1956 state
-> partner
.state
= new_state
;
1958 log_info ("failover peer %s: peer moves from %s to %s",
1960 dhcp_failover_state_name_print (previous_state
),
1961 dhcp_failover_state_name_print (state
-> partner
.state
));
1963 if (!write_failover_state (state
) || !commit_leases ()) {
1964 /* This is bad, but it's not fatal. Of course, if we
1965 can't write to the lease database, we're not going to
1966 get much done anyway. */
1967 log_error ("Unable to record current failover state for %s",
1971 /* Quickly validate the new state as being one of the 13 known
1974 switch (new_state
) {
1978 case communications_interrupted
:
1980 case potential_conflict
:
1985 case resolution_interrupted
:
1991 log_error("failover peer %s: Invalid state: %d", state
->name
,
1993 dhcp_failover_set_state(state
, shut_down
);
1994 return ISC_R_SUCCESS
;
1997 /* Do any state transitions that are required as a result of the
1998 peer's state transition. */
2000 switch (state
-> me
.state
== startup
?
2001 state
-> saved_state
: state
-> me
.state
) {
2003 switch (new_state
) {
2005 dhcp_failover_state_pool_check (state
);
2009 if (state
-> me
.state
== startup
)
2010 dhcp_failover_set_state (state
, recover
);
2012 dhcp_failover_set_state (state
,
2013 potential_conflict
);
2016 case potential_conflict
:
2017 case resolution_interrupted
:
2019 /* None of these transitions should ever occur. */
2020 log_error("Peer %s: Invalid state transition %s "
2021 "to %s.", state
->name
,
2022 dhcp_failover_state_name_print(previous_state
),
2023 dhcp_failover_state_name_print(new_state
));
2024 dhcp_failover_set_state (state
, shut_down
);
2029 dhcp_failover_set_state (state
, partner_down
);
2033 dhcp_failover_set_state (state
,
2034 communications_interrupted
);
2038 /* recover_wait, recover_done, unknown_state, startup,
2039 * communications_interrupted
2046 switch (new_state
) {
2048 log_info ("failover peer %s: requesting %s",
2049 state
-> name
, "full update from peer");
2050 /* Don't send updreqall if we're really in the
2051 startup state, because that will result in two
2053 if (state
-> me
.state
== recover
)
2054 dhcp_failover_send_update_request_all (state
);
2057 case potential_conflict
:
2058 case resolution_interrupted
:
2061 dhcp_failover_set_state (state
, potential_conflict
);
2065 case communications_interrupted
:
2066 /* We're supposed to send an update request at this
2068 /* XXX we don't currently have code here to do any
2069 XXX clever detection of when we should send an
2070 XXX UPDREQALL message rather than an UPDREQ
2071 XXX message. What to do, what to do? */
2072 /* Currently when we enter recover state, no matter
2073 * the reason, we send an UPDREQALL. So, it makes
2074 * the most sense to stick to that until something
2076 * Furthermore, we only want to send the update
2077 * request if we are not in startup state.
2079 if (state
-> me
.state
== recover
)
2080 dhcp_failover_send_update_request_all (state
);
2084 /* XXX We're not explicitly told what to do in this
2085 XXX case, but this transition is consistent with
2086 XXX what is elsewhere in the draft. */
2087 dhcp_failover_set_state (state
, partner_down
);
2090 /* We can't really do anything in this case. */
2092 /* paused, recover_done, recover_wait, unknown_state,
2099 case potential_conflict
:
2100 switch (new_state
) {
2102 /* This is an illegal transition. */
2103 log_error("Peer %s moves to normal during conflict "
2104 "resolution - panic, shutting down.",
2106 dhcp_failover_set_state(state
, shut_down
);
2110 if (previous_state
== potential_conflict
)
2111 dhcp_failover_send_update_request (state
);
2113 log_error("Peer %s: Unexpected move to "
2114 "conflict-done.", state
->name
);
2119 case potential_conflict
:
2121 case communications_interrupted
:
2122 case resolution_interrupted
:
2127 dhcp_failover_set_state (state
, recover
);
2131 dhcp_failover_set_state (state
, partner_down
);
2135 /* unknown_state, startup */
2141 switch (new_state
) {
2144 dhcp_failover_set_state(state
, new_state
);
2148 log_fatal("Peer %s: Invalid attempt to move from %s "
2149 "to %s while local state is conflict-done.",
2151 dhcp_failover_state_name_print(previous_state
),
2152 dhcp_failover_state_name_print(new_state
));
2157 /* Take no action if other server is starting up. */
2161 switch (new_state
) {
2162 /* This is where we should be. */
2168 dhcp_failover_set_state (state
, normal
);
2172 case potential_conflict
:
2174 case communications_interrupted
:
2175 case resolution_interrupted
:
2177 dhcp_failover_set_state (state
, potential_conflict
);
2181 /* shut_down, paused, unknown_state, startup */
2186 case communications_interrupted
:
2187 switch (new_state
) {
2189 /* Stick with the status quo. */
2192 /* If we're in communications-interrupted and an
2193 amnesic peer connects, go to the partner_down
2194 state immediately. */
2196 dhcp_failover_set_state (state
, partner_down
);
2200 case communications_interrupted
:
2203 /* XXX so we don't need to do this specially in
2204 XXX the CONNECT and CONNECTACK handlers. */
2205 dhcp_failover_send_updates (state
);
2206 dhcp_failover_set_state (state
, normal
);
2209 case potential_conflict
:
2211 case resolution_interrupted
:
2213 dhcp_failover_set_state (state
, potential_conflict
);
2217 dhcp_failover_set_state (state
, partner_down
);
2221 /* unknown_state, startup */
2226 case resolution_interrupted
:
2227 switch (new_state
) {
2230 case potential_conflict
:
2232 case communications_interrupted
:
2233 case resolution_interrupted
:
2237 dhcp_failover_set_state (state
, potential_conflict
);
2241 dhcp_failover_set_state (state
, partner_down
);
2245 /* paused, unknown_state, startup */
2250 /* Make no transitions while in recover_wait...just wait. */
2255 switch (new_state
) {
2257 log_error("Both servers have entered recover-done!");
2259 dhcp_failover_set_state (state
, normal
);
2263 dhcp_failover_set_state (state
, partner_down
);
2267 /* potential_conflict, partner_down,
2268 * communications_interrupted, resolution_interrupted,
2269 * paused, recover, recover_wait, unknown_state,
2276 /* We are essentially dead in the water when we're in
2277 either shut_down or paused states, and do not do any
2278 automatic state transitions. */
2283 /* XXX: Shouldn't this be a fatal condition? */
2288 log_fatal("Impossible condition at %s:%d.", MDL
);
2293 /* If we didn't make a transition out of startup as a result of
2294 the peer's state change, do it now as a result of the fact that
2295 we got a state change from the peer. */
2296 if (state
-> me
.state
== startup
&& state
-> saved_state
!= startup
)
2297 dhcp_failover_set_state (state
, state
-> saved_state
);
2299 /* For now, just set the service state based on the peer's state
2301 dhcp_failover_set_service_state (state
);
2303 return ISC_R_SUCCESS
;
2307 * Balance operation manual entry; startup, entrance to normal state. No
2308 * sense sending a POOLREQ at this stage; the peer is likely about to schedule
2309 * their own rebalance event upon entering normal themselves.
2312 dhcp_failover_pool_balance(dhcp_failover_state_t
*state
)
2314 /* Cancel pending event. */
2315 cancel_timeout(dhcp_failover_pool_rebalance
, state
);
2316 state
->sched_balance
= 0;
2318 dhcp_failover_pool_dobalance(state
, NULL
);
2322 * Balance operation entry from timer event. Once per timer interval is
2323 * the only time we want to emit POOLREQs (asserting an interrupt in our
2327 dhcp_failover_pool_rebalance(void *failover_state
)
2329 dhcp_failover_state_t
*state
;
2330 isc_boolean_t sendreq
= ISC_FALSE
;
2332 state
= (dhcp_failover_state_t
*)failover_state
;
2334 /* Clear scheduled event indicator. */
2335 state
->sched_balance
= 0;
2337 if (dhcp_failover_pool_dobalance(state
, &sendreq
))
2338 dhcp_failover_send_updates(state
);
2341 dhcp_failover_send_poolreq(state
);
2345 * Balance operation entry from POOLREQ protocol message. Do not permit a
2346 * POOLREQ to send back a POOLREQ. Ping pong.
2349 dhcp_failover_pool_reqbalance(dhcp_failover_state_t
*state
)
2353 /* Cancel pending event. */
2354 cancel_timeout(dhcp_failover_pool_rebalance
, state
);
2355 state
->sched_balance
= 0;
2357 queued
= dhcp_failover_pool_dobalance(state
, NULL
);
2359 dhcp_failover_send_poolresp(state
, queued
);
2362 dhcp_failover_send_updates(state
);
2364 log_info("peer %s: Got POOLREQ, answering negatively! "
2365 "Peer may be out of leases or database inconsistent.",
2370 * Do the meat of the work common to all forms of pool rebalance. If the
2371 * caller deems it appropriate to transmit POOLREQ messages, it can use the
2372 * sendreq pointer to pass in the address of a FALSE value which this function
2373 * will conditionally turn TRUE if a POOLREQ is determined to be necessary.
2374 * A NULL value may be passed, in which case no action is taken.
2377 dhcp_failover_pool_dobalance(dhcp_failover_state_t
*state
,
2378 isc_boolean_t
*sendreq
)
2380 int lts
, total
, thresh
, hold
, panic
, pass
;
2381 int leases_queued
= 0;
2382 struct lease
*lp
= (struct lease
*)0;
2383 struct lease
*next
= (struct lease
*)0;
2384 struct shared_network
*s
;
2386 binding_state_t peer_lease_state
;
2387 binding_state_t my_lease_state
;
2389 int (*log_func
)(const char *, ...);
2390 const char *result
, *reqlog
;
2392 if (state
-> me
.state
!= normal
)
2395 state
->last_balance
= cur_time
;
2397 for (s
= shared_networks
; s
; s
= s
->next
) {
2398 for (p
= s
->pools
; p
; p
= p
->next
) {
2399 if (p
->failover_peer
!= state
)
2402 /* Right now we're giving the peer half of the free leases.
2403 If we have more leases than the peer (i.e., more than
2404 half), then the number of leases we have, less the number
2405 of leases the peer has, will be how many more leases we
2406 have than the peer has. So if we send half that number
2407 to the peer, we should be even. */
2408 if (p
->failover_peer
->i_am
== primary
) {
2409 lts
= (p
->free_leases
- p
->backup_leases
) / 2;
2410 peer_lease_state
= FTS_BACKUP
;
2411 my_lease_state
= FTS_FREE
;
2414 lts
= (p
->backup_leases
- p
->free_leases
) / 2;
2415 peer_lease_state
= FTS_FREE
;
2416 my_lease_state
= FTS_BACKUP
;
2420 total
= p
->backup_leases
+ p
->free_leases
;
2422 thresh
= ((total
* state
->max_lease_misbalance
) + 50) / 100;
2423 hold
= ((total
* state
->max_lease_ownership
) + 50) / 100;
2426 * If we need leases (so lts is negative) more than negative
2427 * double the thresh%, panic and send poolreq to hopefully wake
2428 * up the peer (but more likely the db is inconsistent). But,
2429 * if this comes out zero, switch to -1 so that the POOLREQ is
2430 * sent on lts == -2 rather than right away at -1.
2432 * Note that we do not subtract -1 from panic all the time
2433 * because thresh% and hold% may come out to the same number,
2434 * and that is correct operation...where thresh% and hold% are
2435 * both -1, we want to send poolreq when lts reaches -3. So,
2436 * "-3 < -2", lts < panic.
2438 panic
= thresh
* -2;
2443 if ((sendreq
!= NULL
) && (lts
< panic
)) {
2444 reqlog
= " (requesting peer rebalance!)";
2445 *sendreq
= ISC_TRUE
;
2449 log_info("balancing pool %lx %s total %d free %d "
2450 "backup %d lts %d max-own (+/-)%d%s",
2452 (p
->shared_network
?
2453 p
->shared_network
->name
: ""), p
->lease_count
,
2454 p
->free_leases
, p
->backup_leases
, lts
, hold
,
2457 /* In the first pass, try to allocate leases to the
2458 * peer which it would normally be responsible for (if
2459 * the lease has a hardware address or client-identifier,
2460 * and the load-balance-algorithm chooses the peer to
2461 * answer that address), up to a hold% excess in the peer's
2462 * favor. In the second pass, just send the oldest (first
2463 * on the list) leases up to a hold% excess in our favor.
2465 * This could make for additional pool rebalance
2466 * events, but preserving MAC possession should be
2470 lease_reference(&lp
, *lq
, MDL
);
2474 lease_dereference(&next
, MDL
);
2476 lease_reference(&next
, lp
->next
, MDL
);
2479 * Stop if the pool is 'balanced enough.'
2481 * The pool is balanced enough if:
2483 * 1) We're on the first run through and the peer has
2484 * its fair share of leases already (lts reaches
2486 * 2) We're on the second run through, we are shifting
2487 * never-used leases, and there is a perfectly even
2488 * balance (lts reaches zero).
2489 * 3) Second run through, we are shifting previously
2490 * used leases, and the local system has its fair
2491 * share but no more (lts reaches hold).
2493 * Note that this is implemented below in 3,2,1 order.
2503 } else if (lts
<= -hold
)
2506 if (pass
|| peer_wants_lease(lp
)) {
2509 lp
->next_binding_state
= peer_lease_state
;
2510 lp
->tstp
= cur_time
;
2511 lp
->starts
= cur_time
;
2513 if (!supersede_lease(lp
, NULL
, 0, 1, 0) ||
2515 log_error("can't commit lease %s on "
2516 "giveaway", piaddr(lp
->ip_addr
));
2519 lease_dereference(&lp
, MDL
);
2521 lease_reference(&lp
, next
, MDL
);
2524 lease_reference(&lp
, *lq
, MDL
);
2529 lease_dereference(&next
, MDL
);
2531 lease_dereference(&lp
, MDL
);
2534 result
= "IMBALANCED";
2535 log_func
= log_error
;
2537 result
= "balanced";
2538 log_func
= log_info
;
2541 log_func("%s pool %lx %s total %d free %d backup %d "
2542 "lts %d max-misbal %d", result
, (unsigned long)p
,
2543 (p
->shared_network
?
2544 p
->shared_network
->name
: ""), p
->lease_count
,
2545 p
->free_leases
, p
->backup_leases
, lts
, thresh
);
2547 /* Recalculate next rebalance event timer. */
2548 dhcp_failover_pool_check(p
);
2555 return leases_queued
;
2558 /* dhcp_failover_pool_check: Called whenever FREE or BACKUP leases change
2559 * states, on both servers. Check the scheduled time to rebalance the pool
2560 * and lower it if applicable.
2563 dhcp_failover_pool_check(struct pool
*pool
)
2565 dhcp_failover_state_t
*peer
;
2569 peer
= pool
->failover_peer
;
2571 if(!peer
|| peer
->me
.state
!= normal
)
2574 /* Estimate the time left until lease exhaustion.
2575 * The first lease on the backup or free lists is also the oldest
2576 * lease. It is reasonable to guess that it will take at least
2577 * as much time for a pool to run out of leases, as the present
2578 * age of the oldest lease (seconds since it expired).
2580 * Note that this isn't so sane of an assumption if the oldest
2581 * lease is a virgin (ends = 0), we wind up sending this against
2582 * the max_balance bounds check.
2584 if(pool
->free
&& pool
->free
->ends
< cur_time
)
2585 est1
= cur_time
- pool
->free
->ends
;
2589 if(pool
->backup
&& pool
->backup
->ends
< cur_time
)
2590 est2
= cur_time
- pool
->backup
->ends
;
2594 /* We don't want to schedule rebalance for when we think we'll run
2595 * out of leases, we want to schedule the rebalance for when we think
2596 * the disparity will be 'large enough' to warrant action.
2598 est1
= ((est1
* peer
->max_lease_misbalance
) + 50) / 100;
2599 est2
= ((est2
* peer
->max_lease_misbalance
) + 50) / 100;
2601 /* Guess when the local system will begin issuing POOLREQ panic
2602 * attacks because "max_lease_misbalance*2" has been exceeded.
2604 if(peer
->i_am
== primary
)
2609 /* Select the smallest time. */
2613 /* Bounded by the maximum configured value. */
2614 if(est1
> peer
->max_balance
)
2615 est1
= peer
->max_balance
;
2617 /* Project this time into the future. */
2620 /* Do not move the time down under the minimum. */
2621 est2
= peer
->last_balance
+ peer
->min_balance
;
2622 if(peer
->last_balance
&& (est1
< est2
))
2625 /* Introduce a random delay. */
2626 est1
+= random() % 5;
2628 /* Do not move the time forward, or reset to the same time. */
2629 if(peer
->sched_balance
) {
2630 if (est1
>= peer
->sched_balance
)
2633 /* We are about to schedule the time down, cancel the
2636 cancel_timeout(dhcp_failover_pool_rebalance
, peer
);
2639 /* The time is different, and lower, use it. */
2640 peer
->sched_balance
= est1
;
2642 #if defined(DEBUG_FAILOVER_TIMING)
2643 log_info("add_timeout +%d dhcp_failover_pool_rebalance",
2644 (int)(est1
- cur_time
));
2648 add_timeout(&tv
, dhcp_failover_pool_rebalance
, peer
,
2649 (tvref_t
)dhcp_failover_state_reference
,
2650 (tvunref_t
)dhcp_failover_state_dereference
);
2653 int dhcp_failover_state_pool_check (dhcp_failover_state_t
*state
)
2655 struct shared_network
*s
;
2658 for (s
= shared_networks
; s
; s
= s
-> next
) {
2659 for (p
= s
-> pools
; p
; p
= p
-> next
) {
2660 if (p
-> failover_peer
!= state
)
2662 dhcp_failover_pool_check (p
);
2668 isc_result_t
dhcp_failover_send_updates (dhcp_failover_state_t
*state
)
2670 struct lease
*lp
= (struct lease
*)0;
2671 isc_result_t status
;
2673 /* Can't update peer if we're not talking to it! */
2674 if (!state
-> link_to_peer
)
2675 return ISC_R_SUCCESS
;
2677 /* If there are acks pending, transmit them prior to potentially
2678 * sending new updates for the same lease.
2680 if (state
->toack_queue_head
!= NULL
)
2681 dhcp_failover_send_acks(state
);
2683 while ((state
-> partner
.max_flying_updates
>
2684 state
-> cur_unacked_updates
) && state
-> update_queue_head
) {
2685 /* Grab the head of the update queue. */
2686 lease_reference (&lp
, state
-> update_queue_head
, MDL
);
2688 /* Send the update to the peer. */
2689 status
= dhcp_failover_send_bind_update (state
, lp
);
2690 if (status
!= ISC_R_SUCCESS
) {
2691 lease_dereference (&lp
, MDL
);
2694 lp
-> flags
&= ~ON_UPDATE_QUEUE
;
2696 /* Take it off the head of the update queue and put the next
2697 item in the update queue at the head. */
2698 lease_dereference (&state
-> update_queue_head
, MDL
);
2699 if (lp
-> next_pending
) {
2700 lease_reference (&state
-> update_queue_head
,
2701 lp
-> next_pending
, MDL
);
2702 lease_dereference (&lp
-> next_pending
, MDL
);
2704 lease_dereference (&state
-> update_queue_tail
, MDL
);
2707 if (state
-> ack_queue_head
) {
2709 (&state
-> ack_queue_tail
-> next_pending
,
2711 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2713 lease_reference (&state
-> ack_queue_head
, lp
, MDL
);
2715 #if defined (POINTER_DEBUG)
2716 if (lp
-> next_pending
) {
2717 log_error ("ack_queue_tail: lp -> next_pending");
2721 lease_reference (&state
-> ack_queue_tail
, lp
, MDL
);
2722 lp
-> flags
|= ON_ACK_QUEUE
;
2723 lease_dereference (&lp
, MDL
);
2725 /* Count the object as an unacked update. */
2726 state
-> cur_unacked_updates
++;
2728 return ISC_R_SUCCESS
;
2731 /* Queue an update for a lease. Always returns 1 at this point - it's
2732 not an error for this to be called on a lease for which there's no
2735 int dhcp_failover_queue_update (struct lease
*lease
, int immediate
)
2737 dhcp_failover_state_t
*state
;
2739 if (!lease
-> pool
||
2740 !lease
-> pool
-> failover_peer
)
2743 /* If it's already on the update queue, leave it there. */
2744 if (lease
-> flags
& ON_UPDATE_QUEUE
)
2747 /* Get the failover state structure for this lease. */
2748 state
= lease
-> pool
-> failover_peer
;
2750 /* If it's on the ack queue, take it off. */
2751 if (lease
-> flags
& ON_ACK_QUEUE
)
2752 dhcp_failover_ack_queue_remove (state
, lease
);
2754 if (state
-> update_queue_head
) {
2755 lease_reference (&state
-> update_queue_tail
-> next_pending
,
2757 lease_dereference (&state
-> update_queue_tail
, MDL
);
2759 lease_reference (&state
-> update_queue_head
, lease
, MDL
);
2761 #if defined (POINTER_DEBUG)
2762 if (lease
-> next_pending
) {
2763 log_error ("next pending on update queue lease.");
2764 #if defined (DEBUG_RC_HISTORY)
2765 dump_rc_history (lease
);
2770 lease_reference (&state
-> update_queue_tail
, lease
, MDL
);
2771 lease
-> flags
|= ON_UPDATE_QUEUE
;
2773 dhcp_failover_send_updates (state
);
2777 int dhcp_failover_send_acks (dhcp_failover_state_t
*state
)
2779 failover_message_t
*msg
= (failover_message_t
*)0;
2781 /* Must commit all leases prior to acking them. */
2782 if (!commit_leases ())
2785 while (state
-> toack_queue_head
) {
2786 failover_message_reference
2787 (&msg
, state
-> toack_queue_head
, MDL
);
2788 failover_message_dereference
2789 (&state
-> toack_queue_head
, MDL
);
2791 failover_message_reference
2792 (&state
-> toack_queue_head
, msg
-> next
, MDL
);
2795 dhcp_failover_send_bind_ack (state
, msg
, 0, (const char *)0);
2797 failover_message_dereference (&msg
, MDL
);
2800 if (state
-> toack_queue_tail
)
2801 failover_message_dereference (&state
-> toack_queue_tail
, MDL
);
2802 state
-> pending_acks
= 0;
2807 void dhcp_failover_toack_queue_timeout (void *vs
)
2809 dhcp_failover_state_t
*state
= vs
;
2811 #if defined (DEBUG_FAILOVER_TIMING)
2812 log_info ("dhcp_failover_toack_queue_timeout");
2815 dhcp_failover_send_acks (state
);
2818 /* Queue an ack for a message. There is currently no way to queue a
2819 negative ack -- these need to be sent directly. */
2821 int dhcp_failover_queue_ack (dhcp_failover_state_t
*state
,
2822 failover_message_t
*msg
)
2826 if (state
-> toack_queue_head
) {
2827 failover_message_reference
2828 (&state
-> toack_queue_tail
-> next
, msg
, MDL
);
2829 failover_message_dereference (&state
-> toack_queue_tail
, MDL
);
2831 failover_message_reference (&state
-> toack_queue_head
,
2834 failover_message_reference (&state
-> toack_queue_tail
, msg
, MDL
);
2836 state
-> pending_acks
++;
2838 /* Flush the toack queue whenever we exceed half the number of
2839 allowed unacked updates. */
2840 if (state
-> pending_acks
>= state
-> partner
.max_flying_updates
/ 2) {
2841 dhcp_failover_send_acks (state
);
2844 /* Schedule a timeout to flush the ack queue. */
2845 if (state
-> pending_acks
> 0) {
2846 #if defined (DEBUG_FAILOVER_TIMING)
2847 log_info ("add_timeout +2 %s",
2848 "dhcp_failover_toack_queue_timeout");
2850 tv
. tv_sec
= cur_time
+ 2;
2853 dhcp_failover_toack_queue_timeout
, state
,
2854 (tvref_t
)dhcp_failover_state_reference
,
2855 (tvunref_t
)dhcp_failover_state_dereference
);
2861 void dhcp_failover_ack_queue_remove (dhcp_failover_state_t
*state
,
2862 struct lease
*lease
)
2866 if (!(lease
-> flags
& ON_ACK_QUEUE
))
2869 if (state
-> ack_queue_head
== lease
) {
2870 lease_dereference (&state
-> ack_queue_head
, MDL
);
2871 if (lease
-> next_pending
) {
2872 lease_reference (&state
-> ack_queue_head
,
2873 lease
-> next_pending
, MDL
);
2874 lease_dereference (&lease
-> next_pending
, MDL
);
2876 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2879 for (lp
= state
-> ack_queue_head
;
2880 lp
&& lp
-> next_pending
!= lease
;
2881 lp
= lp
-> next_pending
)
2887 lease_dereference (&lp
-> next_pending
, MDL
);
2888 if (lease
-> next_pending
) {
2889 lease_reference (&lp
-> next_pending
,
2890 lease
-> next_pending
, MDL
);
2891 lease_dereference (&lease
-> next_pending
, MDL
);
2893 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2894 if (lp
-> next_pending
) {
2895 log_error ("state -> ack_queue_tail");
2898 lease_reference (&state
-> ack_queue_tail
, lp
, MDL
);
2902 lease
-> flags
&= ~ON_ACK_QUEUE
;
2903 /* Multiple acks on one XID is an error and may cause badness. */
2904 lease
->last_xid
= 0;
2905 /* XXX: this violates draft-failover. We can't send another
2906 * update just because we forgot about an old one that hasn't
2909 state
-> cur_unacked_updates
--;
2912 * When updating leases as a result of an ack, we defer the commit
2913 * for performance reasons. When there are no more acks pending,
2916 if (state
-> cur_unacked_updates
== 0) {
2921 isc_result_t
dhcp_failover_state_set_value (omapi_object_t
*h
,
2923 omapi_data_string_t
*name
,
2924 omapi_typed_data_t
*value
)
2926 isc_result_t status
;
2928 if (h
-> type
!= dhcp_type_failover_state
)
2929 return ISC_R_INVALIDARG
;
2931 /* This list of successful returns is completely wrong, but the
2932 fastest way to make dhcpctl do something vaguely sane when
2933 you try to change the local state. */
2935 if (!omapi_ds_strcmp (name
, "name")) {
2936 return ISC_R_SUCCESS
;
2937 } else if (!omapi_ds_strcmp (name
, "partner-address")) {
2938 return ISC_R_SUCCESS
;
2939 } else if (!omapi_ds_strcmp (name
, "local-address")) {
2940 return ISC_R_SUCCESS
;
2941 } else if (!omapi_ds_strcmp (name
, "partner-port")) {
2942 return ISC_R_SUCCESS
;
2943 } else if (!omapi_ds_strcmp (name
, "local-port")) {
2944 return ISC_R_SUCCESS
;
2945 } else if (!omapi_ds_strcmp (name
, "max-outstanding-updates")) {
2946 return ISC_R_SUCCESS
;
2947 } else if (!omapi_ds_strcmp (name
, "mclt")) {
2948 return ISC_R_SUCCESS
;
2949 } else if (!omapi_ds_strcmp (name
, "load-balance-max-secs")) {
2950 return ISC_R_SUCCESS
;
2951 } else if (!omapi_ds_strcmp (name
, "load-balance-hba")) {
2952 return ISC_R_SUCCESS
;
2953 } else if (!omapi_ds_strcmp (name
, "partner-state")) {
2954 return ISC_R_SUCCESS
;
2955 } else if (!omapi_ds_strcmp (name
, "local-state")) {
2957 status
= omapi_get_int_value (&l
, value
);
2958 if (status
!= ISC_R_SUCCESS
)
2960 return dhcp_failover_set_state ((dhcp_failover_state_t
*)h
, l
);
2961 } else if (!omapi_ds_strcmp (name
, "partner-stos")) {
2962 return ISC_R_SUCCESS
;
2963 } else if (!omapi_ds_strcmp (name
, "local-stos")) {
2964 return ISC_R_SUCCESS
;
2965 } else if (!omapi_ds_strcmp (name
, "hierarchy")) {
2966 return ISC_R_SUCCESS
;
2967 } else if (!omapi_ds_strcmp (name
, "last-packet-sent")) {
2968 return ISC_R_SUCCESS
;
2969 } else if (!omapi_ds_strcmp (name
, "last-timestamp-received")) {
2970 return ISC_R_SUCCESS
;
2971 } else if (!omapi_ds_strcmp (name
, "skew")) {
2972 return ISC_R_SUCCESS
;
2973 } else if (!omapi_ds_strcmp (name
, "max-response-delay")) {
2974 return ISC_R_SUCCESS
;
2975 } else if (!omapi_ds_strcmp (name
, "cur-unacked-updates")) {
2976 return ISC_R_SUCCESS
;
2979 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
2980 return (*(h
-> inner
-> type
-> set_value
))
2981 (h
-> inner
, id
, name
, value
);
2982 return ISC_R_NOTFOUND
;
2985 void dhcp_failover_keepalive (void *vs
)
2989 void dhcp_failover_reconnect (void *vs
)
2991 dhcp_failover_state_t
*state
= vs
;
2992 isc_result_t status
;
2995 #if defined (DEBUG_FAILOVER_TIMING)
2996 log_info ("dhcp_failover_reconnect");
2998 /* If we already connected the other way, let the connection
2999 recovery code initiate any retry that may be required. */
3000 if (state
-> link_to_peer
)
3003 status
= dhcp_failover_link_initiate ((omapi_object_t
*)state
);
3004 if (status
!= ISC_R_SUCCESS
&& status
!= ISC_R_INCOMPLETE
) {
3005 log_info ("failover peer %s: %s", state
-> name
,
3006 isc_result_totext (status
));
3007 #if defined (DEBUG_FAILOVER_TIMING)
3008 log_info ("add_timeout +90 %s",
3009 "dhcp_failover_listener_restart");
3011 tv
. tv_sec
= cur_time
+ 90;
3014 dhcp_failover_listener_restart
, state
,
3015 (tvref_t
)dhcp_failover_state_reference
,
3016 (tvunref_t
)dhcp_failover_state_dereference
);
3020 void dhcp_failover_startup_timeout (void *vs
)
3022 dhcp_failover_state_t
*state
= vs
;
3024 #if defined (DEBUG_FAILOVER_TIMING)
3025 log_info ("dhcp_failover_startup_timeout");
3028 dhcp_failover_state_transition (state
, "disconnect");
3031 void dhcp_failover_link_startup_timeout (void *vl
)
3033 dhcp_failover_link_t
*link
= vl
;
3036 for (p
= (omapi_object_t
*)link
; p
-> inner
; p
= p
-> inner
)
3038 for (; p
; p
= p
-> outer
)
3039 if (p
-> type
== omapi_type_connection
)
3042 log_info ("failover: link startup timeout");
3043 omapi_disconnect (p
, 1);
3047 void dhcp_failover_listener_restart (void *vs
)
3049 dhcp_failover_state_t
*state
= vs
;
3050 isc_result_t status
;
3053 #if defined (DEBUG_FAILOVER_TIMING)
3054 log_info ("dhcp_failover_listener_restart");
3057 status
= dhcp_failover_listen ((omapi_object_t
*)state
);
3058 if (status
!= ISC_R_SUCCESS
) {
3059 log_info ("failover peer %s: %s", state
-> name
,
3060 isc_result_totext (status
));
3061 #if defined (DEBUG_FAILOVER_TIMING)
3062 log_info ("add_timeout +90 %s",
3063 "dhcp_failover_listener_restart");
3065 tv
. tv_sec
= cur_time
+ 90;
3068 dhcp_failover_listener_restart
, state
,
3069 (tvref_t
)dhcp_failover_state_reference
,
3070 (tvunref_t
)dhcp_failover_state_dereference
);
3074 isc_result_t
dhcp_failover_state_get_value (omapi_object_t
*h
,
3076 omapi_data_string_t
*name
,
3077 omapi_value_t
**value
)
3079 dhcp_failover_state_t
*s
;
3080 struct option_cache
*oc
;
3081 struct data_string ds
;
3082 isc_result_t status
;
3084 if (h
-> type
!= dhcp_type_failover_state
)
3085 return ISC_R_INVALIDARG
;
3086 s
= (dhcp_failover_state_t
*)h
;
3088 if (!omapi_ds_strcmp (name
, "name")) {
3090 return omapi_make_string_value (value
,
3091 name
, s
-> name
, MDL
);
3092 return ISC_R_NOTFOUND
;
3093 } else if (!omapi_ds_strcmp (name
, "partner-address")) {
3094 oc
= s
-> partner
.address
;
3096 memset (&ds
, 0, sizeof ds
);
3097 if (!evaluate_option_cache (&ds
, (struct packet
*)0,
3099 (struct client_state
*)0,
3100 (struct option_state
*)0,
3101 (struct option_state
*)0,
3102 &global_scope
, oc
, MDL
)) {
3103 return ISC_R_NOTFOUND
;
3105 status
= omapi_make_const_value (value
,
3106 name
, ds
.data
, ds
.len
, MDL
);
3107 /* Disgusting kludge: */
3108 if (oc
== s
-> me
.address
&& !s
-> server_identifier
.len
)
3109 data_string_copy (&s
-> server_identifier
, &ds
, MDL
);
3110 data_string_forget (&ds
, MDL
);
3112 } else if (!omapi_ds_strcmp (name
, "local-address")) {
3113 oc
= s
-> me
.address
;
3115 } else if (!omapi_ds_strcmp (name
, "partner-port")) {
3116 return omapi_make_int_value (value
, name
,
3117 s
-> partner
.port
, MDL
);
3118 } else if (!omapi_ds_strcmp (name
, "local-port")) {
3119 return omapi_make_int_value (value
,
3120 name
, s
-> me
.port
, MDL
);
3121 } else if (!omapi_ds_strcmp (name
, "max-outstanding-updates")) {
3122 return omapi_make_uint_value (value
, name
,
3123 s
-> me
.max_flying_updates
,
3125 } else if (!omapi_ds_strcmp (name
, "mclt")) {
3126 return omapi_make_uint_value (value
, name
, s
-> mclt
, MDL
);
3127 } else if (!omapi_ds_strcmp (name
, "load-balance-max-secs")) {
3128 return omapi_make_int_value (value
, name
,
3129 s
-> load_balance_max_secs
, MDL
);
3130 } else if (!omapi_ds_strcmp (name
, "load-balance-hba")) {
3132 return omapi_make_const_value (value
, name
,
3134 return ISC_R_NOTFOUND
;
3135 } else if (!omapi_ds_strcmp (name
, "partner-state")) {
3136 return omapi_make_uint_value (value
, name
,
3137 s
-> partner
.state
, MDL
);
3138 } else if (!omapi_ds_strcmp (name
, "local-state")) {
3139 return omapi_make_uint_value (value
, name
,
3140 s
-> me
.state
, MDL
);
3141 } else if (!omapi_ds_strcmp (name
, "partner-stos")) {
3142 return omapi_make_int_value (value
, name
,
3143 s
-> partner
.stos
, MDL
);
3144 } else if (!omapi_ds_strcmp (name
, "local-stos")) {
3145 return omapi_make_int_value (value
, name
,
3147 } else if (!omapi_ds_strcmp (name
, "hierarchy")) {
3148 return omapi_make_uint_value (value
, name
, s
-> i_am
, MDL
);
3149 } else if (!omapi_ds_strcmp (name
, "last-packet-sent")) {
3150 return omapi_make_int_value (value
, name
,
3151 s
-> last_packet_sent
, MDL
);
3152 } else if (!omapi_ds_strcmp (name
, "last-timestamp-received")) {
3153 return omapi_make_int_value (value
, name
,
3154 s
-> last_timestamp_received
,
3156 } else if (!omapi_ds_strcmp (name
, "skew")) {
3157 return omapi_make_int_value (value
, name
, s
-> skew
, MDL
);
3158 } else if (!omapi_ds_strcmp (name
, "max-response-delay")) {
3159 return omapi_make_uint_value (value
, name
,
3160 s
-> me
.max_response_delay
,
3162 } else if (!omapi_ds_strcmp (name
, "cur-unacked-updates")) {
3163 return omapi_make_int_value (value
, name
,
3164 s
-> cur_unacked_updates
, MDL
);
3167 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
3168 return (*(h
-> inner
-> type
-> get_value
))
3169 (h
-> inner
, id
, name
, value
);
3170 return ISC_R_NOTFOUND
;
3173 isc_result_t
dhcp_failover_state_destroy (omapi_object_t
*h
,
3174 const char *file
, int line
)
3176 dhcp_failover_state_t
*s
;
3178 if (h
-> type
!= dhcp_type_failover_state
)
3179 return ISC_R_INVALIDARG
;
3180 s
= (dhcp_failover_state_t
*)h
;
3182 if (s
-> link_to_peer
)
3183 dhcp_failover_link_dereference (&s
-> link_to_peer
, file
, line
);
3185 dfree (s
-> name
, MDL
);
3186 s
-> name
= (char *)0;
3188 if (s
-> partner
.address
)
3189 option_cache_dereference (&s
-> partner
.address
, file
, line
);
3190 if (s
-> me
.address
)
3191 option_cache_dereference (&s
-> me
.address
, file
, line
);
3193 dfree (s
-> hba
, file
, line
);
3194 s
-> hba
= (u_int8_t
*)0;
3196 if (s
-> update_queue_head
)
3197 lease_dereference (&s
-> update_queue_head
, file
, line
);
3198 if (s
-> update_queue_tail
)
3199 lease_dereference (&s
-> update_queue_tail
, file
, line
);
3200 if (s
-> ack_queue_head
)
3201 lease_dereference (&s
-> ack_queue_head
, file
, line
);
3202 if (s
-> ack_queue_tail
)
3203 lease_dereference (&s
-> ack_queue_tail
, file
, line
);
3204 if (s
-> send_update_done
)
3205 lease_dereference (&s
-> send_update_done
, file
, line
);
3206 if (s
-> toack_queue_head
)
3207 failover_message_dereference (&s
-> toack_queue_head
,
3209 if (s
-> toack_queue_tail
)
3210 failover_message_dereference (&s
-> toack_queue_tail
,
3212 return ISC_R_SUCCESS
;
3215 /* Write all the published values associated with the object through the
3216 specified connection. */
3218 isc_result_t
dhcp_failover_state_stuff (omapi_object_t
*c
,
3222 dhcp_failover_state_t
*s
;
3223 omapi_connection_object_t
*conn
;
3224 isc_result_t status
;
3226 if (c
-> type
!= omapi_type_connection
)
3227 return ISC_R_INVALIDARG
;
3228 conn
= (omapi_connection_object_t
*)c
;
3230 if (h
-> type
!= dhcp_type_failover_state
)
3231 return ISC_R_INVALIDARG
;
3232 s
= (dhcp_failover_state_t
*)h
;
3234 status
= omapi_connection_put_name (c
, "name");
3235 if (status
!= ISC_R_SUCCESS
)
3237 status
= omapi_connection_put_string (c
, s
-> name
);
3238 if (status
!= ISC_R_SUCCESS
)
3241 status
= omapi_connection_put_name (c
, "partner-address");
3242 if (status
!= ISC_R_SUCCESS
)
3244 status
= omapi_connection_put_uint32 (c
, sizeof s
-> partner
.address
);
3245 if (status
!= ISC_R_SUCCESS
)
3247 status
= omapi_connection_copyin (c
, (u_int8_t
*)&s
-> partner
.address
,
3248 sizeof s
-> partner
.address
);
3249 if (status
!= ISC_R_SUCCESS
)
3252 status
= omapi_connection_put_name (c
, "partner-port");
3253 if (status
!= ISC_R_SUCCESS
)
3255 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3256 if (status
!= ISC_R_SUCCESS
)
3258 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> partner
.port
);
3259 if (status
!= ISC_R_SUCCESS
)
3262 status
= omapi_connection_put_name (c
, "local-address");
3263 if (status
!= ISC_R_SUCCESS
)
3265 status
= omapi_connection_put_uint32 (c
, sizeof s
-> me
.address
);
3266 if (status
!= ISC_R_SUCCESS
)
3268 status
= omapi_connection_copyin (c
, (u_int8_t
*)&s
-> me
.address
,
3269 sizeof s
-> me
.address
);
3270 if (status
!= ISC_R_SUCCESS
)
3273 status
= omapi_connection_put_name (c
, "local-port");
3274 if (status
!= ISC_R_SUCCESS
)
3276 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3277 if (status
!= ISC_R_SUCCESS
)
3279 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> me
.port
);
3280 if (status
!= ISC_R_SUCCESS
)
3283 status
= omapi_connection_put_name (c
, "max-outstanding-updates");
3284 if (status
!= ISC_R_SUCCESS
)
3286 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3287 if (status
!= ISC_R_SUCCESS
)
3289 status
= omapi_connection_put_uint32 (c
,
3290 s
-> me
.max_flying_updates
);
3291 if (status
!= ISC_R_SUCCESS
)
3294 status
= omapi_connection_put_name (c
, "mclt");
3295 if (status
!= ISC_R_SUCCESS
)
3297 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3298 if (status
!= ISC_R_SUCCESS
)
3300 status
= omapi_connection_put_uint32 (c
, s
-> mclt
);
3301 if (status
!= ISC_R_SUCCESS
)
3304 status
= omapi_connection_put_name (c
, "load-balance-max-secs");
3305 if (status
!= ISC_R_SUCCESS
)
3307 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3308 if (status
!= ISC_R_SUCCESS
)
3310 status
= (omapi_connection_put_uint32
3311 (c
, (u_int32_t
)s
-> load_balance_max_secs
));
3312 if (status
!= ISC_R_SUCCESS
)
3317 status
= omapi_connection_put_name (c
, "load-balance-hba");
3318 if (status
!= ISC_R_SUCCESS
)
3320 status
= omapi_connection_put_uint32 (c
, 32);
3321 if (status
!= ISC_R_SUCCESS
)
3323 status
= omapi_connection_copyin (c
, s
-> hba
, 32);
3324 if (status
!= ISC_R_SUCCESS
)
3328 status
= omapi_connection_put_name (c
, "partner-state");
3329 if (status
!= ISC_R_SUCCESS
)
3331 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3332 if (status
!= ISC_R_SUCCESS
)
3334 status
= omapi_connection_put_uint32 (c
, s
-> partner
.state
);
3335 if (status
!= ISC_R_SUCCESS
)
3338 status
= omapi_connection_put_name (c
, "local-state");
3339 if (status
!= ISC_R_SUCCESS
)
3341 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3342 if (status
!= ISC_R_SUCCESS
)
3344 status
= omapi_connection_put_uint32 (c
, s
-> me
.state
);
3345 if (status
!= ISC_R_SUCCESS
)
3348 status
= omapi_connection_put_name (c
, "partner-stos");
3349 if (status
!= ISC_R_SUCCESS
)
3351 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3352 if (status
!= ISC_R_SUCCESS
)
3354 status
= omapi_connection_put_uint32 (c
,
3355 (u_int32_t
)s
-> partner
.stos
);
3356 if (status
!= ISC_R_SUCCESS
)
3359 status
= omapi_connection_put_name (c
, "local-stos");
3360 if (status
!= ISC_R_SUCCESS
)
3362 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3363 if (status
!= ISC_R_SUCCESS
)
3365 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> me
.stos
);
3366 if (status
!= ISC_R_SUCCESS
)
3369 status
= omapi_connection_put_name (c
, "hierarchy");
3370 if (status
!= ISC_R_SUCCESS
)
3372 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3373 if (status
!= ISC_R_SUCCESS
)
3375 status
= omapi_connection_put_uint32 (c
, s
-> i_am
);
3376 if (status
!= ISC_R_SUCCESS
)
3379 status
= omapi_connection_put_name (c
, "last-packet-sent");
3380 if (status
!= ISC_R_SUCCESS
)
3382 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3383 if (status
!= ISC_R_SUCCESS
)
3385 status
= (omapi_connection_put_uint32
3386 (c
, (u_int32_t
)s
-> last_packet_sent
));
3387 if (status
!= ISC_R_SUCCESS
)
3390 status
= omapi_connection_put_name (c
, "last-timestamp-received");
3391 if (status
!= ISC_R_SUCCESS
)
3393 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3394 if (status
!= ISC_R_SUCCESS
)
3396 status
= (omapi_connection_put_uint32
3397 (c
, (u_int32_t
)s
-> last_timestamp_received
));
3398 if (status
!= ISC_R_SUCCESS
)
3401 status
= omapi_connection_put_name (c
, "skew");
3402 if (status
!= ISC_R_SUCCESS
)
3404 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3405 if (status
!= ISC_R_SUCCESS
)
3407 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> skew
);
3408 if (status
!= ISC_R_SUCCESS
)
3411 status
= omapi_connection_put_name (c
, "max-response-delay");
3412 if (status
!= ISC_R_SUCCESS
)
3414 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3415 if (status
!= ISC_R_SUCCESS
)
3417 status
= (omapi_connection_put_uint32
3418 (c
, (u_int32_t
)s
-> me
.max_response_delay
));
3419 if (status
!= ISC_R_SUCCESS
)
3422 status
= omapi_connection_put_name (c
, "cur-unacked-updates");
3423 if (status
!= ISC_R_SUCCESS
)
3425 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3426 if (status
!= ISC_R_SUCCESS
)
3428 status
= (omapi_connection_put_uint32
3429 (c
, (u_int32_t
)s
-> cur_unacked_updates
));
3430 if (status
!= ISC_R_SUCCESS
)
3433 if (h
-> inner
&& h
-> inner
-> type
-> stuff_values
)
3434 return (*(h
-> inner
-> type
-> stuff_values
)) (c
, id
,
3436 return ISC_R_SUCCESS
;
3439 isc_result_t
dhcp_failover_state_lookup (omapi_object_t
**sp
,
3441 omapi_object_t
*ref
)
3443 omapi_value_t
*tv
= (omapi_value_t
*)0;
3444 isc_result_t status
;
3445 dhcp_failover_state_t
*s
;
3448 return ISC_R_NOKEYS
;
3450 /* First see if we were sent a handle. */
3451 status
= omapi_get_value_str (ref
, id
, "handle", &tv
);
3452 if (status
== ISC_R_SUCCESS
) {
3453 status
= omapi_handle_td_lookup (sp
, tv
-> value
);
3455 omapi_value_dereference (&tv
, MDL
);
3456 if (status
!= ISC_R_SUCCESS
)
3459 /* Don't return the object if the type is wrong. */
3460 if ((*sp
) -> type
!= dhcp_type_failover_state
) {
3461 omapi_object_dereference (sp
, MDL
);
3462 return ISC_R_INVALIDARG
;
3466 /* Look the failover state up by peer name. */
3467 status
= omapi_get_value_str (ref
, id
, "name", &tv
);
3468 if (status
== ISC_R_SUCCESS
) {
3469 for (s
= failover_states
; s
; s
= s
-> next
) {
3470 unsigned l
= strlen (s
-> name
);
3471 if (l
== tv
-> value
-> u
.buffer
.len
&&
3473 tv
-> value
-> u
.buffer
.value
, l
))
3476 omapi_value_dereference (&tv
, MDL
);
3478 /* If we already have a lease, and it's not the same one,
3479 then the query was invalid. */
3480 if (*sp
&& *sp
!= (omapi_object_t
*)s
) {
3481 omapi_object_dereference (sp
, MDL
);
3482 return ISC_R_KEYCONFLICT
;
3485 omapi_object_dereference (sp
, MDL
);
3486 return ISC_R_NOTFOUND
;
3488 /* XXX fix so that hash lookup itself creates
3489 XXX the reference. */
3490 omapi_object_reference (sp
, (omapi_object_t
*)s
, MDL
);
3493 /* If we get to here without finding a lease, no valid key was
3496 return ISC_R_NOKEYS
;
3497 return ISC_R_SUCCESS
;
3500 isc_result_t
dhcp_failover_state_create (omapi_object_t
**sp
,
3503 return ISC_R_NOTIMPLEMENTED
;
3506 isc_result_t
dhcp_failover_state_remove (omapi_object_t
*sp
,
3509 return ISC_R_NOTIMPLEMENTED
;
3512 int dhcp_failover_state_match (dhcp_failover_state_t
*state
,
3513 u_int8_t
*addr
, unsigned addrlen
)
3515 struct data_string ds
;
3518 memset (&ds
, 0, sizeof ds
);
3519 if (evaluate_option_cache (&ds
, (struct packet
*)0,
3521 (struct client_state
*)0,
3522 (struct option_state
*)0,
3523 (struct option_state
*)0,
3525 state
-> partner
.address
, MDL
)) {
3526 for (i
= 0; i
+ addrlen
- 1 < ds
.len
; i
+= addrlen
) {
3527 if (!memcmp (&ds
.data
[i
],
3529 data_string_forget (&ds
, MDL
);
3533 data_string_forget (&ds
, MDL
);
3539 dhcp_failover_state_match_by_name(state
, name
)
3540 dhcp_failover_state_t
*state
;
3541 failover_option_t
*name
;
3543 if ((strlen(state
->name
) == name
->count
) &&
3544 (memcmp(state
->name
, name
->data
, name
->count
) == 0))
3550 const char *dhcp_failover_reject_reason_print (int reason
)
3552 static char resbuf
[sizeof("Undefined-255: This reason code is not defined "
3553 "in the protocol standard.")];
3555 if ((reason
> 0xff) || (reason
< 0))
3556 return "Reason code out of range.";
3559 case FTR_ILLEGAL_IP_ADDR
:
3560 return "Illegal IP address (not part of any address pool).";
3562 case FTR_FATAL_CONFLICT
:
3563 return "Fatal conflict exists: address in use by other client.";
3565 case FTR_MISSING_BINDINFO
:
3566 return "Missing binding information.";
3568 case FTR_TIMEMISMATCH
:
3569 return "Connection rejected, time mismatch too great.";
3571 case FTR_INVALID_MCLT
:
3572 return "Connection rejected, invalid MCLT.";
3574 case FTR_MISC_REJECT
:
3575 return "Connection rejected, unknown reason.";
3577 case FTR_DUP_CONNECTION
:
3578 return "Connection rejected, duplicate connection.";
3580 case FTR_INVALID_PARTNER
:
3581 return "Connection rejected, invalid failover partner.";
3583 case FTR_TLS_UNSUPPORTED
:
3584 return "TLS not supported.";
3586 case FTR_TLS_UNCONFIGURED
:
3587 return "TLS supported but not configured.";
3589 case FTR_TLS_REQUIRED
:
3590 return "TLS required but not supported by partner.";
3592 case FTR_DIGEST_UNSUPPORTED
:
3593 return "Message digest not supported.";
3595 case FTR_DIGEST_UNCONFIGURED
:
3596 return "Message digest not configured.";
3598 case FTR_VERSION_MISMATCH
:
3599 return "Protocol version mismatch.";
3601 case FTR_OUTDATED_BIND_INFO
:
3602 return "Outdated binding information.";
3604 case FTR_LESS_CRIT_BIND_INFO
:
3605 return "Less critical binding information.";
3607 case FTR_NO_TRAFFIC
:
3608 return "No traffic within sufficient time.";
3610 case FTR_HBA_CONFLICT
:
3611 return "Hash bucket assignment conflict.";
3613 case FTR_IP_NOT_RESERVED
:
3614 return "IP not reserved on this server.";
3616 case FTR_IP_DIGEST_FAILURE
:
3617 return "Message digest failed to compare.";
3619 case FTR_IP_MISSING_DIGEST
:
3620 return "Missing message digest.";
3623 return "Unknown Error.";
3626 sprintf(resbuf
, "Undefined-%d: This reason code is not defined in the "
3627 "protocol standard.", reason
);
3632 const char *dhcp_failover_state_name_print (enum failover_state state
)
3637 return "unknown-state";
3640 return "partner-down";
3646 return "conflict-done";
3648 case communications_interrupted
:
3649 return "communications-interrupted";
3651 case resolution_interrupted
:
3652 return "resolution-interrupted";
3654 case potential_conflict
:
3655 return "potential-conflict";
3661 return "recover-done";
3664 return "recover-wait";
3677 const char *dhcp_failover_message_name (unsigned type
)
3679 static char messbuf
[sizeof("unknown-message-255")];
3682 return "invalid-message";
3686 return "pool-request";
3689 return "pool-response";
3692 return "bind-update";
3700 case FTM_CONNECTACK
:
3701 return "connect-ack";
3704 return "update-request";
3707 return "update-done";
3710 return "update-request-all";
3718 case FTM_DISCONNECT
:
3719 return "disconnect";
3722 sprintf(messbuf
, "unknown-message-%u", type
);
3727 const char *dhcp_failover_option_name (unsigned type
)
3729 static char optbuf
[sizeof("unknown-option-65535")];
3732 return "invalid-option";
3735 case FTO_ADDRESSES_TRANSFERRED
:
3736 return "addresses-transferred";
3738 case FTO_ASSIGNED_IP_ADDRESS
:
3739 return "assigned-ip-address";
3741 case FTO_BINDING_STATUS
:
3742 return "binding-status";
3744 case FTO_CLIENT_IDENTIFIER
:
3745 return "client-identifier";
3756 case FTO_DELAYED_SERVICE
:
3757 return "delayed-service";
3765 case FTO_LEASE_EXPIRY
:
3766 return "lease-expiry";
3768 case FTO_MAX_UNACKED
:
3769 return "max-unacked";
3777 case FTO_MESSAGE_DIGEST
:
3778 return "message-digest";
3780 case FTO_POTENTIAL_EXPIRY
:
3781 return "potential-expiry";
3783 case FTO_PROTOCOL_VERSION
:
3784 return "protocol-version";
3786 case FTO_RECEIVE_TIMER
:
3787 return "receive-timer";
3789 case FTO_REJECT_REASON
:
3790 return "reject-reason";
3792 case FTO_RELATIONSHIP_NAME
:
3793 return "relationship-name";
3795 case FTO_REPLY_OPTIONS
:
3796 return "reply-options";
3798 case FTO_REQUEST_OPTIONS
:
3799 return "request-options";
3801 case FTO_SERVER_FLAGS
:
3802 return "server-flags";
3804 case FTO_SERVER_STATE
:
3805 return "server-state";
3813 case FTO_TLS_REQUEST
:
3814 return "tls-request";
3816 case FTO_VENDOR_CLASS
:
3817 return "vendor-class";
3819 case FTO_VENDOR_OPTIONS
:
3820 return "vendor-options";
3823 sprintf(optbuf
, "unknown-option-%u", type
);
3828 failover_option_t
*dhcp_failover_option_printf (unsigned code
,
3832 const char *fmt
, ...)
3837 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3838 * It is unclear what the effects of truncation here are, or
3839 * how that condition should be handled. It seems that this
3840 * function is used for formatting messages in the failover
3841 * command channel. For now the safest thing is for
3842 * overflow-truncation to cause a fatal log.
3845 if (vsnprintf (tbuf
, sizeof tbuf
, fmt
, va
) >= sizeof tbuf
)
3846 log_fatal ("%s: vsnprintf would truncate",
3847 "dhcp_failover_make_option");
3850 return dhcp_failover_make_option (code
, obuf
, obufix
, obufmax
,
3851 strlen (tbuf
), tbuf
);
3854 failover_option_t
*dhcp_failover_make_option (unsigned code
,
3855 char *obuf
, unsigned *obufix
,
3856 unsigned obufmax
, ...)
3859 struct failover_option_info
*info
;
3861 unsigned size
, count
;
3867 #if defined (DEBUG_FAILOVER_MESSAGES)
3871 /* Note that the failover_option structure is used differently on
3872 input than on output - on input, count is an element count, and
3873 on output it's the number of bytes total in the option, including
3874 the option code and option length. */
3875 failover_option_t option
, *op
;
3878 /* Bogus option code? */
3879 if (code
< 1 || code
> FTO_MAX
|| ft_options
[code
].type
== FT_UNDEF
) {
3880 return &null_failover_option
;
3882 info
= &ft_options
[code
];
3884 va_start (va
, obufmax
);
3886 /* Get the number of elements and the size of the buffer we need
3888 if (info
-> type
== FT_DDNS
|| info
-> type
== FT_DDNS1
) {
3889 count
= info
-> type
== FT_DDNS
? 1 : 2;
3890 size
= va_arg (va
, int) + count
;
3892 /* Find out how many items in this list. */
3893 if (info
-> num_present
)
3894 count
= info
-> num_present
;
3896 count
= va_arg (va
, int);
3898 /* Figure out size. */
3899 switch (info
-> type
) {
3906 case FT_TEXT_OR_BYTES
:
3908 txt
= va_arg (va
, char *);
3913 ilen
= va_arg (va
, unsigned);
3914 size
= count
* ilen
;
3926 /* shouldn't get here. */
3927 log_fatal ("bogus type in failover_make_option: %d",
3929 return &null_failover_option
;
3935 /* Allocate a buffer for the option. */
3936 option
.count
= size
;
3937 option
.data
= dmalloc (option
.count
, MDL
);
3940 return &null_failover_option
;
3943 /* Put in the option code and option length. */
3944 putUShort (option
.data
, code
);
3945 putUShort (&option
.data
[2], size
- 4);
3947 #if defined (DEBUG_FAILOVER_MESSAGES)
3948 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3949 * It is unclear what the effects of truncation here are, or
3950 * how that condition should be handled. It seems that this
3951 * message may be sent over the failover command channel.
3952 * For now the safest thing is for overflow-truncation to cause
3955 if (snprintf (tbuf
, sizeof tbuf
, " (%s<%d>", info
-> name
,
3956 option
.count
) >= sizeof tbuf
)
3957 log_fatal ("dhcp_failover_make_option: tbuf overflow");
3958 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3961 /* Now put in the data. */
3962 switch (info
-> type
) {
3964 for (i
= 0; i
< count
; i
++) {
3965 val
= va_arg (va
, unsigned);
3966 #if defined (DEBUG_FAILOVER_MESSAGES)
3967 /* %Audit% Cannot exceed 24 bytes. %2004.06.17,Safe% */
3968 sprintf (tbuf
, " %d", val
);
3969 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3971 option
.data
[i
+ 4] = val
;
3976 for (i
= 0; i
< count
; i
++) {
3977 iaddr
= va_arg (va
, u_int8_t
*);
3979 dfree (option
.data
, MDL
);
3980 log_error ("IP addrlen=%d, should be 4.",
3983 return &null_failover_option
;
3986 #if defined (DEBUG_FAILOVER_MESSAGES)
3987 /*%Audit% Cannot exceed 17 bytes. %2004.06.17,Safe%*/
3988 sprintf (tbuf
, " %u.%u.%u.%u",
3989 iaddr
[0], iaddr
[1], iaddr
[2], iaddr
[3]);
3990 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3992 memcpy (&option
.data
[4 + i
* ilen
], iaddr
, ilen
);
3997 for (i
= 0; i
< count
; i
++) {
3998 val
= va_arg (va
, unsigned);
3999 #if defined (DEBUG_FAILOVER_MESSAGES)
4000 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
4001 sprintf (tbuf
, " %d", val
);
4002 failover_print (obuf
, obufix
, obufmax
, tbuf
);
4004 putULong (&option
.data
[4 + i
* 4], val
);
4010 bval
= va_arg (va
, u_int8_t
*);
4011 #if defined (DEBUG_FAILOVER_MESSAGES)
4012 for (i
= 0; i
< count
; i
++) {
4013 /* 23 bytes plus nul, safe. */
4014 sprintf (tbuf
, " %d", bval
[i
]);
4015 failover_print (obuf
, obufix
, obufmax
, tbuf
);
4018 memcpy (&option
.data
[4], bval
, count
);
4021 /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
4022 terminated. Note that the caller should be careful not
4023 to provide a format and data that amount to more than 256
4024 bytes of data, since it will cause a fatal error. */
4025 case FT_TEXT_OR_BYTES
:
4027 #if defined (DEBUG_FAILOVER_MESSAGES)
4028 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
4029 * It is unclear what the effects of truncation here are, or
4030 * how that condition should be handled. It seems that this
4031 * function is used for formatting messages in the failover
4032 * command channel. For now the safest thing is for
4033 * overflow-truncation to cause a fatal log.
4035 if (snprintf (tbuf
, sizeof tbuf
, "\"%s\"", txt
) >= sizeof tbuf
)
4036 log_fatal ("dhcp_failover_make_option: tbuf overflow");
4037 failover_print (obuf
, obufix
, obufmax
, tbuf
);
4039 memcpy (&option
.data
[4], txt
, count
);
4044 option
.data
[4] = va_arg (va
, unsigned);
4046 option
.data
[5] = va_arg (va
, unsigned);
4047 bval
= va_arg (va
, u_int8_t
*);
4048 memcpy (&option
.data
[4 + count
], bval
, size
- count
- 4);
4049 #if defined (DEBUG_FAILOVER_MESSAGES)
4050 for (i
= 4; i
< size
; i
++) {
4051 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
4052 sprintf (tbuf
, " %d", option
.data
[i
]);
4053 failover_print (obuf
, obufix
, obufmax
, tbuf
);
4059 for (i
= 0; i
< count
; i
++) {
4060 val
= va_arg (va
, u_int32_t
);
4061 #if defined (DEBUG_FAILOVER_MESSAGES)
4062 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
4063 sprintf (tbuf
, " %d", val
);
4064 failover_print (obuf
, obufix
, obufmax
, tbuf
);
4066 putUShort (&option
.data
[4 + i
* 2], val
);
4075 #if defined DEBUG_FAILOVER_MESSAGES
4076 failover_print (obuf
, obufix
, obufmax
, ")");
4080 /* Now allocate a place to store what we just set up. */
4081 op
= dmalloc (sizeof (failover_option_t
), MDL
);
4083 dfree (option
.data
, MDL
);
4084 return &null_failover_option
;
4091 /* Send a failover message header. */
4093 isc_result_t
dhcp_failover_put_message (dhcp_failover_link_t
*link
,
4094 omapi_object_t
*connection
,
4095 int msg_type
, u_int32_t xid
, ...)
4101 failover_option_t
*option
;
4102 unsigned char *opbuf
;
4103 isc_result_t status
= ISC_R_SUCCESS
;
4107 /* Run through the argument list once to compute the length of
4108 the option portion of the message. */
4109 va_start (list
, xid
);
4110 while ((option
= va_arg (list
, failover_option_t
*))) {
4111 if (option
!= &skip_failover_option
)
4112 size
+= option
-> count
;
4113 if (option
== &null_failover_option
)
4118 /* Allocate an option buffer, unless we got an error. */
4119 if (!bad_option
&& size
) {
4120 opbuf
= dmalloc (size
, MDL
);
4122 status
= ISC_R_NOMEMORY
;
4124 opbuf
= (unsigned char *)0;
4126 va_start (list
, xid
);
4127 while ((option
= va_arg (list
, failover_option_t
*))) {
4128 if (option
== &skip_failover_option
)
4130 if (!bad_option
&& opbuf
)
4131 memcpy (&opbuf
[opix
],
4132 option
-> data
, option
-> count
);
4133 if (option
!= &null_failover_option
&&
4134 option
!= &skip_failover_option
) {
4135 opix
+= option
-> count
;
4136 dfree (option
-> data
, MDL
);
4137 dfree (option
, MDL
);
4143 return ISC_R_INVALIDARG
;
4145 /* Now send the message header. */
4147 /* Message length. */
4148 status
= omapi_connection_put_uint16 (connection
, size
+ 12);
4149 if (status
!= ISC_R_SUCCESS
)
4154 status
= omapi_connection_copyin (connection
, &cbuf
, 1);
4155 if (status
!= ISC_R_SUCCESS
)
4158 /* Payload offset. */
4160 status
= omapi_connection_copyin (connection
, &cbuf
, 1);
4161 if (status
!= ISC_R_SUCCESS
)
4165 status
= omapi_connection_put_uint32 (connection
, (u_int32_t
)cur_time
);
4166 if (status
!= ISC_R_SUCCESS
)
4169 /* Transaction ID. */
4170 status
= omapi_connection_put_uint32(connection
, xid
);
4171 if (status
!= ISC_R_SUCCESS
)
4176 status
= omapi_connection_copyin (connection
, opbuf
, size
);
4177 if (status
!= ISC_R_SUCCESS
)
4181 if (link
-> state_object
&&
4182 link
-> state_object
-> link_to_peer
== link
) {
4183 #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
4184 log_info ("add_timeout +%d %s",
4185 (int)(link
-> state_object
->
4186 partner
.max_response_delay
) / 3,
4187 "dhcp_failover_send_contact");
4189 tv
. tv_sec
= cur_time
+
4190 (int)(link
-> state_object
->
4191 partner
.max_response_delay
) / 3;
4194 dhcp_failover_send_contact
, link
-> state_object
,
4195 (tvref_t
)dhcp_failover_state_reference
,
4196 (tvunref_t
)dhcp_failover_state_dereference
);
4203 log_info ("dhcp_failover_put_message: something went wrong.");
4204 omapi_disconnect (connection
, 1);
4208 void dhcp_failover_timeout (void *vstate
)
4210 dhcp_failover_state_t
*state
= vstate
;
4211 dhcp_failover_link_t
*link
;
4213 #if defined (DEBUG_FAILOVER_TIMING)
4214 log_info ("dhcp_failover_timeout");
4217 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
4219 link
= state
-> link_to_peer
;
4222 link
-> outer
-> type
!= omapi_type_connection
)
4225 log_error ("timeout waiting for failover peer %s", state
-> name
);
4227 /* If we haven't gotten a timely response, blow away the connection.
4228 This will cause the state to change automatically. */
4229 omapi_disconnect (link
-> outer
, 1);
4232 void dhcp_failover_send_contact (void *vstate
)
4234 dhcp_failover_state_t
*state
= vstate
;
4235 dhcp_failover_link_t
*link
;
4236 isc_result_t status
;
4238 #if defined(DEBUG_FAILOVER_MESSAGES) && \
4239 defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
4241 unsigned obufix
= 0;
4243 failover_print(obuf
, &obufix
, sizeof(obuf
), "(contact");
4246 #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
4247 log_info ("dhcp_failover_send_contact");
4250 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
4252 link
= state
-> link_to_peer
;
4255 link
-> outer
-> type
!= omapi_type_connection
)
4258 status
= (dhcp_failover_put_message
4259 (link
, link
-> outer
,
4260 FTM_CONTACT
, link
->xid
++,
4261 (failover_option_t
*)0));
4263 #if defined(DEBUG_FAILOVER_MESSAGES) && \
4264 defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
4265 if (status
!= ISC_R_SUCCESS
)
4266 failover_print(obuf
, &obufix
, sizeof(obuf
), " (failed)");
4267 failover_print(obuf
, &obufix
, sizeof(obuf
), ")");
4269 log_debug ("%s", obuf
);
4275 isc_result_t
dhcp_failover_send_state (dhcp_failover_state_t
*state
)
4277 dhcp_failover_link_t
*link
;
4278 isc_result_t status
;
4280 #if defined (DEBUG_FAILOVER_MESSAGES)
4282 unsigned obufix
= 0;
4284 # define FMA obuf, &obufix, sizeof obuf
4285 failover_print (FMA
, "(state");
4287 # define FMA (char *)0, (unsigned *)0, 0
4290 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
4291 return ISC_R_INVALIDARG
;
4292 link
= state
-> link_to_peer
;
4295 link
-> outer
-> type
!= omapi_type_connection
)
4296 return ISC_R_INVALIDARG
;
4298 status
= (dhcp_failover_put_message
4299 (link
, link
-> outer
,
4300 FTM_STATE
, link
->xid
++,
4301 dhcp_failover_make_option (FTO_SERVER_STATE
, FMA
,
4302 (state
-> me
.state
== startup
4303 ? state
-> saved_state
4304 : state
-> me
.state
)),
4305 dhcp_failover_make_option
4306 (FTO_SERVER_FLAGS
, FMA
,
4307 (state
-> service_state
== service_startup
4308 ? FTF_SERVER_STARTUP
: 0)),
4309 dhcp_failover_make_option (FTO_STOS
, FMA
, state
-> me
.stos
),
4310 (failover_option_t
*)0));
4312 #if defined (DEBUG_FAILOVER_MESSAGES)
4313 if (status
!= ISC_R_SUCCESS
)
4314 failover_print (FMA
, " (failed)");
4315 failover_print (FMA
, ")");
4317 log_debug ("%s", obuf
);
4320 return ISC_R_SUCCESS
;
4323 /* Send a connect message. */
4325 isc_result_t
dhcp_failover_send_connect (omapi_object_t
*l
)
4327 dhcp_failover_link_t
*link
;
4328 dhcp_failover_state_t
*state
;
4329 isc_result_t status
;
4330 #if defined (DEBUG_FAILOVER_MESSAGES)
4332 unsigned obufix
= 0;
4334 # define FMA obuf, &obufix, sizeof obuf
4335 failover_print (FMA
, "(connect");
4337 # define FMA (char *)0, (unsigned *)0, 0
4340 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
4341 return ISC_R_INVALIDARG
;
4342 link
= (dhcp_failover_link_t
*)l
;
4343 state
= link
-> state_object
;
4344 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
4345 return ISC_R_INVALIDARG
;
4348 (dhcp_failover_put_message
4350 FTM_CONNECT
, link
->xid
++,
4351 dhcp_failover_make_option(FTO_RELATIONSHIP_NAME
, FMA
,
4352 strlen(state
->name
), state
->name
),
4353 dhcp_failover_make_option (FTO_MAX_UNACKED
, FMA
,
4354 state
-> me
.max_flying_updates
),
4355 dhcp_failover_make_option (FTO_RECEIVE_TIMER
, FMA
,
4356 state
-> me
.max_response_delay
),
4357 dhcp_failover_option_printf(FTO_VENDOR_CLASS
, FMA
,
4358 "isc-%s", PACKAGE_VERSION
),
4359 dhcp_failover_make_option (FTO_PROTOCOL_VERSION
, FMA
,
4360 DHCP_FAILOVER_VERSION
),
4361 dhcp_failover_make_option (FTO_TLS_REQUEST
, FMA
,
4363 dhcp_failover_make_option (FTO_MCLT
, FMA
,
4366 ? dhcp_failover_make_option (FTO_HBA
, FMA
, 32, state
-> hba
)
4367 : &skip_failover_option
),
4368 (failover_option_t
*)0));
4370 #if defined (DEBUG_FAILOVER_MESSAGES)
4371 if (status
!= ISC_R_SUCCESS
)
4372 failover_print (FMA
, " (failed)");
4373 failover_print (FMA
, ")");
4375 log_debug ("%s", obuf
);
4381 isc_result_t
dhcp_failover_send_connectack (omapi_object_t
*l
,
4382 dhcp_failover_state_t
*state
,
4383 int reason
, const char *errmsg
)
4385 dhcp_failover_link_t
*link
;
4386 isc_result_t status
;
4387 #if defined (DEBUG_FAILOVER_MESSAGES)
4389 unsigned obufix
= 0;
4391 # define FMA obuf, &obufix, sizeof obuf
4392 failover_print (FMA
, "(connectack");
4394 # define FMA (char *)0, (unsigned *)0, 0
4397 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
4398 return ISC_R_INVALIDARG
;
4399 link
= (dhcp_failover_link_t
*)l
;
4400 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
4401 return ISC_R_INVALIDARG
;
4404 (dhcp_failover_put_message
4406 FTM_CONNECTACK
, link
->imsg
->xid
,
4408 ? dhcp_failover_make_option(FTO_RELATIONSHIP_NAME
, FMA
,
4409 strlen(state
->name
), state
->name
)
4410 : (link
->imsg
->options_present
& FTB_RELATIONSHIP_NAME
)
4411 ? &link
->imsg
->relationship_name
4412 : &skip_failover_option
,
4414 ? dhcp_failover_make_option (FTO_MAX_UNACKED
, FMA
,
4415 state
-> me
.max_flying_updates
)
4416 : &skip_failover_option
,
4418 ? dhcp_failover_make_option (FTO_RECEIVE_TIMER
, FMA
,
4419 state
-> me
.max_response_delay
)
4420 : &skip_failover_option
,
4421 dhcp_failover_option_printf(FTO_VENDOR_CLASS
, FMA
,
4422 "isc-%s", PACKAGE_VERSION
),
4423 dhcp_failover_make_option (FTO_PROTOCOL_VERSION
, FMA
,
4424 DHCP_FAILOVER_VERSION
),
4425 (link
->imsg
->options_present
& FTB_TLS_REQUEST
)
4426 ? dhcp_failover_make_option(FTO_TLS_REPLY
, FMA
,
4428 : &skip_failover_option
,
4430 ? dhcp_failover_make_option (FTO_REJECT_REASON
,
4432 : &skip_failover_option
,
4434 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4435 strlen (errmsg
), errmsg
)
4436 : &skip_failover_option
,
4437 (failover_option_t
*)0));
4439 #if defined (DEBUG_FAILOVER_MESSAGES)
4440 if (status
!= ISC_R_SUCCESS
)
4441 failover_print (FMA
, " (failed)");
4442 failover_print (FMA
, ")");
4444 log_debug ("%s", obuf
);
4450 isc_result_t
dhcp_failover_send_disconnect (omapi_object_t
*l
,
4452 const char *message
)
4454 dhcp_failover_link_t
*link
;
4455 dhcp_failover_state_t
*state
;
4456 isc_result_t status
;
4457 #if defined (DEBUG_FAILOVER_MESSAGES)
4459 unsigned obufix
= 0;
4461 # define FMA obuf, &obufix, sizeof obuf
4462 failover_print (FMA
, "(disconnect");
4464 # define FMA (char *)0, (unsigned *)0, 0
4467 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
4468 return ISC_R_INVALIDARG
;
4469 link
= (dhcp_failover_link_t
*)l
;
4470 state
= link
-> state_object
;
4471 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
4472 return ISC_R_INVALIDARG
;
4474 if (!message
&& reason
)
4475 message
= dhcp_failover_reject_reason_print (reason
);
4477 status
= (dhcp_failover_put_message
4479 FTM_DISCONNECT
, link
->xid
++,
4480 dhcp_failover_make_option (FTO_REJECT_REASON
,
4483 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4484 strlen (message
), message
)
4485 : &skip_failover_option
),
4486 (failover_option_t
*)0));
4488 #if defined (DEBUG_FAILOVER_MESSAGES)
4489 if (status
!= ISC_R_SUCCESS
)
4490 failover_print (FMA
, " (failed)");
4491 failover_print (FMA
, ")");
4493 log_debug ("%s", obuf
);
4499 /* Send a Bind Update message. */
4501 isc_result_t
dhcp_failover_send_bind_update (dhcp_failover_state_t
*state
,
4502 struct lease
*lease
)
4504 dhcp_failover_link_t
*link
;
4505 isc_result_t status
;
4507 binding_state_t transmit_state
;
4508 #if defined (DEBUG_FAILOVER_MESSAGES)
4510 unsigned obufix
= 0;
4512 # define FMA obuf, &obufix, sizeof obuf
4513 failover_print (FMA
, "(bndupd");
4515 # define FMA (char *)0, (unsigned *)0, 0
4518 if (!state
-> link_to_peer
||
4519 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4520 return ISC_R_INVALIDARG
;
4521 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4523 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4524 return ISC_R_INVALIDARG
;
4526 transmit_state
= lease
->desired_binding_state
;
4527 if (lease
->flags
& RESERVED_LEASE
) {
4528 /* If we are listing an allocable (not yet ACTIVE etc) lease
4529 * as reserved, toggle to the peer's 'free state', per the
4530 * draft. This gives the peer permission to alloc it to the
4531 * chaddr/uid-named client.
4533 if ((state
->i_am
== primary
) && (transmit_state
== FTS_FREE
))
4534 transmit_state
= FTS_BACKUP
;
4535 else if ((state
->i_am
== secondary
) &&
4536 (transmit_state
== FTS_BACKUP
))
4537 transmit_state
= FTS_FREE
;
4539 flags
|= FTF_IP_FLAG_RESERVE
;
4541 if (lease
->flags
& BOOTP_LEASE
)
4542 flags
|= FTF_IP_FLAG_BOOTP
;
4544 /* last_xid == 0 is illegal, seek past zero if we hit it. */
4548 lease
->last_xid
= link
->xid
++;
4550 /* Send the update. */
4551 status
= (dhcp_failover_put_message
4552 (link
, link
-> outer
,
4553 FTM_BNDUPD
, lease
->last_xid
,
4554 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS
, FMA
,
4555 lease
-> ip_addr
.len
,
4556 lease
-> ip_addr
.iabuf
),
4557 dhcp_failover_make_option (FTO_BINDING_STATUS
, FMA
,
4558 lease
-> desired_binding_state
),
4560 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER
, FMA
,
4563 : &skip_failover_option
,
4564 lease
-> hardware_addr
.hlen
4565 ? dhcp_failover_make_option (FTO_CHADDR
, FMA
,
4566 lease
-> hardware_addr
.hlen
,
4567 lease
-> hardware_addr
.hbuf
)
4568 : &skip_failover_option
,
4569 dhcp_failover_make_option (FTO_LEASE_EXPIRY
, FMA
,
4571 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY
, FMA
,
4573 dhcp_failover_make_option (FTO_STOS
, FMA
,
4575 (lease
->cltt
!= 0) ?
4576 dhcp_failover_make_option(FTO_CLTT
, FMA
, lease
->cltt
) :
4577 &skip_failover_option
, /* No CLTT */
4578 flags
? dhcp_failover_make_option(FTO_IP_FLAGS
, FMA
,
4580 &skip_failover_option
, /* No IP_FLAGS */
4581 &skip_failover_option
, /* XXX DDNS */
4582 &skip_failover_option
, /* XXX request options */
4583 &skip_failover_option
, /* XXX reply options */
4584 (failover_option_t
*)0));
4586 #if defined (DEBUG_FAILOVER_MESSAGES)
4587 if (status
!= ISC_R_SUCCESS
)
4588 failover_print (FMA
, " (failed)");
4589 failover_print (FMA
, ")");
4591 log_debug ("%s", obuf
);
4597 /* Send a Bind ACK message. */
4599 isc_result_t
dhcp_failover_send_bind_ack (dhcp_failover_state_t
*state
,
4600 failover_message_t
*msg
,
4601 int reason
, const char *message
)
4603 dhcp_failover_link_t
*link
;
4604 isc_result_t status
;
4605 #if defined (DEBUG_FAILOVER_MESSAGES)
4607 unsigned obufix
= 0;
4609 # define FMA obuf, &obufix, sizeof obuf
4610 failover_print (FMA
, "(bndack");
4612 # define FMA (char *)0, (unsigned *)0, 0
4615 if (!state
-> link_to_peer
||
4616 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4617 return ISC_R_INVALIDARG
;
4618 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4620 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4621 return ISC_R_INVALIDARG
;
4623 if (!message
&& reason
)
4624 message
= dhcp_failover_reject_reason_print (reason
);
4626 /* Send the update. */
4627 status
= (dhcp_failover_put_message
4628 (link
, link
-> outer
,
4629 FTM_BNDACK
, msg
->xid
,
4630 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS
, FMA
,
4631 sizeof msg
-> assigned_addr
,
4632 &msg
-> assigned_addr
),
4633 #ifdef DO_BNDACK_SHOULD_NOT
4634 dhcp_failover_make_option (FTO_BINDING_STATUS
, FMA
,
4635 msg
-> binding_status
),
4636 (msg
-> options_present
& FTB_CLIENT_IDENTIFIER
)
4637 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER
, FMA
,
4638 msg
-> client_identifier
.count
,
4639 msg
-> client_identifier
.data
)
4640 : &skip_failover_option
,
4641 (msg
-> options_present
& FTB_CHADDR
)
4642 ? dhcp_failover_make_option (FTO_CHADDR
, FMA
,
4643 msg
-> chaddr
.count
,
4645 : &skip_failover_option
,
4646 dhcp_failover_make_option (FTO_LEASE_EXPIRY
, FMA
,
4648 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY
, FMA
,
4649 msg
-> potential_expiry
),
4650 dhcp_failover_make_option (FTO_STOS
, FMA
,
4652 (msg
->options_present
& FTB_CLTT
) ?
4653 dhcp_failover_make_option(FTO_CLTT
, FMA
, msg
->cltt
) :
4654 &skip_failover_option
, /* No CLTT in the msg to ack. */
4655 ((msg
->options_present
& FTB_IP_FLAGS
) && msg
->ip_flags
) ?
4656 dhcp_failover_make_option(FTO_IP_FLAGS
, FMA
,
4658 : &skip_failover_option
,
4659 #endif /* DO_BNDACK_SHOULD_NOT */
4661 ? dhcp_failover_make_option(FTO_REJECT_REASON
, FMA
, reason
)
4662 : &skip_failover_option
,
4664 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4665 strlen (message
), message
)
4666 : &skip_failover_option
,
4667 #ifdef DO_BNDACK_SHOULD_NOT
4668 &skip_failover_option
, /* XXX DDNS */
4669 &skip_failover_option
, /* XXX request options */
4670 &skip_failover_option
, /* XXX reply options */
4671 #endif /* DO_BNDACK_SHOULD_NOT */
4672 (failover_option_t
*)0));
4674 #if defined (DEBUG_FAILOVER_MESSAGES)
4675 if (status
!= ISC_R_SUCCESS
)
4676 failover_print (FMA
, " (failed)");
4677 failover_print (FMA
, ")");
4679 log_debug ("%s", obuf
);
4685 isc_result_t
dhcp_failover_send_poolreq (dhcp_failover_state_t
*state
)
4687 dhcp_failover_link_t
*link
;
4688 isc_result_t status
;
4689 #if defined (DEBUG_FAILOVER_MESSAGES)
4691 unsigned obufix
= 0;
4693 # define FMA obuf, &obufix, sizeof obuf
4694 failover_print (FMA
, "(poolreq");
4696 # define FMA (char *)0, (unsigned *)0, 0
4699 if (!state
-> link_to_peer
||
4700 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4701 return ISC_R_INVALIDARG
;
4702 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4704 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4705 return ISC_R_INVALIDARG
;
4707 status
= (dhcp_failover_put_message
4708 (link
, link
-> outer
,
4709 FTM_POOLREQ
, link
->xid
++,
4710 (failover_option_t
*)0));
4712 #if defined (DEBUG_FAILOVER_MESSAGES)
4713 if (status
!= ISC_R_SUCCESS
)
4714 failover_print (FMA
, " (failed)");
4715 failover_print (FMA
, ")");
4717 log_debug ("%s", obuf
);
4723 isc_result_t
dhcp_failover_send_poolresp (dhcp_failover_state_t
*state
,
4726 dhcp_failover_link_t
*link
;
4727 isc_result_t status
;
4728 #if defined (DEBUG_FAILOVER_MESSAGES)
4730 unsigned obufix
= 0;
4732 # define FMA obuf, &obufix, sizeof obuf
4733 failover_print (FMA
, "(poolresp");
4735 # define FMA (char *)0, (unsigned *)0, 0
4738 if (!state
-> link_to_peer
||
4739 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4740 return ISC_R_INVALIDARG
;
4741 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4743 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4744 return ISC_R_INVALIDARG
;
4746 status
= (dhcp_failover_put_message
4747 (link
, link
-> outer
,
4748 FTM_POOLRESP
, link
->imsg
->xid
,
4749 dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED
, FMA
,
4751 (failover_option_t
*)0));
4753 #if defined (DEBUG_FAILOVER_MESSAGES)
4754 if (status
!= ISC_R_SUCCESS
)
4755 failover_print (FMA
, " (failed)");
4756 failover_print (FMA
, ")");
4758 log_debug ("%s", obuf
);
4764 isc_result_t
dhcp_failover_send_update_request (dhcp_failover_state_t
*state
)
4766 dhcp_failover_link_t
*link
;
4767 isc_result_t status
;
4768 #if defined (DEBUG_FAILOVER_MESSAGES)
4770 unsigned obufix
= 0;
4772 # define FMA obuf, &obufix, sizeof obuf
4773 failover_print (FMA
, "(updreq");
4775 # define FMA (char *)0, (unsigned *)0, 0
4778 if (!state
-> link_to_peer
||
4779 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4780 return ISC_R_INVALIDARG
;
4781 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4783 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4784 return ISC_R_INVALIDARG
;
4786 if (state
-> curUPD
)
4787 return ISC_R_ALREADYRUNNING
;
4789 status
= (dhcp_failover_put_message
4790 (link
, link
-> outer
,
4791 FTM_UPDREQ
, link
->xid
++,
4792 (failover_option_t
*)0));
4794 if (status
== ISC_R_SUCCESS
)
4795 state
-> curUPD
= FTM_UPDREQ
;
4797 #if defined (DEBUG_FAILOVER_MESSAGES)
4798 if (status
!= ISC_R_SUCCESS
)
4799 failover_print (FMA
, " (failed)");
4800 failover_print (FMA
, ")");
4802 log_debug ("%s", obuf
);
4805 log_info ("Sent update request message to %s", state
-> name
);
4809 isc_result_t
dhcp_failover_send_update_request_all (dhcp_failover_state_t
4812 dhcp_failover_link_t
*link
;
4813 isc_result_t status
;
4814 #if defined (DEBUG_FAILOVER_MESSAGES)
4816 unsigned obufix
= 0;
4818 # define FMA obuf, &obufix, sizeof obuf
4819 failover_print (FMA
, "(updreqall");
4821 # define FMA (char *)0, (unsigned *)0, 0
4824 if (!state
-> link_to_peer
||
4825 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4826 return ISC_R_INVALIDARG
;
4827 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4829 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4830 return ISC_R_INVALIDARG
;
4832 /* If there is an UPDREQ in progress, then upgrade to UPDREQALL. */
4833 if (state
-> curUPD
&& (state
-> curUPD
!= FTM_UPDREQ
))
4834 return ISC_R_ALREADYRUNNING
;
4836 status
= (dhcp_failover_put_message
4837 (link
, link
-> outer
,
4838 FTM_UPDREQALL
, link
->xid
++,
4839 (failover_option_t
*)0));
4841 if (status
== ISC_R_SUCCESS
)
4842 state
-> curUPD
= FTM_UPDREQALL
;
4844 #if defined (DEBUG_FAILOVER_MESSAGES)
4845 if (status
!= ISC_R_SUCCESS
)
4846 failover_print (FMA
, " (failed)");
4847 failover_print (FMA
, ")");
4849 log_debug ("%s", obuf
);
4852 log_info ("Sent update request all message to %s", state
-> name
);
4856 isc_result_t
dhcp_failover_send_update_done (dhcp_failover_state_t
*state
)
4858 dhcp_failover_link_t
*link
;
4859 isc_result_t status
;
4860 #if defined (DEBUG_FAILOVER_MESSAGES)
4862 unsigned obufix
= 0;
4864 # define FMA obuf, &obufix, sizeof obuf
4865 failover_print (FMA
, "(upddone");
4867 # define FMA (char *)0, (unsigned *)0, 0
4870 if (!state
-> link_to_peer
||
4871 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4872 return ISC_R_INVALIDARG
;
4873 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4875 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4876 return ISC_R_INVALIDARG
;
4878 status
= (dhcp_failover_put_message
4879 (link
, link
-> outer
,
4880 FTM_UPDDONE
, state
->updxid
,
4881 (failover_option_t
*)0));
4883 #if defined (DEBUG_FAILOVER_MESSAGES)
4884 if (status
!= ISC_R_SUCCESS
)
4885 failover_print (FMA
, " (failed)");
4886 failover_print (FMA
, ")");
4888 log_debug ("%s", obuf
);
4892 log_info ("Sent update done message to %s", state
-> name
);
4894 state
->updxid
--; /* Paranoia, just so it mismatches. */
4896 /* There may be uncommitted leases at this point (since
4897 dhcp_failover_process_bind_ack() doesn't commit leases);
4898 commit the lease file. */
4905 * failover_lease_is_better() compares the binding update in 'msg' with
4906 * the current lease in 'lease'. If the determination is that the binding
4907 * update shouldn't be allowed to update/crush more critical binding info
4908 * on the lease, the lease is preferred. A value of true is returned if the
4909 * local lease is preferred, or false if the remote binding update is
4912 * For now this function is hopefully simplistic and trivial. It may be that
4913 * a more detailed system of preferences is required, so this is something we
4914 * should monitor as we gain experience with these dueling events.
4916 static isc_boolean_t
4917 failover_lease_is_better(dhcp_failover_state_t
*state
, struct lease
*lease
,
4918 failover_message_t
*msg
)
4920 binding_state_t local_state
;
4923 if (lease
->binding_state
!= lease
->desired_binding_state
)
4924 local_state
= lease
->desired_binding_state
;
4926 local_state
= lease
->binding_state
;
4928 if ((msg
->options_present
& FTB_CLTT
) != 0)
4929 msg_cltt
= msg
->cltt
;
4933 switch(local_state
) {
4935 if (msg
->binding_status
== FTS_ACTIVE
) {
4936 if (msg_cltt
< lease
->cltt
)
4938 else if (msg_cltt
> lease
->cltt
)
4940 else if (state
->i_am
== primary
)
4944 } else if (msg
->binding_status
== FTS_EXPIRED
) {
4955 if (msg
->binding_status
== FTS_ACTIVE
)
4957 else if (state
->i_am
== primary
)
4961 /* FALL THROUGH to impossible condition */
4964 log_fatal("Impossible condition at %s:%d.", MDL
);
4967 log_fatal("Impossible condition at %s:%d.", MDL
);
4968 /* Silence compiler warning. */
4972 isc_result_t
dhcp_failover_process_bind_update (dhcp_failover_state_t
*state
,
4973 failover_message_t
*msg
)
4975 struct lease
*lt
, *lease
;
4977 int reason
= FTR_MISC_REJECT
;
4978 const char *message
;
4979 int new_binding_state
;
4980 int send_to_backup
= 0;
4981 int required_options
;
4983 /* Validate the binding update. */
4984 required_options
= FTB_ASSIGNED_IP_ADDRESS
| FTB_BINDING_STATUS
;
4985 if ((msg
->options_present
& required_options
) != required_options
) {
4986 message
= "binding update lacks required options";
4987 reason
= FTR_MISSING_BINDINFO
;
4991 ia
.len
= sizeof msg
-> assigned_addr
;
4992 memcpy (ia
.iabuf
, &msg
-> assigned_addr
, ia
.len
);
4994 lease
= (struct lease
*)0;
4995 lt
= (struct lease
*)0;
4996 if (!find_lease_by_ip_addr (&lease
, ia
, MDL
)) {
4997 message
= "unknown IP address";
4998 reason
= FTR_ILLEGAL_IP_ADDR
;
5003 * If this lease is covered by a different failover peering
5004 * relationship, assert an error.
5006 if ((lease
->pool
== NULL
) || (lease
->pool
->failover_peer
== NULL
) ||
5007 (lease
->pool
->failover_peer
!= state
)) {
5008 message
= "IP address is covered by a different failover "
5009 "relationship state";
5010 reason
= FTR_ILLEGAL_IP_ADDR
;
5015 * Dueling updates: This happens when both servers send a BNDUPD
5016 * at the same time. We want the best update to win, which means
5017 * we reject if we think ours is better, or cancel if we think the
5018 * peer's is better. We only assert a problem if the lease is on
5019 * the ACK queue, not on the UPDATE queue. This means that after
5020 * accepting this server's BNDUPD, we will send our own BNDUPD
5021 * /after/ sending the BNDACK (this order was recently enforced in
5022 * queue processing).
5024 if ((lease
->flags
& ON_ACK_QUEUE
) != 0) {
5025 if (failover_lease_is_better(state
, lease
, msg
)) {
5026 message
= "incoming update is less critical than "
5028 reason
= FTR_LESS_CRIT_BIND_INFO
;
5031 /* This makes it so we ignore any spurious ACKs. */
5032 dhcp_failover_ack_queue_remove(state
, lease
);
5036 /* Install the new info. Start by taking a copy to markup. */
5037 if (!lease_copy (<
, lease
, MDL
)) {
5038 message
= "no memory";
5042 if (msg
-> options_present
& FTB_CHADDR
) {
5043 if (msg
->binding_status
== FTS_ABANDONED
) {
5044 message
= "BNDUPD to ABANDONED with a CHADDR";
5047 if (msg
-> chaddr
.count
> sizeof lt
-> hardware_addr
.hbuf
) {
5048 message
= "chaddr too long";
5051 lt
-> hardware_addr
.hlen
= msg
-> chaddr
.count
;
5052 memcpy (lt
-> hardware_addr
.hbuf
, msg
-> chaddr
.data
,
5053 msg
-> chaddr
.count
);
5054 } else if (msg
->binding_status
== FTS_ACTIVE
||
5055 msg
->binding_status
== FTS_EXPIRED
||
5056 msg
->binding_status
== FTS_RELEASED
) {
5057 message
= "BNDUPD without CHADDR";
5058 reason
= FTR_MISSING_BINDINFO
;
5060 } else if (msg
->binding_status
== FTS_ABANDONED
) {
5061 lt
->hardware_addr
.hlen
= 0;
5063 binding_scope_dereference(<
->scope
, MDL
);
5066 /* There is no explicit message content to indicate that the client
5067 * supplied no client-identifier. So if we don't hear of a value,
5068 * we discard the last one.
5070 if (msg
->options_present
& FTB_CLIENT_IDENTIFIER
) {
5071 if (msg
->binding_status
== FTS_ABANDONED
) {
5072 message
= "BNDUPD to ABANDONED with client-id";
5076 lt
->uid_len
= msg
->client_identifier
.count
;
5078 /* Allocate the lt->uid buffer if we haven't already, or
5079 * re-allocate the lt-uid buffer if we have one that is not
5080 * large enough. Otherwise, just use the extant buffer.
5082 if (!lt
->uid
|| lt
->uid
== lt
->uid_buf
||
5083 lt
->uid_len
> lt
->uid_max
) {
5084 if (lt
->uid
&& lt
->uid
!= lt
->uid_buf
)
5085 dfree(lt
->uid
, MDL
);
5087 if (lt
->uid_len
> sizeof(lt
->uid_buf
)) {
5088 lt
->uid_max
= lt
->uid_len
;
5089 lt
->uid
= dmalloc(lt
->uid_len
, MDL
);
5091 message
= "no memory";
5095 lt
->uid_max
= sizeof(lt
->uid_buf
);
5096 lt
->uid
= lt
->uid_buf
;
5100 msg
-> client_identifier
.data
, lt
-> uid_len
);
5101 } else if (lt
->uid
&& msg
->binding_status
!= FTS_RESET
&&
5102 msg
->binding_status
!= FTS_FREE
&&
5103 msg
->binding_status
!= FTS_BACKUP
) {
5104 if (lt
->uid
!= lt
->uid_buf
)
5105 dfree (lt
->uid
, MDL
);
5107 lt
->uid_max
= lt
->uid_len
= 0;
5110 /* If the lease was expired, also remove the stale binding scope. */
5111 if (lt
->scope
&& lt
->ends
< cur_time
)
5112 binding_scope_dereference(<
->scope
, MDL
);
5114 /* XXX Times may need to be adjusted based on clock skew! */
5115 if (msg
-> options_present
& FTB_STOS
) {
5116 lt
-> starts
= msg
-> stos
;
5118 if (msg
-> options_present
& FTB_LEASE_EXPIRY
) {
5119 lt
-> ends
= msg
-> expiry
;
5121 if (msg
->options_present
& FTB_POTENTIAL_EXPIRY
) {
5122 lt
->atsfp
= lt
->tsfp
= msg
->potential_expiry
;
5124 if (msg
->options_present
& FTB_IP_FLAGS
) {
5125 if (msg
->ip_flags
& FTF_IP_FLAG_RESERVE
) {
5126 if ((((state
->i_am
== primary
) &&
5127 (lease
->binding_state
== FTS_FREE
)) ||
5128 ((state
->i_am
== secondary
) &&
5129 (lease
->binding_state
== FTS_BACKUP
))) &&
5130 !(lease
->flags
& RESERVED_LEASE
)) {
5131 message
= "Address is not reserved.";
5132 reason
= FTR_IP_NOT_RESERVED
;
5136 lt
->flags
|= RESERVED_LEASE
;
5138 lt
->flags
&= ~RESERVED_LEASE
;
5140 if (msg
->ip_flags
& FTF_IP_FLAG_BOOTP
) {
5141 if ((((state
->i_am
== primary
) &&
5142 (lease
->binding_state
== FTS_FREE
)) ||
5143 ((state
->i_am
== secondary
) &&
5144 (lease
->binding_state
== FTS_BACKUP
))) &&
5145 !(lease
->flags
& BOOTP_LEASE
)) {
5146 message
= "Address is not allocated to BOOTP.";
5149 lt
->flags
|= BOOTP_LEASE
;
5151 lt
->flags
&= ~BOOTP_LEASE
;
5153 if (msg
->ip_flags
& ~(FTF_IP_FLAG_RESERVE
| FTF_IP_FLAG_BOOTP
))
5154 log_info("Unknown IP-flags set in BNDUPD (0x%x).",
5156 } else /* Flags may only not appear if the values are zero. */
5157 lt
->flags
&= ~(RESERVED_LEASE
| BOOTP_LEASE
);
5159 #if defined (DEBUG_LEASE_STATE_TRANSITIONS)
5160 log_info ("processing state transition for %s: %s to %s",
5161 piaddr (lease
-> ip_addr
),
5162 binding_state_print (lease
-> binding_state
),
5163 binding_state_print (msg
-> binding_status
));
5166 /* If we're in normal state, make sure the state transition
5168 if (state
-> me
.state
== normal
) {
5170 (normal_binding_state_transition_check
5171 (lease
, state
, msg
-> binding_status
,
5172 msg
-> potential_expiry
));
5173 /* XXX if the transition the peer asked for isn't
5174 XXX allowed, maybe we should make the transition
5175 XXX into potential-conflict at this point. */
5178 (conflict_binding_state_transition_check
5179 (lease
, state
, msg
-> binding_status
,
5180 msg
-> potential_expiry
));
5182 if (new_binding_state
!= msg
-> binding_status
) {
5185 if (snprintf (outbuf
, sizeof outbuf
,
5186 "%s: invalid state transition: %s to %s",
5187 piaddr (lease
-> ip_addr
),
5188 binding_state_print (lease
-> binding_state
),
5189 binding_state_print (msg
-> binding_status
))
5191 log_fatal ("%s: impossible outbuf overflow",
5192 "dhcp_failover_process_bind_update");
5194 dhcp_failover_send_bind_ack (state
, msg
,
5199 if (new_binding_state
== FTS_EXPIRED
||
5200 new_binding_state
== FTS_RELEASED
||
5201 new_binding_state
== FTS_RESET
) {
5202 lt
-> next_binding_state
= FTS_FREE
;
5204 /* Mac address affinity. Assign the lease to
5205 * BACKUP state if we are the primary and the
5206 * peer is more likely to reallocate this lease
5207 * to a returning client.
5209 if ((state
->i_am
== primary
) &&
5210 !(lt
->flags
& (RESERVED_LEASE
| BOOTP_LEASE
)))
5211 send_to_backup
= peer_wants_lease(lt
);
5213 lt
-> next_binding_state
= new_binding_state
;
5215 msg
-> binding_status
= lt
-> next_binding_state
;
5217 /* Try to install the new information. */
5218 if (!supersede_lease (lease
, lt
, 0, 0, 0) ||
5219 !write_lease (lease
)) {
5220 message
= "database update failed";
5222 dhcp_failover_send_bind_ack (state
, msg
, reason
, message
);
5225 dhcp_failover_queue_ack (state
, msg
);
5228 /* If it is probably wise, assign lease to backup state if the peer
5229 * is not already hoarding leases.
5231 if (send_to_backup
&& secondary_not_hoarding(state
, lease
->pool
)) {
5232 lease
->next_binding_state
= FTS_BACKUP
;
5233 lease
->tstp
= cur_time
;
5234 lease
->starts
= cur_time
;
5236 if (!supersede_lease(lease
, NULL
, 0, 1, 0) ||
5237 !write_lease(lease
))
5238 log_error("can't commit lease %s for mac addr "
5239 "affinity", piaddr(lease
->ip_addr
));
5241 dhcp_failover_send_updates(state
);
5246 lease_dereference (<
, MDL
);
5248 lease_dereference (&lease
, MDL
);
5250 return ISC_R_SUCCESS
;
5253 /* This was hairy enough I didn't want to do it all in an if statement.
5255 * Returns: Truth is the secondary is allowed to get more leases based upon
5256 * MAC address affinity. False otherwise.
5259 secondary_not_hoarding(dhcp_failover_state_t
*state
, struct pool
*p
) {
5264 total
= p
->free_leases
+ p
->backup_leases
;
5266 /* How many leases is one side or the other allowed to "hold"? */
5267 hold
= ((total
* state
->max_lease_ownership
) + 50) / 100;
5269 /* If we were to send leases (or if the secondary were to send us
5270 * leases in the negative direction), how many would that be?
5272 lts
= (p
->free_leases
- p
->backup_leases
) / 2;
5274 /* The peer is not hoarding leases if we would send them more leases
5275 * (or they would take fewer leases) than the maximum they are allowed
5276 * to hold (the negative hold).
5278 return(lts
> -hold
);
5281 isc_result_t
dhcp_failover_process_bind_ack (dhcp_failover_state_t
*state
,
5282 failover_message_t
*msg
)
5284 struct lease
*lt
= (struct lease
*)0;
5285 struct lease
*lease
= (struct lease
*)0;
5287 const char *message
= "no memory";
5288 u_int32_t pot_expire
;
5289 int send_to_backup
= ISC_FALSE
;
5292 ia
.len
= sizeof msg
-> assigned_addr
;
5293 memcpy (ia
.iabuf
, &msg
-> assigned_addr
, ia
.len
);
5295 if (!find_lease_by_ip_addr (&lease
, ia
, MDL
)) {
5296 message
= "no such lease";
5300 /* XXX check for conflicts. */
5301 if (msg
-> options_present
& FTB_REJECT_REASON
) {
5302 log_error ("bind update on %s from %s rejected: %.*s",
5303 piaddr (ia
), state
-> name
,
5304 (int)((msg
-> options_present
& FTB_MESSAGE
)
5305 ? msg
-> message
.count
5306 : strlen (dhcp_failover_reject_reason_print
5307 (msg
-> reject_reason
))),
5308 (msg
-> options_present
& FTB_MESSAGE
)
5309 ? (const char *)(msg
-> message
.data
)
5310 : (dhcp_failover_reject_reason_print
5311 (msg
-> reject_reason
)));
5315 /* Silently discard acks for leases we did not update (or multiple
5318 if (!lease
->last_xid
)
5321 if (lease
->last_xid
!= msg
->xid
) {
5322 message
= "xid mismatch";
5326 /* XXX Times may need to be adjusted based on clock skew! */
5327 if (msg
->options_present
& FTO_POTENTIAL_EXPIRY
)
5328 pot_expire
= msg
->potential_expiry
;
5330 pot_expire
= lease
->tstp
;
5332 /* If the lease was desired to enter a binding state, we set
5333 * such a value upon transmitting a bndupd. We do not clear it
5334 * if we receive a bndupd in the meantime (or change the state
5335 * of the lease again ourselves), but we do set binding_state
5336 * if we get a bndupd.
5338 * So desired_binding_state tells us what we sent a bndupd for,
5339 * and binding_state tells us what we have since determined in
5342 if (lease
->desired_binding_state
== FTS_EXPIRED
||
5343 lease
->desired_binding_state
== FTS_RESET
||
5344 lease
->desired_binding_state
== FTS_RELEASED
)
5346 /* It is not a problem to do this directly as we call
5347 * supersede_lease immediately after: the lease is requeued
5348 * even if its sort order (tsfp) has changed.
5350 lease
->atsfp
= lease
->tsfp
= pot_expire
;
5351 if ((state
->i_am
== secondary
) &&
5352 (lease
->flags
& RESERVED_LEASE
))
5353 lease
->next_binding_state
= FTS_BACKUP
;
5355 lease
->next_binding_state
= FTS_FREE
;
5356 /* Clear this condition for the next go-round. */
5357 lease
->desired_binding_state
= lease
->next_binding_state
;
5358 supersede_lease(lease
, (struct lease
*)0, 0, 0, 0);
5361 /* Lease has returned to FREE state from the
5362 * transitional states. If the lease 'belongs'
5363 * to a client that would be served by the
5364 * peer, process a binding update now to send
5365 * the lease to backup state. But not if we
5366 * think we already have.
5368 if (state
->i_am
== primary
&&
5369 !(lease
->flags
& (RESERVED_LEASE
| BOOTP_LEASE
)) &&
5370 peer_wants_lease(lease
))
5371 send_to_backup
= ISC_TRUE
;
5373 if (!send_to_backup
&& state
->me
.state
== normal
)
5376 /* XXX It could be a problem to do this directly if the lease
5377 * XXX is sorted by tsfp.
5379 lease
->atsfp
= lease
->tsfp
= pot_expire
;
5380 if (lease
->desired_binding_state
!= lease
->binding_state
) {
5381 lease
->next_binding_state
=
5382 lease
->desired_binding_state
;
5383 supersede_lease(lease
,
5384 (struct lease
*)0, 0, 0, 0);
5387 /* Commit the lease only after a two-second timeout,
5388 so that if we get a bunch of acks in quick
5389 succession (e.g., when stealing leases from the
5390 secondary), we do not do an immediate commit for
5392 tv
.tv_sec
= cur_time
+ 2;
5394 add_timeout(&tv
, commit_leases_timeout
, (void *)0, 0, 0);
5398 dhcp_failover_ack_queue_remove (state
, lease
);
5400 /* If we are supposed to send an update done after we send
5401 this lease, go ahead and send it. */
5402 if (state
-> send_update_done
== lease
) {
5403 lease_dereference (&state
-> send_update_done
, MDL
);
5404 dhcp_failover_send_update_done (state
);
5407 /* Now that the lease is off the ack queue, consider putting it
5408 * back on the update queue for mac address affinity.
5410 if (send_to_backup
&& secondary_not_hoarding(state
, lease
->pool
)) {
5411 lease
->next_binding_state
= FTS_BACKUP
;
5412 lease
->tstp
= lease
->starts
= cur_time
;
5414 if (!supersede_lease(lease
, NULL
, 0, 1, 0) ||
5415 !write_lease(lease
))
5416 log_error("can't commit lease %s for "
5417 "client affinity", piaddr(lease
->ip_addr
));
5419 if (state
->me
.state
== normal
)
5423 /* If there are updates pending, we've created space to send at
5425 dhcp_failover_send_updates (state
);
5428 lease_dereference (&lease
, MDL
);
5430 lease_dereference (<
, MDL
);
5432 return ISC_R_SUCCESS
;
5435 log_info ("bind update on %s got ack from %s: %s.",
5436 piaddr (ia
), state
-> name
, message
);
5440 isc_result_t
dhcp_failover_generate_update_queue (dhcp_failover_state_t
*state
,
5443 struct shared_network
*s
;
5447 #define FREE_LEASES 0
5448 #define ACTIVE_LEASES 1
5449 #define EXPIRED_LEASES 2
5450 #define ABANDONED_LEASES 3
5451 #define BACKUP_LEASES 4
5452 #define RESERVED_LEASES 5
5453 struct lease
**lptr
[RESERVED_LEASES
+1];
5455 /* Loop through each pool in each shared network and call the
5456 expiry routine on the pool. */
5457 for (s
= shared_networks
; s
; s
= s
-> next
) {
5458 for (p
= s
-> pools
; p
; p
= p
-> next
) {
5459 if (p
->failover_peer
!= state
)
5462 lptr
[FREE_LEASES
] = &p
->free
;
5463 lptr
[ACTIVE_LEASES
] = &p
->active
;
5464 lptr
[EXPIRED_LEASES
] = &p
->expired
;
5465 lptr
[ABANDONED_LEASES
] = &p
->abandoned
;
5466 lptr
[BACKUP_LEASES
] = &p
->backup
;
5467 lptr
[RESERVED_LEASES
] = &p
->reserved
;
5469 for (i
= FREE_LEASES
; i
<= RESERVED_LEASES
; i
++) {
5470 for (l
= *(lptr
[i
]); l
; l
= l
-> next
) {
5471 if ((l
->flags
& ON_QUEUE
) == 0 &&
5473 (l
->tstp
> l
->atsfp
) ||
5474 (i
== EXPIRED_LEASES
))) {
5475 l
-> desired_binding_state
= l
-> binding_state
;
5476 dhcp_failover_queue_update (l
, 0);
5482 return ISC_R_SUCCESS
;
5486 dhcp_failover_process_update_request (dhcp_failover_state_t
*state
,
5487 failover_message_t
*msg
)
5489 if (state
->send_update_done
) {
5490 log_info("Received update request while old update still "
5491 "flying! Silently discarding old request.");
5492 lease_dereference(&state
->send_update_done
, MDL
);
5495 /* Generate a fresh update queue. */
5496 dhcp_failover_generate_update_queue (state
, 0);
5498 state
->updxid
= msg
->xid
;
5500 /* If there's anything on the update queue (there shouldn't be
5501 anything on the ack queue), trigger an update done message
5502 when we get an ack for that lease. */
5503 if (state
-> update_queue_tail
) {
5504 lease_reference (&state
-> send_update_done
,
5505 state
-> update_queue_tail
, MDL
);
5506 dhcp_failover_send_updates (state
);
5507 log_info ("Update request from %s: sending update",
5510 /* Otherwise, there are no updates to send, so we can
5511 just send an UPDDONE message immediately. */
5512 dhcp_failover_send_update_done (state
);
5513 log_info ("Update request from %s: nothing pending",
5517 return ISC_R_SUCCESS
;
5521 dhcp_failover_process_update_request_all (dhcp_failover_state_t
*state
,
5522 failover_message_t
*msg
)
5524 if (state
->send_update_done
) {
5525 log_info("Received update request while old update still "
5526 "flying! Silently discarding old request.");
5527 lease_dereference(&state
->send_update_done
, MDL
);
5530 /* Generate a fresh update queue that includes every lease. */
5531 dhcp_failover_generate_update_queue (state
, 1);
5533 state
->updxid
= msg
->xid
;
5535 if (state
-> update_queue_tail
) {
5536 lease_reference (&state
-> send_update_done
,
5537 state
-> update_queue_tail
, MDL
);
5538 dhcp_failover_send_updates (state
);
5539 log_info ("Update request all from %s: sending update",
5542 /* This should really never happen, but it could happen
5543 on a server that currently has no leases configured. */
5544 dhcp_failover_send_update_done (state
);
5545 log_info ("Update request all from %s: nothing pending",
5549 return ISC_R_SUCCESS
;
5553 dhcp_failover_process_update_done (dhcp_failover_state_t
*state
,
5554 failover_message_t
*msg
)
5558 log_info ("failover peer %s: peer update completed.",
5561 state
-> curUPD
= 0;
5563 switch (state
-> me
.state
) {
5567 case communications_interrupted
:
5568 case resolution_interrupted
:
5574 break; /* shouldn't happen. */
5576 /* We got the UPDDONE, so we can go into normal state! */
5577 case potential_conflict
:
5578 if (state
->partner
.state
== conflict_done
) {
5579 if (state
->i_am
== secondary
) {
5580 dhcp_failover_set_state (state
, normal
);
5582 log_error("Secondary is in conflict_done "
5583 "state after conflict resolution, "
5584 "this is illegal.");
5585 dhcp_failover_set_state (state
, shut_down
);
5588 if (state
->i_am
== primary
)
5589 dhcp_failover_set_state (state
, conflict_done
);
5591 log_error("Spurious update-done message.");
5597 log_error("Spurious update-done message.");
5601 /* Wait for MCLT to expire before moving to recover_done,
5602 except that if both peers come up in recover, there is
5603 no point in waiting for MCLT to expire - this probably
5604 indicates the initial startup of a newly-configured
5606 if (state
-> me
.stos
+ state
-> mclt
> cur_time
&&
5607 state
-> partner
.state
!= recover
&&
5608 state
-> partner
.state
!= recover_done
) {
5609 dhcp_failover_set_state (state
, recover_wait
);
5610 #if defined (DEBUG_FAILOVER_TIMING)
5611 log_info ("add_timeout +%d %s",
5613 state
-> me
.stos
+ state
-> mclt
),
5614 "dhcp_failover_recover_done");
5616 tv
. tv_sec
= (int)(state
-> me
.stos
+ state
-> mclt
);
5619 dhcp_failover_recover_done
,
5621 (tvref_t
)omapi_object_reference
,
5623 omapi_object_dereference
);
5625 dhcp_failover_recover_done (state
);
5628 return ISC_R_SUCCESS
;
5631 void dhcp_failover_recover_done (void *sp
)
5633 dhcp_failover_state_t
*state
= sp
;
5635 #if defined (DEBUG_FAILOVER_TIMING)
5636 log_info ("dhcp_failover_recover_done");
5639 dhcp_failover_set_state (state
, recover_done
);
5642 #if defined (DEBUG_FAILOVER_MESSAGES)
5643 /* Print hunks of failover messages, doing line breaks as appropriate.
5644 Note that this assumes syslog is being used, rather than, e.g., the
5645 Windows NT logging facility, where just dumping the whole message in
5646 one hunk would be more appropriate. */
5648 void failover_print (char *obuf
,
5649 unsigned *obufix
, unsigned obufmax
, const char *s
)
5651 int len
= strlen (s
);
5653 while (len
+ *obufix
+ 1 >= obufmax
) {
5654 log_debug ("%s", obuf
);
5656 log_debug ("%s", s
);
5662 strcpy (&obuf
[*obufix
], s
);
5665 #endif /* defined (DEBUG_FAILOVER_MESSAGES) */
5667 /* Taken from draft-ietf-dhc-loadb-01.txt: */
5668 /* A "mixing table" of 256 distinct values, in pseudo-random order. */
5669 unsigned char loadb_mx_tbl
[256] = {
5670 251, 175, 119, 215, 81, 14, 79, 191, 103, 49,
5671 181, 143, 186, 157, 0, 232, 31, 32, 55, 60,
5672 152, 58, 17, 237, 174, 70, 160, 144, 220, 90,
5673 57, 223, 59, 3, 18, 140, 111, 166, 203, 196,
5674 134, 243, 124, 95, 222, 179, 197, 65, 180, 48,
5675 36, 15, 107, 46, 233, 130, 165, 30, 123, 161,
5676 209, 23, 97, 16, 40, 91, 219, 61, 100, 10,
5677 210, 109, 250, 127, 22, 138, 29, 108, 244, 67,
5678 207, 9, 178, 204, 74, 98, 126, 249, 167, 116,
5679 34, 77, 193, 200, 121, 5, 20, 113, 71, 35,
5680 128, 13, 182, 94, 25, 226, 227, 199, 75, 27,
5681 41, 245, 230, 224, 43, 225, 177, 26, 155, 150,
5682 212, 142, 218, 115, 241, 73, 88, 105, 39, 114,
5683 62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
5684 154, 122, 12, 84, 82, 163, 44, 139, 228, 236,
5685 205, 242, 217, 11, 187, 146, 159, 64, 86, 239,
5686 195, 42, 106, 198, 118, 112, 184, 172, 87, 2,
5687 173, 117, 176, 229, 247, 253, 137, 185, 99, 164,
5688 102, 147, 45, 66, 231, 52, 141, 211, 194, 206,
5689 246, 238, 56, 110, 78, 248, 63, 240, 189, 93,
5690 92, 51, 53, 183, 19, 171, 72, 50, 33, 104,
5691 101, 69, 8, 252, 83, 120, 76, 135, 85, 54,
5692 202, 125, 188, 213, 96, 235, 136, 208, 162, 129,
5693 190, 132, 156, 38, 47, 1, 7, 254, 24, 4,
5694 216, 131, 89, 21, 28, 133, 37, 153, 149, 80,
5695 170, 68, 6, 169, 234, 151 };
5697 static unsigned char loadb_p_hash (const unsigned char *, unsigned);
5698 static unsigned char loadb_p_hash (const unsigned char *key
, unsigned len
)
5700 unsigned char hash
= len
;
5702 for(i
= len
; i
> 0; )
5703 hash
= loadb_mx_tbl
[hash
^ (key
[--i
])];
5707 int load_balance_mine (struct packet
*packet
, dhcp_failover_state_t
*state
)
5709 struct option_cache
*oc
;
5710 struct data_string ds
;
5711 unsigned char hbaix
;
5714 if (state
-> load_balance_max_secs
< ntohs (packet
-> raw
-> secs
)) {
5718 /* If we don't have a hash bucket array, we can't tell if this
5719 one's ours, so we assume it's not. */
5723 oc
= lookup_option (&dhcp_universe
, packet
-> options
,
5724 DHO_DHCP_CLIENT_IDENTIFIER
);
5725 memset (&ds
, 0, sizeof ds
);
5727 evaluate_option_cache (&ds
, packet
, (struct lease
*)0,
5728 (struct client_state
*)0,
5729 packet
-> options
, (struct option_state
*)0,
5730 &global_scope
, oc
, MDL
)) {
5731 hbaix
= loadb_p_hash (ds
.data
, ds
.len
);
5733 data_string_forget(&ds
, MDL
);
5735 hbaix
= loadb_p_hash (packet
-> raw
-> chaddr
,
5736 packet
-> raw
-> hlen
);
5739 hm
= state
->hba
[(hbaix
>> 3) & 0x1F] & (1 << (hbaix
& 0x07));
5741 if (state
-> i_am
== primary
)
5747 /* The inverse of load_balance_mine ("load balance theirs"). We can't
5748 * use the regular load_balance_mine() and invert it because of the case
5749 * where there might not be an HBA, and we want to indicate false here
5750 * in this case only.
5753 peer_wants_lease(struct lease
*lp
)
5755 dhcp_failover_state_t
*state
;
5756 unsigned char hbaix
;
5762 state
= lp
->pool
->failover_peer
;
5764 if (!state
|| !state
->hba
)
5768 hbaix
= loadb_p_hash(lp
->uid
, lp
->uid_len
);
5769 else if (lp
->hardware_addr
.hlen
> 1)
5770 /* Skip the first byte, which is the hardware type, and is
5771 * not included during actual load balancing checks above
5772 * since it is separate from the packet header chaddr field.
5773 * The remainder of the hardware address should be identical
5774 * to the chaddr contents.
5776 hbaix
= loadb_p_hash(lp
->hardware_addr
.hbuf
+ 1,
5777 lp
->hardware_addr
.hlen
- 1);
5778 else /* impossible to categorize into LBA */
5781 hm
= state
->hba
[(hbaix
>> 3) & 0x1F] & (1 << (hbaix
& 0x07));
5783 if (state
->i_am
== primary
)
5789 /* This deals with what to do with bind updates when
5790 we're in the normal state
5792 Note that tsfp had better be set from the latest bind update
5793 _before_ this function is called! */
5796 normal_binding_state_transition_check (struct lease
*lease
,
5797 dhcp_failover_state_t
*state
,
5798 binding_state_t binding_state
,
5801 binding_state_t new_state
;
5803 /* If there is no transition, it's no problem. */
5804 if (binding_state
== lease
-> binding_state
)
5805 return binding_state
;
5807 switch (lease
-> binding_state
) {
5810 switch (binding_state
) {
5817 /* If the lease was free, and our peer is primary,
5818 then it can make it active, or abandoned, or
5819 backup. Abandoned is treated like free in
5821 if (state
-> i_am
== secondary
)
5822 return binding_state
;
5824 /* Otherwise, it can't legitimately do any sort of
5825 state transition. Because the lease was free,
5826 and the error has already been made, we allow the
5827 peer to change its state anyway, but log a warning
5828 message in hopes that the error will be fixed. */
5829 case FTS_FREE
: /* for compiler */
5830 new_state
= binding_state
;
5834 log_fatal ("Impossible case at %s:%d.", MDL
);
5838 /* The secondary can't change the state of an active
5840 if (state
-> i_am
== primary
) {
5841 /* Except that the client may send the DHCPRELEASE
5842 to the secondary, and we have to accept that. */
5843 if (binding_state
== FTS_RELEASED
)
5844 return binding_state
;
5845 new_state
= lease
-> binding_state
;
5849 /* So this is only for transitions made by the primary: */
5850 switch (binding_state
) {
5853 /* Can't set a lease to free or backup until the
5854 peer agrees that it's expired. */
5855 if (tsfp
> cur_time
) {
5856 new_state
= lease
-> binding_state
;
5859 return binding_state
;
5862 /* XXX 65 should be the clock skew between the peers
5863 XXX plus a fudge factor. This code will result
5864 XXX in problems if MCLT is really short or the
5865 XXX max-lease-time is really short (less than the
5866 XXX fudge factor. */
5867 if (lease
-> ends
- 65 > cur_time
) {
5868 new_state
= lease
-> binding_state
;
5876 return binding_state
;
5879 log_fatal ("Impossible case at %s:%d.", MDL
);
5884 switch (binding_state
) {
5887 /* Can't set a lease to free or backup until the
5888 peer agrees that it's expired. */
5889 if (tsfp
> cur_time
) {
5890 new_state
= lease
-> binding_state
;
5893 return binding_state
;
5900 return binding_state
;
5903 log_fatal ("Impossible case at %s:%d.", MDL
);
5907 switch (binding_state
) {
5911 /* These are invalid state transitions - should we
5918 return binding_state
;
5921 log_fatal ("Impossible case at %s:%d.", MDL
);
5925 switch (binding_state
) {
5928 /* Can't set a lease to free or backup until the
5929 peer agrees that it's expired. */
5930 if (tsfp
> cur_time
) {
5931 new_state
= lease
-> binding_state
;
5934 return binding_state
;
5941 return binding_state
;
5944 log_fatal ("Impossible case at %s:%d.", MDL
);
5948 switch (binding_state
) {
5954 /* If the lease was in backup, and our peer
5955 is secondary, then it can make it active
5957 if (state
-> i_am
== primary
)
5958 return binding_state
;
5960 /* Either the primary or the secondary can
5961 reasonably move a lease from the backup
5962 state to the free state. */
5964 return binding_state
;
5967 new_state
= lease
-> binding_state
;
5971 log_fatal ("Impossible case at %s:%d.", MDL
);
5976 log_fatal ("Impossible case at %s:%d.", MDL
);
5983 /* Determine whether the state transition is okay when we're potentially
5984 in conflict with the peer. */
5986 conflict_binding_state_transition_check (struct lease
*lease
,
5987 dhcp_failover_state_t
*state
,
5988 binding_state_t binding_state
,
5991 binding_state_t new_state
;
5993 /* If there is no transition, it's no problem. */
5994 if (binding_state
== lease
-> binding_state
)
5995 new_state
= binding_state
;
5997 switch (lease
-> binding_state
) {
5998 /* If we think the lease is not in use, then the
5999 state into which the partner put it is just fine,
6007 new_state
= binding_state
;
6010 /* If we think the lease *is* in use, then we're not
6011 going to take the partner's change if the partner
6012 thinks it's free. */
6014 switch (binding_state
) {
6017 new_state
= lease
-> binding_state
;
6021 /* If we don't agree about expiry, it's
6022 * invalid. 65 should allow for max
6023 * clock skew (60) plus some fudge.
6024 * XXX: should we refetch cur_time?
6026 if ((lease
->ends
- 65) > cur_time
)
6027 new_state
= lease
->binding_state
;
6029 new_state
= binding_state
;
6032 /* RELEASED, RESET, and ABANDONED indicate
6033 * that our partner has information about
6034 * this lease that we did not witness. Our
6040 new_state
= binding_state
;
6044 log_fatal ("Impossible case at %s:%d.", MDL
);
6050 log_fatal ("Impossible case at %s:%d.", MDL
);
6057 /* We can reallocate a lease under the following circumstances:
6059 (1) It belongs to us - it's FTS_FREE, and we're primary, or it's
6060 FTS_BACKUP, and we're secondary.
6061 (2) We're in partner_down, and the lease is not active, and we
6062 can be sure that the other server didn't make it active.
6063 We can only be sure that the server didn't make it active
6064 when we are in the partner_down state and one of the following
6065 two conditions holds:
6066 (a) in the case that the time sent from the peer is earlier than
6067 the time we entered the partner_down state, at least MCLT has
6068 gone by since we entered partner_down, or
6069 (b) in the case that the time sent from the peer is later than
6070 the time when we entered partner_down, the current time is
6071 later than the time sent from the peer by at least MCLT. */
6073 int lease_mine_to_reallocate (struct lease
*lease
)
6075 dhcp_failover_state_t
*peer
;
6077 if (lease
&& lease
->pool
&&
6078 (peer
= lease
->pool
->failover_peer
)) {
6079 switch (lease
->binding_state
) {
6081 /* ACTIVE leases may not be reallocated. */
6086 /* FREE leases may only be allocated by the primary,
6087 * unless the secondary is acting in partner_down
6088 * state and stos+mclt or tsfp+mclt has expired,
6089 * whichever is greater.
6091 * ABANDONED are treated the same as FREE for all
6092 * purposes here. Note that servers will only try
6093 * for ABANDONED leases as a last resort anyway.
6095 if (peer
-> i_am
== primary
)
6098 return(peer
->service_state
== service_partner_down
&&
6099 ((lease
->tsfp
< peer
->me
.stos
) ?
6100 (peer
->me
.stos
+ peer
->mclt
< cur_time
) :
6101 (lease
->tsfp
+ peer
->mclt
< cur_time
)));
6106 /* These three lease states go onto the 'expired'
6107 * queue. Upon entry into partner-down state, this
6108 * queue of leases has their tsfp values modified
6109 * to equal stos+mclt, the point at which the server
6110 * is allowed to remove them from these transitional
6111 * states without an acknowledgement.
6113 * Note that although tsfp has been possibly extended
6114 * past the actual tsfp we received from the peer, we
6115 * don't have to take any special action. Since tsfp
6116 * is now in the past (or now), we can guarantee that
6117 * this server will only allocate a lease time equal
6118 * to MCLT, rather than a TSFP-optimal lease, which is
6119 * the only danger for a lease in one of these states.
6121 return((peer
->service_state
== service_partner_down
) &&
6122 (lease
->tsfp
< cur_time
));
6125 /* Only the secondary may allocate BACKUP leases,
6126 * unless in partner_down state in which case at
6127 * least TSFP+MCLT or STOS+MCLT must have expired,
6128 * whichever is greater.
6130 if (peer
->i_am
== secondary
)
6133 return((peer
->service_state
== service_partner_down
) &&
6134 ((lease
->tsfp
< peer
->me
.stos
) ?
6135 (peer
->me
.stos
+ peer
->mclt
< cur_time
) :
6136 (lease
->tsfp
+ peer
->mclt
< cur_time
)));
6139 /* All lease states appear above. */
6140 log_fatal("Impossible case at %s:%d.", MDL
);
6146 return(lease
->binding_state
== FTS_FREE
||
6147 lease
->binding_state
== FTS_BACKUP
);
6152 static isc_result_t
failover_message_reference (failover_message_t
**mp
,
6153 failover_message_t
*m
,
6154 const char *file
, int line
)
6158 return ISC_R_SUCCESS
;
6161 static isc_result_t
failover_message_dereference (failover_message_t
**mp
,
6162 const char *file
, int line
)
6164 failover_message_t
*m
;
6167 if (m
-> refcnt
== 0) {
6169 failover_message_dereference (&m
-> next
,
6171 if (m
-> chaddr
.data
)
6172 dfree (m
-> chaddr
.data
, file
, line
);
6173 if (m
-> client_identifier
.data
)
6174 dfree (m
-> client_identifier
.data
, file
, line
);
6176 dfree (m
-> hba
.data
, file
, line
);
6177 if (m
-> message
.data
)
6178 dfree (m
-> message
.data
, file
, line
);
6179 if (m
-> reply_options
.data
)
6180 dfree (m
-> reply_options
.data
, file
, line
);
6181 if (m
-> request_options
.data
)
6182 dfree (m
-> request_options
.data
, file
, line
);
6183 if (m
-> vendor_class
.data
)
6184 dfree (m
-> vendor_class
.data
, file
, line
);
6185 if (m
-> vendor_options
.data
)
6186 dfree (m
-> vendor_options
.data
, file
, line
);
6188 dfree (m
-> ddns
.data
, file
, line
);
6189 dfree (*mp
, file
, line
);
6192 return ISC_R_SUCCESS
;
6195 OMAPI_OBJECT_ALLOC (dhcp_failover_state
, dhcp_failover_state_t
,
6196 dhcp_type_failover_state
)
6197 OMAPI_OBJECT_ALLOC (dhcp_failover_listener
, dhcp_failover_listener_t
,
6198 dhcp_type_failover_listener
)
6199 OMAPI_OBJECT_ALLOC (dhcp_failover_link
, dhcp_failover_link_t
,
6200 dhcp_type_failover_link
)
6201 #endif /* defined (FAILOVER_PROTOCOL) */
6203 const char *binding_state_print (enum failover_state state
)