3 Failover protocol support code... */
6 * Copyright (c) 2004-2014 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
25 * https://www.isc.org/
31 #include <omapip/omapip_p.h>
33 #if defined (FAILOVER_PROTOCOL)
34 dhcp_failover_state_t
*failover_states
;
35 static isc_result_t
do_a_failover_option (omapi_object_t
*,
36 dhcp_failover_link_t
*);
37 dhcp_failover_listener_t
*failover_listeners
;
39 static isc_result_t
failover_message_reference (failover_message_t
**,
41 const char *file
, int line
);
42 static isc_result_t
failover_message_dereference (failover_message_t
**,
43 const char *file
, int line
);
45 static void dhcp_failover_pool_balance(dhcp_failover_state_t
*state
);
46 static void dhcp_failover_pool_reqbalance(dhcp_failover_state_t
*state
);
47 static int dhcp_failover_pool_dobalance(dhcp_failover_state_t
*state
,
48 isc_boolean_t
*sendreq
);
49 static inline int secondary_not_hoarding(dhcp_failover_state_t
*state
,
53 void dhcp_failover_startup ()
55 dhcp_failover_state_t
*state
;
59 for (state
= failover_states
; state
; state
= state
-> next
) {
60 dhcp_failover_state_transition (state
, "startup");
62 if (state
-> pool_count
== 0) {
63 log_error ("failover peer declaration with no %s",
65 log_error ("In order to use failover, you MUST %s",
66 "refer to your main failover declaration");
67 log_error ("in each pool declaration. You MUST %s",
68 "NOT use range declarations outside");
69 log_fatal ("of pool declarations.");
71 /* In case the peer is already running, immediately try
72 to establish a connection with it. */
73 status
= dhcp_failover_link_initiate ((omapi_object_t
*)state
);
74 if (status
!= ISC_R_SUCCESS
&& status
!= DHCP_R_INCOMPLETE
) {
75 #if defined (DEBUG_FAILOVER_TIMING)
76 log_info ("add_timeout +90 dhcp_failover_reconnect");
78 tv
. tv_sec
= cur_time
+ 90;
81 dhcp_failover_reconnect
, state
,
83 dhcp_failover_state_reference
,
85 dhcp_failover_state_dereference
);
86 log_error ("failover peer %s: %s", state
-> name
,
87 isc_result_totext (status
));
90 status
= (dhcp_failover_listen
91 ((omapi_object_t
*)state
));
92 if (status
!= ISC_R_SUCCESS
) {
93 #if defined (DEBUG_FAILOVER_TIMING)
94 log_info ("add_timeout +90 %s",
95 "dhcp_failover_listener_restart");
97 tv
. tv_sec
= cur_time
+ 90;
100 dhcp_failover_listener_restart
,
102 (tvref_t
)omapi_object_reference
,
103 (tvunref_t
)omapi_object_dereference
);
108 int dhcp_failover_write_all_states ()
110 dhcp_failover_state_t
*state
;
112 for (state
= failover_states
; state
; state
= state
-> next
) {
113 if (!write_failover_state (state
))
119 isc_result_t
enter_failover_peer (peer
)
120 dhcp_failover_state_t
*peer
;
122 dhcp_failover_state_t
*dup
= (dhcp_failover_state_t
*)0;
125 status
= find_failover_peer (&dup
, peer
-> name
, MDL
);
126 if (status
== ISC_R_NOTFOUND
) {
127 if (failover_states
) {
128 dhcp_failover_state_reference (&peer
-> next
,
129 failover_states
, MDL
);
130 dhcp_failover_state_dereference (&failover_states
,
133 dhcp_failover_state_reference (&failover_states
, peer
, MDL
);
134 return ISC_R_SUCCESS
;
136 dhcp_failover_state_dereference (&dup
, MDL
);
137 if (status
== ISC_R_SUCCESS
)
142 isc_result_t
find_failover_peer (peer
, name
, file
, line
)
143 dhcp_failover_state_t
**peer
;
148 dhcp_failover_state_t
*p
;
150 for (p
= failover_states
; p
; p
= p
-> next
)
151 if (!strcmp (name
, p
-> name
))
154 return dhcp_failover_state_reference (peer
, p
, file
, line
);
155 return ISC_R_NOTFOUND
;
158 /* The failover protocol has three objects associated with it. For
159 each failover partner declaration in the dhcpd.conf file, primary
160 or secondary, there is a failover_state object. For any primary or
161 secondary state object that has a connection to its peer, there is
162 also a failover_link object, which has its own input state separate
163 from the failover protocol state for managing the actual bytes
164 coming in off the wire. Finally, there will be one listener object
165 for every distinct port number associated with a secondary
166 failover_state object. Normally all secondary failover_state
167 objects are expected to listen on the same port number, so there
168 need be only one listener object, but if different port numbers are
169 specified for each failover object, there could be as many as one
170 listener object for each secondary failover_state object. */
172 /* This, then, is the implementation of the failover link object. */
174 isc_result_t
dhcp_failover_link_initiate (omapi_object_t
*h
)
177 dhcp_failover_link_t
*obj
;
178 dhcp_failover_state_t
*state
;
181 struct data_string ds
;
182 omapi_addr_list_t
*addrs
= (omapi_addr_list_t
*)0;
183 omapi_addr_t local_addr
;
185 /* Find the failover state in the object chain. */
186 for (o
= h
; o
-> outer
; o
= o
-> outer
)
188 for (; o
; o
= o
-> inner
) {
189 if (o
-> type
== dhcp_type_failover_state
)
193 return DHCP_R_INVALIDARG
;
194 state
= (dhcp_failover_state_t
*)o
;
196 obj
= (dhcp_failover_link_t
*)0;
197 status
= dhcp_failover_link_allocate (&obj
, MDL
);
198 if (status
!= ISC_R_SUCCESS
)
200 option_cache_reference (&obj
-> peer_address
,
201 state
-> partner
.address
, MDL
);
202 obj
-> peer_port
= state
-> partner
.port
;
203 dhcp_failover_state_reference (&obj
-> state_object
, state
, MDL
);
205 memset (&ds
, 0, sizeof ds
);
206 if (!evaluate_option_cache (&ds
, (struct packet
*)0, (struct lease
*)0,
207 (struct client_state
*)0,
208 (struct option_state
*)0,
209 (struct option_state
*)0,
210 &global_scope
, obj
-> peer_address
, MDL
)) {
211 dhcp_failover_link_dereference (&obj
, MDL
);
212 return ISC_R_UNEXPECTED
;
215 /* Make an omapi address list out of a buffer containing zero or more
217 status
= omapi_addr_list_new (&addrs
, ds
.len
/ 4, MDL
);
218 if (status
!= ISC_R_SUCCESS
) {
219 dhcp_failover_link_dereference (&obj
, MDL
);
223 for (i
= 0; i
< addrs
-> count
; i
++) {
224 addrs
-> addresses
[i
].addrtype
= AF_INET
;
225 addrs
-> addresses
[i
].addrlen
= sizeof (struct in_addr
);
226 memcpy (addrs
-> addresses
[i
].address
,
227 &ds
.data
[i
* 4], sizeof (struct in_addr
));
228 addrs
-> addresses
[i
].port
= obj
-> peer_port
;
230 data_string_forget (&ds
, MDL
);
232 /* Now figure out the local address that we're supposed to use. */
233 if (!state
-> me
.address
||
234 !evaluate_option_cache (&ds
, (struct packet
*)0,
236 (struct client_state
*)0,
237 (struct option_state
*)0,
238 (struct option_state
*)0,
239 &global_scope
, state
-> me
.address
,
241 memset (&local_addr
, 0, sizeof local_addr
);
242 local_addr
.addrtype
= AF_INET
;
243 local_addr
.addrlen
= sizeof (struct in_addr
);
244 if (!state
-> server_identifier
.len
) {
245 log_fatal ("failover peer %s: no local address.",
249 if (ds
.len
!= sizeof (struct in_addr
)) {
250 log_error("failover peer %s: 'address' parameter "
251 "fails to resolve to an IPv4 address",
253 data_string_forget (&ds
, MDL
);
254 dhcp_failover_link_dereference (&obj
, MDL
);
255 omapi_addr_list_dereference (&addrs
, MDL
);
256 return DHCP_R_INVALIDARG
;
258 local_addr
.addrtype
= AF_INET
;
259 local_addr
.addrlen
= ds
.len
;
260 memcpy (local_addr
.address
, ds
.data
, ds
.len
);
261 if (!state
-> server_identifier
.len
)
262 data_string_copy (&state
-> server_identifier
,
264 data_string_forget (&ds
, MDL
);
265 local_addr
.port
= 0; /* Let the O.S. choose. */
268 status
= omapi_connect_list ((omapi_object_t
*)obj
,
270 omapi_addr_list_dereference (&addrs
, MDL
);
272 dhcp_failover_link_dereference (&obj
, MDL
);
276 isc_result_t
dhcp_failover_link_signal (omapi_object_t
*h
,
277 const char *name
, va_list ap
)
280 dhcp_failover_link_t
*link
;
282 dhcp_failover_state_t
*s
, *state
= (dhcp_failover_state_t
*)0;
287 if (h
-> type
!= dhcp_type_failover_link
) {
288 /* XXX shouldn't happen. Put an assert here? */
289 return ISC_R_UNEXPECTED
;
291 link
= (dhcp_failover_link_t
*)h
;
293 if (!strcmp (name
, "connect")) {
294 if (link
-> state_object
-> i_am
== primary
) {
295 status
= dhcp_failover_send_connect (h
);
296 if (status
!= ISC_R_SUCCESS
) {
297 log_info ("dhcp_failover_send_connect: %s",
298 isc_result_totext (status
));
299 omapi_disconnect (h
-> outer
, 1);
302 status
= ISC_R_SUCCESS
;
303 /* Allow the peer fifteen seconds to send us a
305 #if defined (DEBUG_FAILOVER_TIMING)
306 log_info ("add_timeout +15 %s",
307 "dhcp_failover_link_startup_timeout");
309 tv
. tv_sec
= cur_time
+ 15;
312 dhcp_failover_link_startup_timeout
,
314 (tvref_t
)dhcp_failover_link_reference
,
315 (tvunref_t
)dhcp_failover_link_dereference
);
319 if (!strcmp (name
, "disconnect")) {
320 if (link
-> state_object
) {
321 dhcp_failover_state_reference (&state
,
322 link
-> state_object
, MDL
);
323 link
-> state
= dhcp_flink_disconnected
;
325 /* Make the transition. */
326 if (state
->link_to_peer
== link
)
327 dhcp_failover_state_transition(link
->state_object
, name
);
329 /* Schedule an attempt to reconnect. */
330 #if defined (DEBUG_FAILOVER_TIMING)
331 log_info("add_timeout +5 dhcp_failover_reconnect");
333 tv
.tv_sec
= cur_time
+ 5;
334 tv
.tv_usec
= cur_tv
.tv_usec
;
335 add_timeout(&tv
, dhcp_failover_reconnect
, state
,
336 (tvref_t
)dhcp_failover_state_reference
,
337 (tvunref_t
)dhcp_failover_state_dereference
);
339 dhcp_failover_state_dereference (&state
, MDL
);
341 return ISC_R_SUCCESS
;
344 if (!strcmp (name
, "status")) {
345 if (link
-> state_object
) {
348 status
= va_arg(ap
, isc_result_t
);
350 if ((status
== ISC_R_HOSTUNREACH
) || (status
== ISC_R_TIMEDOUT
)) {
351 dhcp_failover_state_reference (&state
,
352 link
-> state_object
, MDL
);
353 link
-> state
= dhcp_flink_disconnected
;
355 /* Make the transition. */
356 dhcp_failover_state_transition (link
-> state_object
,
359 /* Start trying to reconnect. */
360 #if defined (DEBUG_FAILOVER_TIMING)
361 log_info ("add_timeout +5 %s",
362 "dhcp_failover_reconnect");
364 tv
. tv_sec
= cur_time
+ 5;
366 add_timeout (&tv
, dhcp_failover_reconnect
,
368 (tvref_t
)dhcp_failover_state_reference
,
369 (tvunref_t
)dhcp_failover_state_dereference
);
371 dhcp_failover_state_dereference (&state
, MDL
);
373 return ISC_R_SUCCESS
;
376 /* Not a signal we recognize? */
377 if (strcmp (name
, "ready")) {
378 if (h
-> inner
&& h
-> inner
-> type
-> signal_handler
)
379 return (*(h
-> inner
-> type
-> signal_handler
))
380 (h
-> inner
, name
, ap
);
381 return ISC_R_NOTFOUND
;
384 if (!h
-> outer
|| h
-> outer
-> type
!= omapi_type_connection
)
385 return DHCP_R_INVALIDARG
;
388 /* We get here because we requested that we be woken up after
389 some number of bytes were read, and that number of bytes
390 has in fact been read. */
391 switch (link
-> state
) {
392 case dhcp_flink_start
:
393 link
-> state
= dhcp_flink_message_length_wait
;
394 if ((omapi_connection_require (c
, 2)) != ISC_R_SUCCESS
)
396 case dhcp_flink_message_length_wait
:
398 link
-> state
= dhcp_flink_message_wait
;
399 link
-> imsg
= dmalloc (sizeof (failover_message_t
), MDL
);
401 status
= ISC_R_NOMEMORY
;
404 failover_message_dereference (&link
->imsg
,
407 link
-> state
= dhcp_flink_disconnected
;
408 log_info ("message length wait: %s",
409 isc_result_totext (status
));
410 omapi_disconnect (c
, 1);
411 /* XXX just blow away the protocol state now?
412 XXX or will disconnect blow it away? */
413 return ISC_R_UNEXPECTED
;
415 memset (link
-> imsg
, 0, sizeof (failover_message_t
));
416 link
-> imsg
-> refcnt
= 1;
417 /* Get the length: */
418 omapi_connection_get_uint16 (c
, &link
-> imsg_len
);
419 link
-> imsg_count
= 0; /* Bytes read. */
421 /* Ensure the message is of valid length. */
422 if (link
->imsg_len
< DHCP_FAILOVER_MIN_MESSAGE_SIZE
||
423 link
->imsg_len
> DHCP_FAILOVER_MAX_MESSAGE_SIZE
) {
424 status
= ISC_R_UNEXPECTED
;
425 goto dhcp_flink_fail
;
428 if ((omapi_connection_require (c
, link
-> imsg_len
- 2U)) !=
431 case dhcp_flink_message_wait
:
432 /* Read in the message. At this point we have the
433 entire message in the input buffer. For each
434 incoming value ID, set a bit in the bitmask
435 indicating that we've gotten it. Maybe flag an
436 error message if the bit is already set. Once
437 we're done reading, we can check the bitmask to
438 make sure that the required fields for each message
439 have been included. */
441 link
-> imsg_count
+= 2; /* Count the length as read. */
443 /* Get message type. */
444 omapi_connection_copyout (&link
-> imsg
-> type
, c
, 1);
445 link
-> imsg_count
++;
447 /* Get message payload offset. */
448 omapi_connection_copyout (&link
-> imsg_payoff
, c
, 1);
449 link
-> imsg_count
++;
451 /* Get message time. */
452 omapi_connection_get_uint32 (c
, &link
-> imsg
-> time
);
453 link
-> imsg_count
+= 4;
455 /* Get transaction ID. */
456 omapi_connection_get_uint32 (c
, &link
-> imsg
-> xid
);
457 link
-> imsg_count
+= 4;
459 #if defined (DEBUG_FAILOVER_MESSAGES)
460 # if !defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
461 if (link
->imsg
->type
== FTM_CONTACT
)
464 log_info ("link: message %s payoff %d time %ld xid %ld",
465 dhcp_failover_message_name (link
-> imsg
-> type
),
467 (unsigned long)link
-> imsg
-> time
,
468 (unsigned long)link
-> imsg
-> xid
);
469 # if !defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
473 /* Skip over any portions of the message header that we
475 if (link
-> imsg_payoff
- link
-> imsg_count
) {
476 omapi_connection_copyout ((unsigned char *)0, c
,
477 (link
-> imsg_payoff
-
478 link
-> imsg_count
));
479 link
-> imsg_count
= link
-> imsg_payoff
;
482 /* Now start sucking options off the wire. */
483 while (link
-> imsg_count
< link
-> imsg_len
) {
484 status
= do_a_failover_option (c
, link
);
485 if (status
!= ISC_R_SUCCESS
)
486 goto dhcp_flink_fail
;
489 /* If it's a connect message, try to associate it with
491 /* XXX this should be authenticated! */
492 if (link
-> imsg
-> type
== FTM_CONNECT
) {
496 if (!(link
->imsg
->options_present
&
497 FTB_RELATIONSHIP_NAME
)) {
498 errmsg
= "missing relationship-name";
499 reason
= FTR_INVALID_PARTNER
;
503 /* See if we can find a failover_state object that
504 matches this connection. This message should only
505 be received by a secondary from a primary. */
506 for (s
= failover_states
; s
; s
= s
-> next
) {
507 if (dhcp_failover_state_match_by_name(s
,
508 &link
->imsg
->relationship_name
))
512 /* If we can't find a failover protocol state
513 for this remote host, drop the connection */
515 errmsg
= "unknown failover relationship name";
516 reason
= FTR_INVALID_PARTNER
;
519 /* XXX Send a refusal message first?
520 XXX Look in protocol spec for guidance. */
524 slen
= strlen(sname
);
525 } else if (link
->imsg
->options_present
&
526 FTB_RELATIONSHIP_NAME
) {
527 sname
= (char *)link
->imsg
->
528 relationship_name
.data
;
529 slen
= link
->imsg
->relationship_name
.count
;
532 slen
= strlen(sname
);
535 log_error("Failover CONNECT from %.*s: %s",
536 slen
, sname
, errmsg
);
537 dhcp_failover_send_connectack
538 ((omapi_object_t
*)link
, state
,
540 log_info ("failover: disconnect: %s", errmsg
);
541 omapi_disconnect (c
, 0);
542 link
-> state
= dhcp_flink_disconnected
;
543 return ISC_R_SUCCESS
;
546 if ((cur_time
> link
-> imsg
-> time
&&
547 cur_time
- link
-> imsg
-> time
> 60) ||
548 (cur_time
< link
-> imsg
-> time
&&
549 link
-> imsg
-> time
- cur_time
> 60)) {
550 errmsg
= "time offset too large";
551 reason
= FTR_TIMEMISMATCH
;
555 if (!(link
-> imsg
-> options_present
& FTB_HBA
) ||
556 link
-> imsg
-> hba
.count
!= 32) {
557 errmsg
= "invalid HBA";
558 reason
= FTR_HBA_CONFLICT
; /* XXX */
562 dfree (state
-> hba
, MDL
);
563 state
-> hba
= dmalloc (32, MDL
);
565 errmsg
= "no memory";
566 reason
= FTR_MISC_REJECT
;
569 memcpy (state
-> hba
, link
-> imsg
-> hba
.data
, 32);
571 if (!link
-> state_object
)
572 dhcp_failover_state_reference
573 (&link
-> state_object
, state
, MDL
);
574 if (!link
-> peer_address
)
575 option_cache_reference
576 (&link
-> peer_address
,
577 state
-> partner
.address
, MDL
);
580 /* If we don't have a state object at this point, it's
581 some kind of bogus situation, so just drop the
583 if (!link
-> state_object
) {
584 log_info ("failover: connect: no matching state.");
585 omapi_disconnect (c
, 1);
586 link
-> state
= dhcp_flink_disconnected
;
587 return DHCP_R_INVALIDARG
;
590 /* Once we have the entire message, and we've validated
591 it as best we can here, pass it to the parent. */
592 omapi_signal ((omapi_object_t
*)link
-> state_object
,
594 link
-> state
= dhcp_flink_message_length_wait
;
596 failover_message_dereference (&link
-> imsg
, MDL
);
597 /* XXX This is dangerous because we could get into a tight
598 XXX loop reading input without servicing any other stuff.
599 XXX There needs to be a way to relinquish control but
600 XXX get it back immediately if there's no other work to
602 if ((omapi_connection_require (c
, 2)) == ISC_R_SUCCESS
)
607 log_fatal("Impossible case at %s:%d.", MDL
);
610 return ISC_R_SUCCESS
;
613 static isc_result_t
do_a_failover_option (c
, link
)
615 dhcp_failover_link_t
*link
;
617 u_int16_t option_code
;
618 u_int16_t option_len
;
624 if (link
-> imsg_count
+ 2 > link
-> imsg_len
) {
625 log_error ("FAILOVER: message overflow at option code.");
626 return DHCP_R_PROTOCOLERROR
;
629 /* Get option code. */
630 omapi_connection_get_uint16 (c
, &option_code
);
631 link
-> imsg_count
+= 2;
633 if (link
-> imsg_count
+ 2 > link
-> imsg_len
) {
634 log_error ("FAILOVER: message overflow at length.");
635 return DHCP_R_PROTOCOLERROR
;
638 /* Get option length. */
639 omapi_connection_get_uint16 (c
, &option_len
);
640 link
-> imsg_count
+= 2;
642 if (link
-> imsg_count
+ option_len
> link
-> imsg_len
) {
643 log_error ("FAILOVER: message overflow at data.");
644 return DHCP_R_PROTOCOLERROR
;
647 /* If it's an unknown code, skip over it. */
648 if ((option_code
> FTO_MAX
) ||
649 (ft_options
[option_code
].type
== FT_UNDEF
)) {
650 #if defined (DEBUG_FAILOVER_MESSAGES)
651 log_debug (" option code %d (%s) len %d (not recognized)",
653 dhcp_failover_option_name (option_code
),
656 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
657 link
-> imsg_count
+= option_len
;
658 return ISC_R_SUCCESS
;
661 /* If it's the digest, do it now. */
662 if (ft_options
[option_code
].type
== FT_DIGEST
) {
663 link
-> imsg_count
+= option_len
;
664 if (link
-> imsg_count
!= link
-> imsg_len
) {
665 log_error ("FAILOVER: digest not at end of message");
666 return DHCP_R_PROTOCOLERROR
;
668 #if defined (DEBUG_FAILOVER_MESSAGES)
669 log_debug (" option %s len %d",
670 ft_options
[option_code
].name
, option_len
);
672 /* For now, just dump it. */
673 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
674 return ISC_R_SUCCESS
;
677 /* Only accept an option once. */
678 if (link
-> imsg
-> options_present
& ft_options
[option_code
].bit
) {
679 log_error ("FAILOVER: duplicate option %s",
680 ft_options
[option_code
].name
);
681 return DHCP_R_PROTOCOLERROR
;
684 /* Make sure the option is appropriate for this type of message.
685 Really, any option is generally allowed for any message, and the
686 cases where this is not true are too complicated to represent in
687 this way - what this code is doing is to just avoid saving the
688 value of an option we don't have any way to use, which allows
689 us to make the failover_message structure smaller. */
690 if (ft_options
[option_code
].bit
&&
691 !(fto_allowed
[link
-> imsg
-> type
] &
692 ft_options
[option_code
].bit
)) {
693 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
694 link
-> imsg_count
+= option_len
;
695 return ISC_R_SUCCESS
;
698 /* Figure out how many elements, how big they are, and where
700 if (ft_options
[option_code
].num_present
) {
701 /* If this option takes a fixed number of elements,
702 we expect the space for them to be preallocated,
703 and we can just read the data in. */
705 op
= ((unsigned char *)link
-> imsg
) +
706 ft_options
[option_code
].offset
;
707 op_size
= ft_sizes
[ft_options
[option_code
].type
];
708 op_count
= ft_options
[option_code
].num_present
;
710 if (option_len
!= op_size
* op_count
) {
711 log_error ("FAILOVER: option size (%d:%d), option %s",
713 (ft_sizes
[ft_options
[option_code
].type
] *
714 ft_options
[option_code
].num_present
),
715 ft_options
[option_code
].name
);
716 return DHCP_R_PROTOCOLERROR
;
719 failover_option_t
*fo
;
721 /* FT_DDNS* are special - one or two bytes of status
722 followed by the client FQDN. */
723 if (ft_options
[option_code
].type
== FT_DDNS1
||
724 ft_options
[option_code
].type
== FT_DDNS1
) {
727 (((char *)link
-> imsg
) +
728 ft_options
[option_code
].offset
));
730 op_count
= (ft_options
[option_code
].type
== FT_DDNS1
733 omapi_connection_copyout (&ddns
-> codes
[0],
735 link
-> imsg_count
+= op_count
;
737 ddns
-> codes
[1] = 0;
739 op_count
= option_len
- op_count
;
741 ddns
-> length
= op_count
;
742 ddns
-> data
= dmalloc (op_count
, MDL
);
744 log_error ("FAILOVER: no memory getting%s(%d)",
745 " DNS data ", op_count
);
747 /* Actually, NO_MEMORY, but if we lose here
748 we have to drop the connection. */
749 return DHCP_R_PROTOCOLERROR
;
751 omapi_connection_copyout (ddns
-> data
, c
, op_count
);
755 /* A zero for num_present means that any number of
756 elements can appear, so we have to figure out how
757 many we got from the length of the option, and then
758 fill out a failover_option structure describing the
760 op_size
= ft_sizes
[ft_options
[option_code
].type
];
762 /* Make sure that option data length is a multiple of the
763 size of the data type being sent. */
764 if (op_size
> 1 && option_len
% op_size
) {
765 log_error ("FAILOVER: option_len %d not %s%d",
766 option_len
, "multiple of ", op_size
);
767 return DHCP_R_PROTOCOLERROR
;
770 op_count
= option_len
/ op_size
;
772 fo
= ((failover_option_t
*)
773 (((char *)link
-> imsg
) +
774 ft_options
[option_code
].offset
));
776 fo
-> count
= op_count
;
777 fo
-> data
= dmalloc (option_len
, MDL
);
779 log_error ("FAILOVER: no memory getting %s (%d)",
780 "option data", op_count
);
782 return DHCP_R_PROTOCOLERROR
;
787 /* For single-byte message values and multi-byte values that
788 don't need swapping, just read them in all at once. */
789 if (op_size
== 1 || ft_options
[option_code
].type
== FT_IPADDR
) {
790 omapi_connection_copyout ((unsigned char *)op
, c
, option_len
);
791 link
-> imsg_count
+= option_len
;
794 * As of 3.1.0, many option codes were changed to conform to
795 * draft revision 12 (which alphabetized, then renumbered all
796 * the option codes without preserving the version option code
797 * nor bumping its value). As it turns out, the message codes
798 * for CONNECT and CONNECTACK turn out the same, so it tries
799 * its darndest to connect, and falls short (when TLS_REQUEST
800 * comes up size 2 rather than size 1 as draft revision 12 also
803 * The VENDOR_CLASS code in 3.0.x was 11, which is now the HBA
804 * code. Both work out to be arbitrarily long text-or-byte
805 * strings, so they pass parsing.
807 * Note that it is possible (or intentional), if highly
808 * improbable, for the HBA bit array to exactly match
809 * isc-V3.0.x. Warning here is not an issue; if it really is
810 * 3.0.x, there will be a protocol error later on. If it isn't
811 * actually 3.0.x, then I guess the lucky user will have to
812 * live with a weird warning.
814 if ((option_code
== 11) && (option_len
> 9) &&
815 (strncmp((const char *)op
, "isc-V3.0.", 9) == 0)) {
816 log_error("WARNING: failover as of versions 3.1.0 and "
817 "on are not reverse compatible with "
824 /* For values that require swapping, read them in one at a time
825 using routines that swap bytes. */
826 for (i
= 0; i
< op_count
; i
++) {
827 switch (ft_options
[option_code
].type
) {
829 omapi_connection_get_uint32 (c
, (u_int32_t
*)op
);
831 link
-> imsg_count
+= 4;
835 omapi_connection_get_uint16 (c
, (u_int16_t
*)op
);
837 link
-> imsg_count
+= 2;
841 /* Everything else should have been handled
843 log_error ("FAILOVER: option %s: bad type %d",
844 ft_options
[option_code
].name
,
845 ft_options
[option_code
].type
);
846 return DHCP_R_PROTOCOLERROR
;
850 /* Remember that we got this option. */
851 link
-> imsg
-> options_present
|= ft_options
[option_code
].bit
;
852 return ISC_R_SUCCESS
;
855 isc_result_t
dhcp_failover_link_set_value (omapi_object_t
*h
,
857 omapi_data_string_t
*name
,
858 omapi_typed_data_t
*value
)
860 if (h
-> type
!= omapi_type_protocol
)
861 return DHCP_R_INVALIDARG
;
863 /* Never valid to set these. */
864 if (!omapi_ds_strcmp (name
, "link-port") ||
865 !omapi_ds_strcmp (name
, "link-name") ||
866 !omapi_ds_strcmp (name
, "link-state"))
869 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
870 return (*(h
-> inner
-> type
-> set_value
))
871 (h
-> inner
, id
, name
, value
);
872 return ISC_R_NOTFOUND
;
875 isc_result_t
dhcp_failover_link_get_value (omapi_object_t
*h
,
877 omapi_data_string_t
*name
,
878 omapi_value_t
**value
)
880 dhcp_failover_link_t
*link
;
882 if (h
-> type
!= omapi_type_protocol
)
883 return DHCP_R_INVALIDARG
;
884 link
= (dhcp_failover_link_t
*)h
;
886 if (!omapi_ds_strcmp (name
, "link-port")) {
887 return omapi_make_int_value (value
, name
,
888 (int)link
-> peer_port
, MDL
);
889 } else if (!omapi_ds_strcmp (name
, "link-state")) {
890 if (link
-> state
>= dhcp_flink_state_max
)
891 return omapi_make_string_value (value
, name
,
892 "invalid link state",
894 return omapi_make_string_value
896 dhcp_flink_state_names
[link
-> state
], MDL
);
899 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
900 return (*(h
-> inner
-> type
-> get_value
))
901 (h
-> inner
, id
, name
, value
);
902 return ISC_R_NOTFOUND
;
905 isc_result_t
dhcp_failover_link_destroy (omapi_object_t
*h
,
906 const char *file
, int line
)
908 dhcp_failover_link_t
*link
;
909 if (h
-> type
!= dhcp_type_failover_link
)
910 return DHCP_R_INVALIDARG
;
911 link
= (dhcp_failover_link_t
*)h
;
913 if (link
-> peer_address
)
914 option_cache_dereference (&link
-> peer_address
, file
, line
);
916 failover_message_dereference (&link
-> imsg
, file
, line
);
917 if (link
-> state_object
)
918 dhcp_failover_state_dereference (&link
-> state_object
,
920 return ISC_R_SUCCESS
;
923 /* Write all the published values associated with the object through the
924 specified connection. */
926 isc_result_t
dhcp_failover_link_stuff_values (omapi_object_t
*c
,
930 dhcp_failover_link_t
*link
;
933 if (l
-> type
!= dhcp_type_failover_link
)
934 return DHCP_R_INVALIDARG
;
935 link
= (dhcp_failover_link_t
*)l
;
937 status
= omapi_connection_put_name (c
, "link-port");
938 if (status
!= ISC_R_SUCCESS
)
940 status
= omapi_connection_put_uint32 (c
, sizeof (int));
941 if (status
!= ISC_R_SUCCESS
)
943 status
= omapi_connection_put_uint32 (c
, link
-> peer_port
);
944 if (status
!= ISC_R_SUCCESS
)
947 status
= omapi_connection_put_name (c
, "link-state");
948 if (status
!= ISC_R_SUCCESS
)
950 if (link
-> state
>= dhcp_flink_state_max
)
951 status
= omapi_connection_put_string (c
, "invalid link state");
953 status
= (omapi_connection_put_string
954 (c
, dhcp_flink_state_names
[link
-> state
]));
955 if (status
!= ISC_R_SUCCESS
)
958 if (link
-> inner
&& link
-> inner
-> type
-> stuff_values
)
959 return (*(link
-> inner
-> type
-> stuff_values
)) (c
, id
,
961 return ISC_R_SUCCESS
;
964 /* Set up a listener for the omapi protocol. The handle stored points to
965 a listener object, not a protocol object. */
967 isc_result_t
dhcp_failover_listen (omapi_object_t
*h
)
970 dhcp_failover_listener_t
*obj
, *l
;
971 omapi_value_t
*value
= (omapi_value_t
*)0;
972 omapi_addr_t local_addr
;
975 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
976 "local-port", &value
);
977 if (status
!= ISC_R_SUCCESS
)
979 if (!value
-> value
) {
980 omapi_value_dereference (&value
, MDL
);
981 return DHCP_R_INVALIDARG
;
984 status
= omapi_get_int_value (&port
, value
-> value
);
985 omapi_value_dereference (&value
, MDL
);
986 if (status
!= ISC_R_SUCCESS
)
988 local_addr
.port
= port
;
990 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
991 "local-address", &value
);
992 if (status
!= ISC_R_SUCCESS
)
994 if (!value
-> value
) {
996 omapi_value_dereference (&value
, MDL
);
997 return DHCP_R_INVALIDARG
;
1000 if (value
-> value
-> type
!= omapi_datatype_data
||
1001 value
-> value
-> u
.buffer
.len
!= sizeof (struct in_addr
))
1004 memcpy (local_addr
.address
, value
-> value
-> u
.buffer
.value
,
1005 value
-> value
-> u
.buffer
.len
);
1006 local_addr
.addrlen
= value
-> value
-> u
.buffer
.len
;
1007 local_addr
.addrtype
= AF_INET
;
1009 omapi_value_dereference (&value
, MDL
);
1011 /* Are we already listening on this port and address? */
1012 for (l
= failover_listeners
; l
; l
= l
-> next
) {
1013 if (l
-> address
.port
== local_addr
.port
&&
1014 l
-> address
.addrtype
== local_addr
.addrtype
&&
1015 l
-> address
.addrlen
== local_addr
.addrlen
&&
1016 !memcmp (l
-> address
.address
, local_addr
.address
,
1017 local_addr
.addrlen
))
1020 /* Already listening. */
1022 return ISC_R_SUCCESS
;
1024 obj
= (dhcp_failover_listener_t
*)0;
1025 status
= dhcp_failover_listener_allocate (&obj
, MDL
);
1026 if (status
!= ISC_R_SUCCESS
)
1028 obj
-> address
= local_addr
;
1030 status
= omapi_listen_addr ((omapi_object_t
*)obj
, &obj
-> address
, 1);
1031 if (status
!= ISC_R_SUCCESS
)
1034 status
= omapi_object_reference (&h
-> outer
,
1035 (omapi_object_t
*)obj
, MDL
);
1036 if (status
!= ISC_R_SUCCESS
) {
1037 dhcp_failover_listener_dereference (&obj
, MDL
);
1040 status
= omapi_object_reference (&obj
-> inner
, h
, MDL
);
1041 if (status
!= ISC_R_SUCCESS
) {
1042 dhcp_failover_listener_dereference (&obj
, MDL
);
1046 /* Put this listener on the list. */
1047 if (failover_listeners
) {
1048 dhcp_failover_listener_reference (&obj
-> next
,
1049 failover_listeners
, MDL
);
1050 dhcp_failover_listener_dereference (&failover_listeners
, MDL
);
1052 dhcp_failover_listener_reference (&failover_listeners
, obj
, MDL
);
1054 return dhcp_failover_listener_dereference (&obj
, MDL
);
1057 /* Signal handler for protocol listener - if we get a connect signal,
1058 create a new protocol connection, otherwise pass the signal down. */
1060 isc_result_t
dhcp_failover_listener_signal (omapi_object_t
*o
,
1061 const char *name
, va_list ap
)
1063 isc_result_t status
;
1064 omapi_connection_object_t
*c
;
1065 dhcp_failover_link_t
*obj
;
1066 dhcp_failover_listener_t
*p
;
1067 dhcp_failover_state_t
*s
, *state
= (dhcp_failover_state_t
*)0;
1069 if (!o
|| o
-> type
!= dhcp_type_failover_listener
)
1070 return DHCP_R_INVALIDARG
;
1071 p
= (dhcp_failover_listener_t
*)o
;
1073 /* Not a signal we recognize? */
1074 if (strcmp (name
, "connect")) {
1075 if (p
-> inner
&& p
-> inner
-> type
-> signal_handler
)
1076 return (*(p
-> inner
-> type
-> signal_handler
))
1077 (p
-> inner
, name
, ap
);
1078 return ISC_R_NOTFOUND
;
1081 c
= va_arg (ap
, omapi_connection_object_t
*);
1082 if (!c
|| c
-> type
!= omapi_type_connection
)
1083 return DHCP_R_INVALIDARG
;
1085 /* See if we can find a failover_state object that
1086 matches this connection. */
1087 for (s
= failover_states
; s
; s
= s
-> next
) {
1088 if (dhcp_failover_state_match
1089 (s
, (u_int8_t
*)&c
-> remote_addr
.sin_addr
,
1090 sizeof c
-> remote_addr
.sin_addr
)) {
1096 log_info ("failover: listener: no matching state");
1097 omapi_disconnect ((omapi_object_t
*)c
, 1);
1098 return(ISC_R_NOTFOUND
);
1101 obj
= (dhcp_failover_link_t
*)0;
1102 status
= dhcp_failover_link_allocate (&obj
, MDL
);
1103 if (status
!= ISC_R_SUCCESS
)
1105 obj
-> peer_port
= ntohs (c
-> remote_addr
.sin_port
);
1107 status
= omapi_object_reference (&obj
-> outer
,
1108 (omapi_object_t
*)c
, MDL
);
1109 if (status
!= ISC_R_SUCCESS
) {
1111 dhcp_failover_link_dereference (&obj
, MDL
);
1112 log_info ("failover: listener: picayune failure.");
1113 omapi_disconnect ((omapi_object_t
*)c
, 1);
1117 status
= omapi_object_reference (&c
-> inner
,
1118 (omapi_object_t
*)obj
, MDL
);
1119 if (status
!= ISC_R_SUCCESS
)
1122 status
= dhcp_failover_state_reference (&obj
-> state_object
,
1124 if (status
!= ISC_R_SUCCESS
)
1127 omapi_signal_in ((omapi_object_t
*)obj
, "connect");
1129 return dhcp_failover_link_dereference (&obj
, MDL
);
1132 isc_result_t
dhcp_failover_listener_set_value (omapi_object_t
*h
,
1134 omapi_data_string_t
*name
,
1135 omapi_typed_data_t
*value
)
1137 if (h
-> type
!= dhcp_type_failover_listener
)
1138 return DHCP_R_INVALIDARG
;
1140 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
1141 return (*(h
-> inner
-> type
-> set_value
))
1142 (h
-> inner
, id
, name
, value
);
1143 return ISC_R_NOTFOUND
;
1146 isc_result_t
dhcp_failover_listener_get_value (omapi_object_t
*h
,
1148 omapi_data_string_t
*name
,
1149 omapi_value_t
**value
)
1151 if (h
-> type
!= dhcp_type_failover_listener
)
1152 return DHCP_R_INVALIDARG
;
1154 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
1155 return (*(h
-> inner
-> type
-> get_value
))
1156 (h
-> inner
, id
, name
, value
);
1157 return ISC_R_NOTFOUND
;
1160 isc_result_t
dhcp_failover_listener_destroy (omapi_object_t
*h
,
1161 const char *file
, int line
)
1163 dhcp_failover_listener_t
*l
;
1165 if (h
-> type
!= dhcp_type_failover_listener
)
1166 return DHCP_R_INVALIDARG
;
1167 l
= (dhcp_failover_listener_t
*)h
;
1169 dhcp_failover_listener_dereference (&l
-> next
, file
, line
);
1171 return ISC_R_SUCCESS
;
1174 /* Write all the published values associated with the object through the
1175 specified connection. */
1177 isc_result_t
dhcp_failover_listener_stuff (omapi_object_t
*c
,
1181 if (p
-> type
!= dhcp_type_failover_listener
)
1182 return DHCP_R_INVALIDARG
;
1184 if (p
-> inner
&& p
-> inner
-> type
-> stuff_values
)
1185 return (*(p
-> inner
-> type
-> stuff_values
)) (c
, id
,
1187 return ISC_R_SUCCESS
;
1190 /* Set up master state machine for the failover protocol. */
1192 isc_result_t
dhcp_failover_register (omapi_object_t
*h
)
1194 isc_result_t status
;
1195 dhcp_failover_state_t
*obj
;
1197 omapi_value_t
*value
= (omapi_value_t
*)0;
1199 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
1200 "local-port", &value
);
1201 if (status
!= ISC_R_SUCCESS
)
1203 if (!value
-> value
) {
1204 omapi_value_dereference (&value
, MDL
);
1205 return DHCP_R_INVALIDARG
;
1208 status
= omapi_get_int_value (&port
, value
-> value
);
1209 omapi_value_dereference (&value
, MDL
);
1210 if (status
!= ISC_R_SUCCESS
)
1213 obj
= (dhcp_failover_state_t
*)0;
1214 dhcp_failover_state_allocate (&obj
, MDL
);
1215 obj
-> me
.port
= port
;
1217 status
= omapi_listen ((omapi_object_t
*)obj
, port
, 1);
1218 if (status
!= ISC_R_SUCCESS
) {
1219 dhcp_failover_state_dereference (&obj
, MDL
);
1223 status
= omapi_object_reference (&h
-> outer
, (omapi_object_t
*)obj
,
1225 if (status
!= ISC_R_SUCCESS
) {
1226 dhcp_failover_state_dereference (&obj
, MDL
);
1229 status
= omapi_object_reference (&obj
-> inner
, h
, MDL
);
1230 dhcp_failover_state_dereference (&obj
, MDL
);
1234 /* Signal handler for protocol state machine. */
1236 isc_result_t
dhcp_failover_state_signal (omapi_object_t
*o
,
1237 const char *name
, va_list ap
)
1239 isc_result_t status
;
1240 dhcp_failover_state_t
*state
;
1241 dhcp_failover_link_t
*link
;
1244 if (!o
|| o
-> type
!= dhcp_type_failover_state
)
1245 return DHCP_R_INVALIDARG
;
1246 state
= (dhcp_failover_state_t
*)o
;
1248 /* Not a signal we recognize? */
1249 if (strcmp (name
, "disconnect") &&
1250 strcmp (name
, "message")) {
1251 if (state
-> inner
&& state
-> inner
-> type
-> signal_handler
)
1252 return (*(state
-> inner
-> type
-> signal_handler
))
1253 (state
-> inner
, name
, ap
);
1254 return ISC_R_NOTFOUND
;
1257 /* Handle connect signals by seeing what state we're in
1258 and potentially doing a state transition. */
1259 if (!strcmp (name
, "disconnect")) {
1260 link
= va_arg (ap
, dhcp_failover_link_t
*);
1262 dhcp_failover_link_dereference (&state
-> link_to_peer
, MDL
);
1263 dhcp_failover_state_transition (state
, "disconnect");
1264 if (state
-> i_am
== primary
) {
1265 #if defined (DEBUG_FAILOVER_TIMING)
1266 log_info ("add_timeout +90 %s",
1267 "dhcp_failover_reconnect");
1269 tv
. tv_sec
= cur_time
+ 90;
1271 add_timeout (&tv
, dhcp_failover_reconnect
,
1273 (tvref_t
)dhcp_failover_state_reference
,
1275 dhcp_failover_state_dereference
);
1277 } else if (!strcmp (name
, "message")) {
1278 link
= va_arg (ap
, dhcp_failover_link_t
*);
1280 if (link
-> imsg
-> type
== FTM_CONNECT
) {
1281 /* If we already have a link to the peer, it must be
1283 XXX Is this the right thing to do?
1284 XXX Probably not - what if both peers start at
1285 XXX the same time? */
1286 if (state
-> link_to_peer
) {
1287 dhcp_failover_send_connectack
1288 ((omapi_object_t
*)link
, state
,
1290 "already connected");
1291 omapi_disconnect (link
-> outer
, 1);
1292 return ISC_R_SUCCESS
;
1294 if (!(link
-> imsg
-> options_present
& FTB_MCLT
)) {
1295 dhcp_failover_send_connectack
1296 ((omapi_object_t
*)link
, state
,
1298 "no MCLT provided");
1299 omapi_disconnect (link
-> outer
, 1);
1300 return ISC_R_SUCCESS
;
1303 dhcp_failover_link_reference (&state
-> link_to_peer
,
1305 status
= (dhcp_failover_send_connectack
1306 ((omapi_object_t
*)link
, state
, 0, 0));
1307 if (status
!= ISC_R_SUCCESS
) {
1308 dhcp_failover_link_dereference
1309 (&state
-> link_to_peer
, MDL
);
1310 log_info ("dhcp_failover_send_connectack: %s",
1311 isc_result_totext (status
));
1312 omapi_disconnect (link
-> outer
, 1);
1313 return ISC_R_SUCCESS
;
1315 if (link
-> imsg
-> options_present
& FTB_MAX_UNACKED
)
1316 state
-> partner
.max_flying_updates
=
1317 link
-> imsg
-> max_unacked
;
1318 if (link
-> imsg
-> options_present
& FTB_RECEIVE_TIMER
)
1319 state
-> partner
.max_response_delay
=
1320 link
-> imsg
-> receive_timer
;
1321 state
-> mclt
= link
-> imsg
-> mclt
;
1322 dhcp_failover_send_state (state
);
1323 cancel_timeout (dhcp_failover_link_startup_timeout
,
1325 } else if (link
-> imsg
-> type
== FTM_CONNECTACK
) {
1330 cancel_timeout (dhcp_failover_link_startup_timeout
,
1333 if (!(link
->imsg
->options_present
&
1334 FTB_RELATIONSHIP_NAME
)) {
1335 errmsg
= "missing relationship-name";
1336 reason
= FTR_INVALID_PARTNER
;
1340 if (link
->imsg
->options_present
& FTB_REJECT_REASON
) {
1341 /* XXX: add message option to text output. */
1342 log_error ("Failover CONNECT to %s rejected: %s",
1343 state
? state
->name
: "unknown",
1344 (dhcp_failover_reject_reason_print
1345 (link
-> imsg
-> reject_reason
)));
1346 /* XXX print message from peer if peer sent message. */
1347 omapi_disconnect (link
-> outer
, 1);
1348 return ISC_R_SUCCESS
;
1351 if (!dhcp_failover_state_match_by_name(state
,
1352 &link
->imsg
->relationship_name
)) {
1353 /* XXX: Overflow results in log truncation, safe. */
1354 snprintf(errbuf
, sizeof(errbuf
), "remote failover "
1355 "relationship name %.*s does not match",
1356 (int)link
->imsg
->relationship_name
.count
,
1357 link
->imsg
->relationship_name
.data
);
1359 reason
= FTR_INVALID_PARTNER
;
1361 log_error("Failover CONNECTACK from %s: %s",
1362 state
->name
, errmsg
);
1363 dhcp_failover_send_disconnect ((omapi_object_t
*)link
,
1365 omapi_disconnect (link
-> outer
, 0);
1366 return ISC_R_SUCCESS
;
1369 if (state
-> link_to_peer
) {
1370 errmsg
= "already connected";
1371 reason
= FTR_DUP_CONNECTION
;
1375 if ((cur_time
> link
-> imsg
-> time
&&
1376 cur_time
- link
-> imsg
-> time
> 60) ||
1377 (cur_time
< link
-> imsg
-> time
&&
1378 link
-> imsg
-> time
- cur_time
> 60)) {
1379 errmsg
= "time offset too large";
1380 reason
= FTR_TIMEMISMATCH
;
1384 dhcp_failover_link_reference (&state
-> link_to_peer
,
1387 /* XXX This is probably the right thing to do, but
1388 XXX for release three, to make the smallest possible
1389 XXX change, we are doing this when the peer state
1390 XXX changes instead. */
1391 if (state
-> me
.state
== startup
)
1392 dhcp_failover_set_state (state
,
1393 state
-> saved_state
);
1396 dhcp_failover_send_state (state
);
1398 if (link
-> imsg
-> options_present
& FTB_MAX_UNACKED
)
1399 state
-> partner
.max_flying_updates
=
1400 link
-> imsg
-> max_unacked
;
1401 if (link
-> imsg
-> options_present
& FTB_RECEIVE_TIMER
)
1402 state
-> partner
.max_response_delay
=
1403 link
-> imsg
-> receive_timer
;
1404 #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
1405 log_info ("add_timeout +%d %s",
1406 (int)state
-> partner
.max_response_delay
/ 3,
1407 "dhcp_failover_send_contact");
1409 tv
. tv_sec
= cur_time
+
1410 (int)state
-> partner
.max_response_delay
/ 3;
1413 dhcp_failover_send_contact
, state
,
1414 (tvref_t
)dhcp_failover_state_reference
,
1415 (tvunref_t
)dhcp_failover_state_dereference
);
1416 #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
1417 log_info ("add_timeout +%d %s",
1418 (int)state
-> me
.max_response_delay
,
1419 "dhcp_failover_timeout");
1421 tv
. tv_sec
= cur_time
+
1422 (int)state
-> me
.max_response_delay
;
1425 dhcp_failover_timeout
, state
,
1426 (tvref_t
)dhcp_failover_state_reference
,
1427 (tvunref_t
)dhcp_failover_state_dereference
);
1428 } else if (link
-> imsg
-> type
== FTM_DISCONNECT
) {
1429 if (link
-> imsg
-> reject_reason
) {
1430 log_error ("Failover DISCONNECT from %s: %s",
1431 state
? state
->name
: "unknown",
1432 (dhcp_failover_reject_reason_print
1433 (link
-> imsg
-> reject_reason
)));
1435 omapi_disconnect (link
-> outer
, 1);
1436 } else if (link
-> imsg
-> type
== FTM_BNDUPD
) {
1437 dhcp_failover_process_bind_update (state
,
1439 } else if (link
-> imsg
-> type
== FTM_BNDACK
) {
1440 dhcp_failover_process_bind_ack (state
, link
-> imsg
);
1441 } else if (link
-> imsg
-> type
== FTM_UPDREQ
) {
1442 dhcp_failover_process_update_request (state
,
1444 } else if (link
-> imsg
-> type
== FTM_UPDREQALL
) {
1445 dhcp_failover_process_update_request_all
1446 (state
, link
-> imsg
);
1447 } else if (link
-> imsg
-> type
== FTM_UPDDONE
) {
1448 dhcp_failover_process_update_done (state
,
1450 } else if (link
-> imsg
-> type
== FTM_POOLREQ
) {
1451 dhcp_failover_pool_reqbalance(state
);
1452 } else if (link
-> imsg
-> type
== FTM_POOLRESP
) {
1453 log_info ("pool response: %ld leases",
1455 link
-> imsg
-> addresses_transferred
);
1456 } else if (link
-> imsg
-> type
== FTM_STATE
) {
1457 dhcp_failover_peer_state_changed (state
,
1461 /* Add a timeout so that if the partner doesn't send
1462 another message for the maximum transmit idle time
1463 plus a grace of one second, we close the
1465 if (state
-> link_to_peer
&&
1466 state
-> link_to_peer
== link
&&
1467 state
-> link_to_peer
-> state
!= dhcp_flink_disconnected
)
1469 #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
1470 log_info ("add_timeout +%d %s",
1471 (int)state
-> me
.max_response_delay
,
1472 "dhcp_failover_timeout");
1474 tv
. tv_sec
= cur_time
+
1475 (int)state
-> me
.max_response_delay
;
1478 dhcp_failover_timeout
, state
,
1479 (tvref_t
)dhcp_failover_state_reference
,
1480 (tvunref_t
)dhcp_failover_state_dereference
);
1485 /* Handle all the events we care about... */
1486 return ISC_R_SUCCESS
;
1489 isc_result_t
dhcp_failover_state_transition (dhcp_failover_state_t
*state
,
1492 isc_result_t status
;
1494 /* XXX Check these state transitions against the spec! */
1495 if (!strcmp (name
, "disconnect")) {
1496 if (state
-> link_to_peer
) {
1497 log_info ("peer %s: disconnected", state
-> name
);
1498 if (state
-> link_to_peer
-> state_object
)
1499 dhcp_failover_state_dereference
1500 (&state
-> link_to_peer
-> state_object
, MDL
);
1501 dhcp_failover_link_dereference (&state
-> link_to_peer
,
1504 cancel_timeout (dhcp_failover_send_contact
, state
);
1505 cancel_timeout (dhcp_failover_timeout
, state
);
1506 cancel_timeout (dhcp_failover_startup_timeout
, state
);
1508 switch (state
-> me
.state
== startup
?
1509 state
-> saved_state
: state
-> me
.state
) {
1510 /* In these situations, we remain in the current
1511 * state, or if in startup enter those states.
1513 case communications_interrupted
:
1520 case resolution_interrupted
:
1522 /* Already in the right state? */
1523 if (state
-> me
.state
== startup
)
1524 return (dhcp_failover_set_state
1525 (state
, state
-> saved_state
));
1526 return ISC_R_SUCCESS
;
1528 case potential_conflict
:
1529 return dhcp_failover_set_state
1530 (state
, resolution_interrupted
);
1533 return dhcp_failover_set_state
1534 (state
, communications_interrupted
);
1537 return dhcp_failover_set_state
1538 (state
, resolution_interrupted
);
1541 log_fatal("Impossible case at %s:%d.", MDL
);
1542 break; /* can't happen. */
1544 } else if (!strcmp (name
, "connect")) {
1545 switch (state
-> me
.state
) {
1546 case communications_interrupted
:
1547 status
= dhcp_failover_set_state (state
, normal
);
1548 dhcp_failover_send_updates (state
);
1551 case resolution_interrupted
:
1552 return dhcp_failover_set_state (state
,
1553 potential_conflict
);
1557 case potential_conflict
:
1566 return dhcp_failover_send_state (state
);
1569 log_fatal("Impossible case at %s:%d.", MDL
);
1572 } else if (!strcmp (name
, "startup")) {
1573 dhcp_failover_set_state (state
, startup
);
1574 return ISC_R_SUCCESS
;
1575 } else if (!strcmp (name
, "connect-timeout")) {
1576 switch (state
-> me
.state
) {
1577 case communications_interrupted
:
1579 case resolution_interrupted
:
1584 return ISC_R_SUCCESS
;
1591 return dhcp_failover_set_state
1592 (state
, communications_interrupted
);
1594 case potential_conflict
:
1595 return dhcp_failover_set_state
1596 (state
, resolution_interrupted
);
1599 log_fatal("Impossible case at %s:%d.", MDL
);
1603 return DHCP_R_INVALIDARG
;
1606 isc_result_t
dhcp_failover_set_service_state (dhcp_failover_state_t
*state
)
1608 switch (state
-> me
.state
) {
1610 state
-> service_state
= not_responding
;
1611 state
-> nrr
= " (my state unknown)";
1615 state
-> service_state
= service_partner_down
;
1620 state
-> service_state
= cooperating
;
1624 case communications_interrupted
:
1625 state
-> service_state
= not_cooperating
;
1629 case resolution_interrupted
:
1630 case potential_conflict
:
1632 state
-> service_state
= not_responding
;
1633 state
-> nrr
= " (resolving conflicts)";
1637 state
-> service_state
= not_responding
;
1638 state
-> nrr
= " (recovering)";
1642 state
-> service_state
= not_responding
;
1643 state
-> nrr
= " (shut down)";
1647 state
-> service_state
= not_responding
;
1648 state
-> nrr
= " (paused)";
1652 state
-> service_state
= not_responding
;
1653 state
-> nrr
= " (recover wait)";
1657 state
-> service_state
= not_responding
;
1658 state
-> nrr
= " (recover done)";
1662 state
-> service_state
= service_startup
;
1663 state
-> nrr
= " (startup)";
1667 log_fatal("Impossible case at %s:%d.\n", MDL
);
1671 /* Some peer states can require us not to respond, even if our
1673 /* XXX hm. I suspect this isn't true anymore. */
1674 if (state
-> service_state
!= not_responding
) {
1675 switch (state
-> partner
.state
) {
1677 state
-> service_state
= not_responding
;
1678 state
-> nrr
= " (peer demands: recovering)";
1681 case potential_conflict
:
1683 case resolution_interrupted
:
1684 state
-> service_state
= not_responding
;
1685 state
-> nrr
= " (peer demands: resolving conflicts)";
1688 /* Other peer states don't affect our behaviour. */
1694 return ISC_R_SUCCESS
;
1697 isc_result_t
dhcp_failover_set_state (dhcp_failover_state_t
*state
,
1698 enum failover_state new_state
)
1700 enum failover_state saved_state
;
1703 struct shared_network
*s
;
1707 /* If we're in certain states where we're sending updates, and the peer
1708 * state changes, we need to re-schedule any pending updates just to
1709 * be on the safe side. This results in retransmission.
1711 switch (state
-> me
.state
) {
1713 case potential_conflict
:
1715 if (state
-> ack_queue_tail
) {
1718 /* Zap the flags. */
1719 for (lp
= state
-> ack_queue_head
; lp
; lp
= lp
-> next_pending
)
1720 lp
-> flags
= ((lp
-> flags
& ~ON_ACK_QUEUE
) |
1723 /* Now hook the ack queue to the beginning of the update
1725 if (state
-> update_queue_head
) {
1726 lease_reference (&state
-> ack_queue_tail
-> next_pending
,
1727 state
-> update_queue_head
, MDL
);
1728 lease_dereference (&state
-> update_queue_head
, MDL
);
1730 lease_reference (&state
-> update_queue_head
,
1731 state
-> ack_queue_head
, MDL
);
1732 if (!state
-> update_queue_tail
) {
1733 #if defined (POINTER_DEBUG)
1734 if (state
-> ack_queue_tail
-> next_pending
) {
1735 log_error ("next pending on ack queue tail.");
1739 lease_reference (&state
-> update_queue_tail
,
1740 state
-> ack_queue_tail
, MDL
);
1742 lease_dereference (&state
-> ack_queue_tail
, MDL
);
1743 lease_dereference (&state
-> ack_queue_head
, MDL
);
1744 state
-> cur_unacked_updates
= 0;
1746 /* We will re-queue a timeout later, if applicable. */
1747 cancel_timeout (dhcp_failover_keepalive
, state
);
1754 /* Tentatively make the transition. */
1755 saved_state
= state
-> me
.state
;
1756 saved_stos
= state
-> me
.stos
;
1758 /* Keep the old stos if we're going into recover_wait or if we're
1759 coming into or out of startup. */
1760 if (new_state
!= recover_wait
&& new_state
!= startup
&&
1761 saved_state
!= startup
)
1762 state
-> me
.stos
= cur_time
;
1764 /* If we're in shutdown, peer is in partner_down, and we're moving
1765 to recover, we can skip waiting for MCLT to expire. This happens
1766 when a server is moved administratively into shutdown prior to
1767 actually shutting down. Of course, if there are any updates
1768 pending we can't actually do this. */
1769 if (new_state
== recover
&& saved_state
== shut_down
&&
1770 state
-> partner
.state
== partner_down
&&
1771 !state
-> update_queue_head
&& !state
-> ack_queue_head
)
1772 state
-> me
.stos
= cur_time
- state
-> mclt
;
1774 state
-> me
.state
= new_state
;
1775 if (new_state
== startup
&& saved_state
!= startup
)
1776 state
-> saved_state
= saved_state
;
1778 /* If we can't record the new state, we can't make a state transition. */
1779 if (!write_failover_state (state
) || !commit_leases ()) {
1780 log_error ("Unable to record current failover state for %s",
1782 state
-> me
.state
= saved_state
;
1783 state
-> me
.stos
= saved_stos
;
1784 return ISC_R_IOERROR
;
1787 log_info ("failover peer %s: I move from %s to %s",
1788 state
-> name
, dhcp_failover_state_name_print (saved_state
),
1789 dhcp_failover_state_name_print (state
-> me
.state
));
1791 /* If both servers are now normal log it */
1792 if ((state
->me
.state
== normal
) && (state
->partner
.state
== normal
))
1793 log_info("failover peer %s: Both servers normal", state
->name
);
1795 /* If we were in startup and we just left it, cancel the timeout. */
1796 if (new_state
!= startup
&& saved_state
== startup
)
1797 cancel_timeout (dhcp_failover_startup_timeout
, state
);
1800 * If the state changes for any reason, cancel 'delayed auto state
1801 * changes' (currently there is just the one).
1803 cancel_timeout(dhcp_failover_auto_partner_down
, state
);
1805 /* Set our service state. */
1806 dhcp_failover_set_service_state (state
);
1808 /* Tell the peer about it. */
1809 if (state
-> link_to_peer
)
1810 dhcp_failover_send_state (state
);
1812 switch (new_state
) {
1813 case communications_interrupted
:
1815 * There is an optional feature to automatically enter partner
1816 * down after a timer expires, upon entering comms-interrupted.
1817 * This feature is generally not safe except in specific
1820 * A zero value (also the default) disables it.
1822 if (state
->auto_partner_down
== 0)
1825 #if defined (DEBUG_FAILOVER_TIMING)
1826 log_info("add_timeout +%lu dhcp_failover_auto_partner_down",
1827 (unsigned long)state
->auto_partner_down
);
1829 tv
.tv_sec
= cur_time
+ state
->auto_partner_down
;
1831 add_timeout(&tv
, dhcp_failover_auto_partner_down
, state
,
1832 (tvref_t
)omapi_object_reference
,
1833 (tvunref_t
)omapi_object_dereference
);
1837 /* Upon entering normal state, the server is expected to retransmit
1838 * all pending binding updates. This is a good opportunity to
1839 * rebalance the pool (potentially making new pending updates),
1840 * which also schedules the next pool rebalance.
1842 dhcp_failover_pool_balance(state
);
1843 dhcp_failover_generate_update_queue(state
, 0);
1845 if (state
->update_queue_tail
!= NULL
) {
1846 dhcp_failover_send_updates(state
);
1847 log_info("Sending updates to %s.", state
->name
);
1852 case potential_conflict
:
1853 if (state
-> i_am
== primary
)
1854 dhcp_failover_send_update_request (state
);
1858 #if defined (DEBUG_FAILOVER_TIMING)
1859 log_info ("add_timeout +15 %s",
1860 "dhcp_failover_startup_timeout");
1862 tv
. tv_sec
= cur_time
+ 15;
1865 dhcp_failover_startup_timeout
,
1867 (tvref_t
)omapi_object_reference
,
1869 omapi_object_dereference
);
1872 /* If we come back in recover_wait and there's still waiting
1873 to do, set a timeout. */
1875 if (state
-> me
.stos
+ state
-> mclt
> cur_time
) {
1876 #if defined (DEBUG_FAILOVER_TIMING)
1877 log_info ("add_timeout +%d %s",
1879 state
-> me
.stos
+ state
-> mclt
),
1880 "dhcp_failover_startup_timeout");
1882 tv
. tv_sec
= (int)(state
-> me
.stos
+ state
-> mclt
);
1885 dhcp_failover_recover_done
,
1887 (tvref_t
)omapi_object_reference
,
1889 omapi_object_dereference
);
1891 dhcp_failover_recover_done (state
);
1895 /* XXX: We're supposed to calculate if updreq or updreqall is
1896 * needed. In theory, we should only have to updreqall if we
1897 * are positive we lost our stable storage.
1899 if (state
-> link_to_peer
)
1900 dhcp_failover_send_update_request_all (state
);
1904 /* For every expired lease, set a timeout for it to become free. */
1905 for (s
= shared_networks
; s
; s
= s
-> next
) {
1906 for (p
= s
-> pools
; p
; p
= p
-> next
) {
1907 if (p
-> failover_peer
== state
) {
1908 for (l
= p
->expired
; l
; l
= l
->next
) {
1909 l
->tsfp
= state
->me
.stos
+ state
->mclt
;
1910 l
->sort_time
= (l
->tsfp
> l
->ends
) ?
1914 (p
->expired
->sort_time
< p
->next_event_time
)) {
1916 p
->next_event_time
= p
->expired
->sort_time
;
1917 #if defined (DEBUG_FAILOVER_TIMING)
1918 log_info ("add_timeout +%d %s",
1919 (int)(cur_time
- p
->next_event_time
),
1922 tv
.tv_sec
= p
->next_event_time
;
1924 add_timeout(&tv
, pool_timer
, p
,
1925 (tvref_t
)pool_reference
,
1926 (tvunref_t
)pool_dereference
);
1938 return ISC_R_SUCCESS
;
1941 isc_result_t
dhcp_failover_peer_state_changed (dhcp_failover_state_t
*state
,
1942 failover_message_t
*msg
)
1944 enum failover_state previous_state
= state
-> partner
.state
;
1945 enum failover_state new_state
;
1948 new_state
= msg
-> server_state
;
1949 startupp
= (msg
-> server_flags
& FTF_SERVER_STARTUP
) ? 1 : 0;
1951 if (state
-> partner
.state
== new_state
&& state
-> me
.state
) {
1952 switch (state
-> me
.state
) {
1954 dhcp_failover_set_state (state
, state
-> saved_state
);
1955 return ISC_R_SUCCESS
;
1959 case potential_conflict
:
1964 return ISC_R_SUCCESS
;
1966 /* If we get a peer state change when we're
1967 disconnected, we always process it. */
1969 case communications_interrupted
:
1970 case resolution_interrupted
:
1976 log_fatal("Impossible case at %s:%d.", MDL
);
1981 state
-> partner
.state
= new_state
;
1983 log_info ("failover peer %s: peer moves from %s to %s",
1985 dhcp_failover_state_name_print (previous_state
),
1986 dhcp_failover_state_name_print (state
-> partner
.state
));
1988 /* If both servers are now normal log it */
1989 if ((state
->me
.state
== normal
) && (state
->partner
.state
== normal
))
1990 log_info("failover peer %s: Both servers normal", state
->name
);
1992 if (!write_failover_state (state
) || !commit_leases ()) {
1993 /* This is bad, but it's not fatal. Of course, if we
1994 can't write to the lease database, we're not going to
1995 get much done anyway. */
1996 log_error ("Unable to record current failover state for %s",
2000 /* Quickly validate the new state as being one of the 13 known
2003 switch (new_state
) {
2007 case communications_interrupted
:
2009 case potential_conflict
:
2014 case resolution_interrupted
:
2020 log_error("failover peer %s: Invalid state: %d", state
->name
,
2022 dhcp_failover_set_state(state
, shut_down
);
2023 return ISC_R_SUCCESS
;
2026 /* Do any state transitions that are required as a result of the
2027 peer's state transition. */
2029 switch (state
-> me
.state
== startup
?
2030 state
-> saved_state
: state
-> me
.state
) {
2032 switch (new_state
) {
2034 dhcp_failover_state_pool_check (state
);
2038 if (state
-> me
.state
== startup
)
2039 dhcp_failover_set_state (state
, recover
);
2041 dhcp_failover_set_state (state
,
2042 potential_conflict
);
2045 case potential_conflict
:
2046 case resolution_interrupted
:
2048 /* None of these transitions should ever occur. */
2049 log_error("Peer %s: Invalid state transition %s "
2050 "to %s.", state
->name
,
2051 dhcp_failover_state_name_print(previous_state
),
2052 dhcp_failover_state_name_print(new_state
));
2053 dhcp_failover_set_state (state
, shut_down
);
2058 dhcp_failover_set_state (state
, partner_down
);
2062 dhcp_failover_set_state (state
,
2063 communications_interrupted
);
2067 /* recover_wait, recover_done, unknown_state, startup,
2068 * communications_interrupted
2075 switch (new_state
) {
2077 log_info ("failover peer %s: requesting %s",
2078 state
-> name
, "full update from peer");
2079 /* Don't send updreqall if we're really in the
2080 startup state, because that will result in two
2082 if (state
-> me
.state
== recover
)
2083 dhcp_failover_send_update_request_all (state
);
2086 case potential_conflict
:
2087 case resolution_interrupted
:
2090 dhcp_failover_set_state (state
, potential_conflict
);
2094 case communications_interrupted
:
2095 /* We're supposed to send an update request at this
2097 /* XXX we don't currently have code here to do any
2098 XXX clever detection of when we should send an
2099 XXX UPDREQALL message rather than an UPDREQ
2100 XXX message. What to do, what to do? */
2101 /* Currently when we enter recover state, no matter
2102 * the reason, we send an UPDREQALL. So, it makes
2103 * the most sense to stick to that until something
2105 * Furthermore, we only want to send the update
2106 * request if we are not in startup state.
2108 if (state
-> me
.state
== recover
)
2109 dhcp_failover_send_update_request_all (state
);
2113 /* XXX We're not explicitly told what to do in this
2114 XXX case, but this transition is consistent with
2115 XXX what is elsewhere in the draft. */
2116 dhcp_failover_set_state (state
, partner_down
);
2119 /* We can't really do anything in this case. */
2121 /* paused, recover_done, recover_wait, unknown_state,
2128 case potential_conflict
:
2129 switch (new_state
) {
2131 /* This is an illegal transition. */
2132 log_error("Peer %s moves to normal during conflict "
2133 "resolution - panic, shutting down.",
2135 dhcp_failover_set_state(state
, shut_down
);
2139 if (previous_state
== potential_conflict
)
2140 dhcp_failover_send_update_request (state
);
2142 log_error("Peer %s: Unexpected move to "
2143 "conflict-done.", state
->name
);
2148 case potential_conflict
:
2150 case communications_interrupted
:
2151 case resolution_interrupted
:
2156 dhcp_failover_set_state (state
, recover
);
2160 dhcp_failover_set_state (state
, partner_down
);
2164 /* unknown_state, startup */
2170 switch (new_state
) {
2173 dhcp_failover_set_state(state
, new_state
);
2177 log_fatal("Peer %s: Invalid attempt to move from %s "
2178 "to %s while local state is conflict-done.",
2180 dhcp_failover_state_name_print(previous_state
),
2181 dhcp_failover_state_name_print(new_state
));
2186 /* Take no action if other server is starting up. */
2190 switch (new_state
) {
2191 /* This is where we should be. */
2197 dhcp_failover_set_state (state
, normal
);
2201 case potential_conflict
:
2203 case communications_interrupted
:
2204 case resolution_interrupted
:
2206 dhcp_failover_set_state (state
, potential_conflict
);
2210 /* shut_down, paused, unknown_state, startup */
2215 case communications_interrupted
:
2216 switch (new_state
) {
2218 /* Stick with the status quo. */
2221 /* If we're in communications-interrupted and an
2222 amnesic peer connects, go to the partner_down
2223 state immediately. */
2225 dhcp_failover_set_state (state
, partner_down
);
2229 case communications_interrupted
:
2232 /* XXX so we don't need to do this specially in
2233 XXX the CONNECT and CONNECTACK handlers. */
2234 dhcp_failover_send_updates (state
);
2235 dhcp_failover_set_state (state
, normal
);
2238 case potential_conflict
:
2240 case resolution_interrupted
:
2242 dhcp_failover_set_state (state
, potential_conflict
);
2246 dhcp_failover_set_state (state
, partner_down
);
2250 /* unknown_state, startup */
2255 case resolution_interrupted
:
2256 switch (new_state
) {
2259 case potential_conflict
:
2261 case communications_interrupted
:
2262 case resolution_interrupted
:
2266 dhcp_failover_set_state (state
, potential_conflict
);
2270 dhcp_failover_set_state (state
, partner_down
);
2274 /* paused, unknown_state, startup */
2279 /* Make no transitions while in recover_wait...just wait. */
2284 switch (new_state
) {
2286 log_error("Both servers have entered recover-done!");
2288 dhcp_failover_set_state (state
, normal
);
2292 dhcp_failover_set_state (state
, partner_down
);
2296 /* potential_conflict, partner_down,
2297 * communications_interrupted, resolution_interrupted,
2298 * paused, recover, recover_wait, unknown_state,
2305 /* We are essentially dead in the water when we're in
2306 either shut_down or paused states, and do not do any
2307 automatic state transitions. */
2312 /* XXX: Shouldn't this be a fatal condition? */
2317 log_fatal("Impossible condition at %s:%d.", MDL
);
2322 /* If we didn't make a transition out of startup as a result of
2323 the peer's state change, do it now as a result of the fact that
2324 we got a state change from the peer. */
2325 if (state
-> me
.state
== startup
&& state
-> saved_state
!= startup
)
2326 dhcp_failover_set_state (state
, state
-> saved_state
);
2328 /* For now, just set the service state based on the peer's state
2330 dhcp_failover_set_service_state (state
);
2332 return ISC_R_SUCCESS
;
2336 * Balance operation manual entry; startup, entrance to normal state. No
2337 * sense sending a POOLREQ at this stage; the peer is likely about to schedule
2338 * their own rebalance event upon entering normal themselves.
2341 dhcp_failover_pool_balance(dhcp_failover_state_t
*state
)
2343 /* Cancel pending event. */
2344 cancel_timeout(dhcp_failover_pool_rebalance
, state
);
2345 state
->sched_balance
= 0;
2347 dhcp_failover_pool_dobalance(state
, NULL
);
2351 * Balance operation entry from timer event. Once per timer interval is
2352 * the only time we want to emit POOLREQs (asserting an interrupt in our
2356 dhcp_failover_pool_rebalance(void *failover_state
)
2358 dhcp_failover_state_t
*state
;
2359 isc_boolean_t sendreq
= ISC_FALSE
;
2361 state
= (dhcp_failover_state_t
*)failover_state
;
2363 /* Clear scheduled event indicator. */
2364 state
->sched_balance
= 0;
2366 if (dhcp_failover_pool_dobalance(state
, &sendreq
))
2367 dhcp_failover_send_updates(state
);
2370 dhcp_failover_send_poolreq(state
);
2374 * Balance operation entry from POOLREQ protocol message. Do not permit a
2375 * POOLREQ to send back a POOLREQ. Ping pong.
2378 dhcp_failover_pool_reqbalance(dhcp_failover_state_t
*state
)
2382 /* Cancel pending event. */
2383 cancel_timeout(dhcp_failover_pool_rebalance
, state
);
2384 state
->sched_balance
= 0;
2386 queued
= dhcp_failover_pool_dobalance(state
, NULL
);
2388 dhcp_failover_send_poolresp(state
, queued
);
2391 dhcp_failover_send_updates(state
);
2393 log_info("peer %s: Got POOLREQ, answering negatively! "
2394 "Peer may be out of leases or database inconsistent.",
2399 * Do the meat of the work common to all forms of pool rebalance. If the
2400 * caller deems it appropriate to transmit POOLREQ messages, it can use the
2401 * sendreq pointer to pass in the address of a FALSE value which this function
2402 * will conditionally turn TRUE if a POOLREQ is determined to be necessary.
2403 * A NULL value may be passed, in which case no action is taken.
2406 dhcp_failover_pool_dobalance(dhcp_failover_state_t
*state
,
2407 isc_boolean_t
*sendreq
)
2409 int lts
, total
, thresh
, hold
, panic
, pass
;
2410 int leases_queued
= 0;
2411 struct lease
*lp
= (struct lease
*)0;
2412 struct lease
*next
= (struct lease
*)0;
2413 struct shared_network
*s
;
2415 binding_state_t peer_lease_state
;
2416 /* binding_state_t my_lease_state; */
2417 /* XXX Why is this my_lease_state never used? */
2419 int (*log_func
)(const char *, ...);
2420 const char *result
, *reqlog
;
2422 if (state
-> me
.state
!= normal
)
2425 state
->last_balance
= cur_time
;
2427 for (s
= shared_networks
; s
; s
= s
->next
) {
2428 for (p
= s
->pools
; p
; p
= p
->next
) {
2429 if (p
->failover_peer
!= state
)
2432 /* Right now we're giving the peer half of the free leases.
2433 If we have more leases than the peer (i.e., more than
2434 half), then the number of leases we have, less the number
2435 of leases the peer has, will be how many more leases we
2436 have than the peer has. So if we send half that number
2437 to the peer, we should be even. */
2438 if (p
->failover_peer
->i_am
== primary
) {
2439 lts
= (p
->free_leases
- p
->backup_leases
) / 2;
2440 peer_lease_state
= FTS_BACKUP
;
2441 /* my_lease_state = FTS_FREE; */
2444 lts
= (p
->backup_leases
- p
->free_leases
) / 2;
2445 peer_lease_state
= FTS_FREE
;
2446 /* my_lease_state = FTS_BACKUP; */
2450 total
= p
->backup_leases
+ p
->free_leases
;
2452 thresh
= ((total
* state
->max_lease_misbalance
) + 50) / 100;
2453 hold
= ((total
* state
->max_lease_ownership
) + 50) / 100;
2456 * If we need leases (so lts is negative) more than negative
2457 * double the thresh%, panic and send poolreq to hopefully wake
2458 * up the peer (but more likely the db is inconsistent). But,
2459 * if this comes out zero, switch to -1 so that the POOLREQ is
2460 * sent on lts == -2 rather than right away at -1.
2462 * Note that we do not subtract -1 from panic all the time
2463 * because thresh% and hold% may come out to the same number,
2464 * and that is correct operation...where thresh% and hold% are
2465 * both -1, we want to send poolreq when lts reaches -3. So,
2466 * "-3 < -2", lts < panic.
2468 panic
= thresh
* -2;
2473 if ((sendreq
!= NULL
) && (lts
< panic
)) {
2474 reqlog
= " (requesting peer rebalance!)";
2475 *sendreq
= ISC_TRUE
;
2479 log_info("balancing pool %lx %s total %d free %d "
2480 "backup %d lts %d max-own (+/-)%d%s",
2482 (p
->shared_network
?
2483 p
->shared_network
->name
: ""), p
->lease_count
,
2484 p
->free_leases
, p
->backup_leases
, lts
, hold
,
2487 /* In the first pass, try to allocate leases to the
2488 * peer which it would normally be responsible for (if
2489 * the lease has a hardware address or client-identifier,
2490 * and the load-balance-algorithm chooses the peer to
2491 * answer that address), up to a hold% excess in the peer's
2492 * favor. In the second pass, just send the oldest (first
2493 * on the list) leases up to a hold% excess in our favor.
2495 * This could make for additional pool rebalance
2496 * events, but preserving MAC possession should be
2500 lease_reference(&lp
, *lq
, MDL
);
2504 lease_dereference(&next
, MDL
);
2506 lease_reference(&next
, lp
->next
, MDL
);
2509 * Stop if the pool is 'balanced enough.'
2511 * The pool is balanced enough if:
2513 * 1) We're on the first run through and the peer has
2514 * its fair share of leases already (lts reaches
2516 * 2) We're on the second run through, we are shifting
2517 * never-used leases, and there is a perfectly even
2518 * balance (lts reaches zero).
2519 * 3) Second run through, we are shifting previously
2520 * used leases, and the local system has its fair
2521 * share but no more (lts reaches hold).
2523 * Note that this is implemented below in 3,2,1 order.
2533 } else if (lts
<= -hold
)
2536 if (pass
|| peer_wants_lease(lp
)) {
2539 lp
->next_binding_state
= peer_lease_state
;
2540 lp
->tstp
= cur_time
;
2541 lp
->starts
= cur_time
;
2543 if (!supersede_lease(lp
, NULL
, 0, 1, 0) ||
2545 log_error("can't commit lease %s on "
2546 "giveaway", piaddr(lp
->ip_addr
));
2549 lease_dereference(&lp
, MDL
);
2551 lease_reference(&lp
, next
, MDL
);
2554 lease_reference(&lp
, *lq
, MDL
);
2559 lease_dereference(&next
, MDL
);
2561 lease_dereference(&lp
, MDL
);
2564 result
= "IMBALANCED";
2565 log_func
= log_error
;
2567 result
= "balanced";
2568 log_func
= log_info
;
2571 log_func("%s pool %lx %s total %d free %d backup %d "
2572 "lts %d max-misbal %d", result
, (unsigned long)p
,
2573 (p
->shared_network
?
2574 p
->shared_network
->name
: ""), p
->lease_count
,
2575 p
->free_leases
, p
->backup_leases
, lts
, thresh
);
2577 /* Recalculate next rebalance event timer. */
2578 dhcp_failover_pool_check(p
);
2585 return leases_queued
;
2588 /* dhcp_failover_pool_check: Called whenever FREE or BACKUP leases change
2589 * states, on both servers. Check the scheduled time to rebalance the pool
2590 * and lower it if applicable.
2593 dhcp_failover_pool_check(struct pool
*pool
)
2595 dhcp_failover_state_t
*peer
;
2599 peer
= pool
->failover_peer
;
2601 if(!peer
|| peer
->me
.state
!= normal
)
2604 /* Estimate the time left until lease exhaustion.
2605 * The first lease on the backup or free lists is also the oldest
2606 * lease. It is reasonable to guess that it will take at least
2607 * as much time for a pool to run out of leases, as the present
2608 * age of the oldest lease (seconds since it expired).
2610 * Note that this isn't so sane of an assumption if the oldest
2611 * lease is a virgin (ends = 0), we wind up sending this against
2612 * the max_balance bounds check.
2614 if(pool
->free
&& pool
->free
->ends
< cur_time
)
2615 est1
= cur_time
- pool
->free
->ends
;
2619 if(pool
->backup
&& pool
->backup
->ends
< cur_time
)
2620 est2
= cur_time
- pool
->backup
->ends
;
2624 /* We don't want to schedule rebalance for when we think we'll run
2625 * out of leases, we want to schedule the rebalance for when we think
2626 * the disparity will be 'large enough' to warrant action.
2628 est1
= ((est1
* peer
->max_lease_misbalance
) + 50) / 100;
2629 est2
= ((est2
* peer
->max_lease_misbalance
) + 50) / 100;
2631 /* Guess when the local system will begin issuing POOLREQ panic
2632 * attacks because "max_lease_misbalance*2" has been exceeded.
2634 if(peer
->i_am
== primary
)
2639 /* Select the smallest time. */
2643 /* Bounded by the maximum configured value. */
2644 if(est1
> peer
->max_balance
)
2645 est1
= peer
->max_balance
;
2647 /* Project this time into the future. */
2650 /* Do not move the time down under the minimum. */
2651 est2
= peer
->last_balance
+ peer
->min_balance
;
2652 if(peer
->last_balance
&& (est1
< est2
))
2655 /* Introduce a random delay. */
2656 est1
+= random() % 5;
2658 /* Do not move the time forward, or reset to the same time. */
2659 if(peer
->sched_balance
) {
2660 if (est1
>= peer
->sched_balance
)
2663 /* We are about to schedule the time down, cancel the
2666 cancel_timeout(dhcp_failover_pool_rebalance
, peer
);
2669 /* The time is different, and lower, use it. */
2670 peer
->sched_balance
= est1
;
2672 #if defined(DEBUG_FAILOVER_TIMING)
2673 log_info("add_timeout +%d dhcp_failover_pool_rebalance",
2674 (int)(est1
- cur_time
));
2678 add_timeout(&tv
, dhcp_failover_pool_rebalance
, peer
,
2679 (tvref_t
)dhcp_failover_state_reference
,
2680 (tvunref_t
)dhcp_failover_state_dereference
);
2683 int dhcp_failover_state_pool_check (dhcp_failover_state_t
*state
)
2685 struct shared_network
*s
;
2688 for (s
= shared_networks
; s
; s
= s
-> next
) {
2689 for (p
= s
-> pools
; p
; p
= p
-> next
) {
2690 if (p
-> failover_peer
!= state
)
2692 dhcp_failover_pool_check (p
);
2698 isc_result_t
dhcp_failover_send_updates (dhcp_failover_state_t
*state
)
2700 struct lease
*lp
= (struct lease
*)0;
2701 isc_result_t status
;
2703 /* Can't update peer if we're not talking to it! */
2704 if (!state
-> link_to_peer
)
2705 return ISC_R_SUCCESS
;
2707 /* If there are acks pending, transmit them prior to potentially
2708 * sending new updates for the same lease.
2710 if (state
->toack_queue_head
!= NULL
)
2711 dhcp_failover_send_acks(state
);
2713 while ((state
-> partner
.max_flying_updates
>
2714 state
-> cur_unacked_updates
) && state
-> update_queue_head
) {
2715 /* Grab the head of the update queue. */
2716 lease_reference (&lp
, state
-> update_queue_head
, MDL
);
2718 /* Send the update to the peer. */
2719 status
= dhcp_failover_send_bind_update (state
, lp
);
2720 if (status
!= ISC_R_SUCCESS
) {
2721 lease_dereference (&lp
, MDL
);
2724 lp
-> flags
&= ~ON_UPDATE_QUEUE
;
2726 /* Take it off the head of the update queue and put the next
2727 item in the update queue at the head. */
2728 lease_dereference (&state
-> update_queue_head
, MDL
);
2729 if (lp
-> next_pending
) {
2730 lease_reference (&state
-> update_queue_head
,
2731 lp
-> next_pending
, MDL
);
2732 lease_dereference (&lp
-> next_pending
, MDL
);
2734 lease_dereference (&state
-> update_queue_tail
, MDL
);
2737 if (state
-> ack_queue_head
) {
2739 (&state
-> ack_queue_tail
-> next_pending
,
2741 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2743 lease_reference (&state
-> ack_queue_head
, lp
, MDL
);
2745 #if defined (POINTER_DEBUG)
2746 if (lp
-> next_pending
) {
2747 log_error ("ack_queue_tail: lp -> next_pending");
2751 lease_reference (&state
-> ack_queue_tail
, lp
, MDL
);
2752 lp
-> flags
|= ON_ACK_QUEUE
;
2753 lease_dereference (&lp
, MDL
);
2755 /* Count the object as an unacked update. */
2756 state
-> cur_unacked_updates
++;
2758 return ISC_R_SUCCESS
;
2761 /* Queue an update for a lease. Always returns 1 at this point - it's
2762 not an error for this to be called on a lease for which there's no
2765 int dhcp_failover_queue_update (struct lease
*lease
, int immediate
)
2767 dhcp_failover_state_t
*state
;
2769 if (!lease
-> pool
||
2770 !lease
-> pool
-> failover_peer
)
2773 /* If it's already on the update queue, leave it there. */
2774 if (lease
-> flags
& ON_UPDATE_QUEUE
)
2777 /* Get the failover state structure for this lease. */
2778 state
= lease
-> pool
-> failover_peer
;
2780 /* If it's on the ack queue, take it off. */
2781 if (lease
-> flags
& ON_ACK_QUEUE
)
2782 dhcp_failover_ack_queue_remove (state
, lease
);
2784 if (state
-> update_queue_head
) {
2785 lease_reference (&state
-> update_queue_tail
-> next_pending
,
2787 lease_dereference (&state
-> update_queue_tail
, MDL
);
2789 lease_reference (&state
-> update_queue_head
, lease
, MDL
);
2791 #if defined (POINTER_DEBUG)
2792 if (lease
-> next_pending
) {
2793 log_error ("next pending on update queue lease.");
2794 #if defined (DEBUG_RC_HISTORY)
2795 dump_rc_history (lease
);
2800 lease_reference (&state
-> update_queue_tail
, lease
, MDL
);
2801 lease
-> flags
|= ON_UPDATE_QUEUE
;
2803 dhcp_failover_send_updates (state
);
2807 int dhcp_failover_send_acks (dhcp_failover_state_t
*state
)
2809 failover_message_t
*msg
= (failover_message_t
*)0;
2811 /* Must commit all leases prior to acking them. */
2812 if (!commit_leases ())
2815 while (state
-> toack_queue_head
) {
2816 failover_message_reference
2817 (&msg
, state
-> toack_queue_head
, MDL
);
2818 failover_message_dereference
2819 (&state
-> toack_queue_head
, MDL
);
2821 failover_message_reference
2822 (&state
-> toack_queue_head
, msg
-> next
, MDL
);
2825 dhcp_failover_send_bind_ack (state
, msg
, 0, (const char *)0);
2827 failover_message_dereference (&msg
, MDL
);
2830 if (state
-> toack_queue_tail
)
2831 failover_message_dereference (&state
-> toack_queue_tail
, MDL
);
2832 state
-> pending_acks
= 0;
2837 void dhcp_failover_toack_queue_timeout (void *vs
)
2839 dhcp_failover_state_t
*state
= vs
;
2841 #if defined (DEBUG_FAILOVER_TIMING)
2842 log_info ("dhcp_failover_toack_queue_timeout");
2845 dhcp_failover_send_acks (state
);
2848 /* Queue an ack for a message. There is currently no way to queue a
2849 negative ack -- these need to be sent directly. */
2851 int dhcp_failover_queue_ack (dhcp_failover_state_t
*state
,
2852 failover_message_t
*msg
)
2856 if (state
-> toack_queue_head
) {
2857 failover_message_reference
2858 (&state
-> toack_queue_tail
-> next
, msg
, MDL
);
2859 failover_message_dereference (&state
-> toack_queue_tail
, MDL
);
2861 failover_message_reference (&state
-> toack_queue_head
,
2864 failover_message_reference (&state
-> toack_queue_tail
, msg
, MDL
);
2866 state
-> pending_acks
++;
2868 /* Flush the toack queue whenever we exceed half the number of
2869 allowed unacked updates. */
2870 if (state
-> pending_acks
>= state
-> partner
.max_flying_updates
/ 2) {
2871 dhcp_failover_send_acks (state
);
2874 /* Schedule a timeout to flush the ack queue. */
2875 if (state
-> pending_acks
> 0) {
2876 #if defined (DEBUG_FAILOVER_TIMING)
2877 log_info ("add_timeout +2 %s",
2878 "dhcp_failover_toack_queue_timeout");
2880 tv
. tv_sec
= cur_time
+ 2;
2883 dhcp_failover_toack_queue_timeout
, state
,
2884 (tvref_t
)dhcp_failover_state_reference
,
2885 (tvunref_t
)dhcp_failover_state_dereference
);
2891 void dhcp_failover_ack_queue_remove (dhcp_failover_state_t
*state
,
2892 struct lease
*lease
)
2896 if (!(lease
-> flags
& ON_ACK_QUEUE
))
2899 if (state
-> ack_queue_head
== lease
) {
2900 lease_dereference (&state
-> ack_queue_head
, MDL
);
2901 if (lease
-> next_pending
) {
2902 lease_reference (&state
-> ack_queue_head
,
2903 lease
-> next_pending
, MDL
);
2904 lease_dereference (&lease
-> next_pending
, MDL
);
2906 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2909 for (lp
= state
-> ack_queue_head
;
2910 lp
&& lp
-> next_pending
!= lease
;
2911 lp
= lp
-> next_pending
)
2917 lease_dereference (&lp
-> next_pending
, MDL
);
2918 if (lease
-> next_pending
) {
2919 lease_reference (&lp
-> next_pending
,
2920 lease
-> next_pending
, MDL
);
2921 lease_dereference (&lease
-> next_pending
, MDL
);
2923 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2924 if (lp
-> next_pending
) {
2925 log_error ("state -> ack_queue_tail");
2928 lease_reference (&state
-> ack_queue_tail
, lp
, MDL
);
2932 lease
-> flags
&= ~ON_ACK_QUEUE
;
2933 /* Multiple acks on one XID is an error and may cause badness. */
2934 lease
->last_xid
= 0;
2935 /* XXX: this violates draft-failover. We can't send another
2936 * update just because we forgot about an old one that hasn't
2939 state
-> cur_unacked_updates
--;
2942 * When updating leases as a result of an ack, we defer the commit
2943 * for performance reasons. When there are no more acks pending,
2946 if (state
-> cur_unacked_updates
== 0) {
2951 isc_result_t
dhcp_failover_state_set_value (omapi_object_t
*h
,
2953 omapi_data_string_t
*name
,
2954 omapi_typed_data_t
*value
)
2956 isc_result_t status
;
2958 if (h
-> type
!= dhcp_type_failover_state
)
2959 return DHCP_R_INVALIDARG
;
2961 /* This list of successful returns is completely wrong, but the
2962 fastest way to make dhcpctl do something vaguely sane when
2963 you try to change the local state. */
2965 if (!omapi_ds_strcmp (name
, "name")) {
2966 return ISC_R_SUCCESS
;
2967 } else if (!omapi_ds_strcmp (name
, "partner-address")) {
2968 return ISC_R_SUCCESS
;
2969 } else if (!omapi_ds_strcmp (name
, "local-address")) {
2970 return ISC_R_SUCCESS
;
2971 } else if (!omapi_ds_strcmp (name
, "partner-port")) {
2972 return ISC_R_SUCCESS
;
2973 } else if (!omapi_ds_strcmp (name
, "local-port")) {
2974 return ISC_R_SUCCESS
;
2975 } else if (!omapi_ds_strcmp (name
, "max-outstanding-updates")) {
2976 return ISC_R_SUCCESS
;
2977 } else if (!omapi_ds_strcmp (name
, "mclt")) {
2978 return ISC_R_SUCCESS
;
2979 } else if (!omapi_ds_strcmp (name
, "load-balance-max-secs")) {
2980 return ISC_R_SUCCESS
;
2981 } else if (!omapi_ds_strcmp (name
, "load-balance-hba")) {
2982 return ISC_R_SUCCESS
;
2983 } else if (!omapi_ds_strcmp (name
, "partner-state")) {
2984 return ISC_R_SUCCESS
;
2985 } else if (!omapi_ds_strcmp (name
, "local-state")) {
2987 status
= omapi_get_int_value (&l
, value
);
2988 if (status
!= ISC_R_SUCCESS
)
2990 return dhcp_failover_set_state ((dhcp_failover_state_t
*)h
, l
);
2991 } else if (!omapi_ds_strcmp (name
, "partner-stos")) {
2992 return ISC_R_SUCCESS
;
2993 } else if (!omapi_ds_strcmp (name
, "local-stos")) {
2994 return ISC_R_SUCCESS
;
2995 } else if (!omapi_ds_strcmp (name
, "hierarchy")) {
2996 return ISC_R_SUCCESS
;
2997 } else if (!omapi_ds_strcmp (name
, "last-packet-sent")) {
2998 return ISC_R_SUCCESS
;
2999 } else if (!omapi_ds_strcmp (name
, "last-timestamp-received")) {
3000 return ISC_R_SUCCESS
;
3001 } else if (!omapi_ds_strcmp (name
, "skew")) {
3002 return ISC_R_SUCCESS
;
3003 } else if (!omapi_ds_strcmp (name
, "max-response-delay")) {
3004 return ISC_R_SUCCESS
;
3005 } else if (!omapi_ds_strcmp (name
, "cur-unacked-updates")) {
3006 return ISC_R_SUCCESS
;
3009 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
3010 return (*(h
-> inner
-> type
-> set_value
))
3011 (h
-> inner
, id
, name
, value
);
3012 return ISC_R_NOTFOUND
;
3015 void dhcp_failover_keepalive (void *vs
)
3019 void dhcp_failover_reconnect (void *vs
)
3021 dhcp_failover_state_t
*state
= vs
;
3022 isc_result_t status
;
3025 #if defined (DEBUG_FAILOVER_TIMING)
3026 log_info ("dhcp_failover_reconnect");
3028 /* If we already connected the other way, let the connection
3029 recovery code initiate any retry that may be required. */
3030 if (state
-> link_to_peer
)
3033 status
= dhcp_failover_link_initiate ((omapi_object_t
*)state
);
3034 if (status
!= ISC_R_SUCCESS
&& status
!= DHCP_R_INCOMPLETE
) {
3035 log_info ("failover peer %s: %s", state
-> name
,
3036 isc_result_totext (status
));
3037 #if defined (DEBUG_FAILOVER_TIMING)
3038 log_info("add_timeout +90 dhcp_failover_reconnect");
3040 tv
. tv_sec
= cur_time
+ 90;
3042 add_timeout(&tv
, dhcp_failover_reconnect
, state
,
3043 (tvref_t
)dhcp_failover_state_reference
,
3044 (tvunref_t
)dhcp_failover_state_dereference
);
3048 void dhcp_failover_startup_timeout (void *vs
)
3050 dhcp_failover_state_t
*state
= vs
;
3052 #if defined (DEBUG_FAILOVER_TIMING)
3053 log_info ("dhcp_failover_startup_timeout");
3056 dhcp_failover_state_transition (state
, "disconnect");
3059 void dhcp_failover_link_startup_timeout (void *vl
)
3061 dhcp_failover_link_t
*link
= vl
;
3064 for (p
= (omapi_object_t
*)link
; p
-> inner
; p
= p
-> inner
)
3066 for (; p
; p
= p
-> outer
)
3067 if (p
-> type
== omapi_type_connection
)
3070 log_info ("failover: link startup timeout");
3071 omapi_disconnect (p
, 1);
3075 void dhcp_failover_listener_restart (void *vs
)
3077 dhcp_failover_state_t
*state
= vs
;
3078 isc_result_t status
;
3081 #if defined (DEBUG_FAILOVER_TIMING)
3082 log_info ("dhcp_failover_listener_restart");
3085 status
= dhcp_failover_listen ((omapi_object_t
*)state
);
3086 if (status
!= ISC_R_SUCCESS
) {
3087 log_info ("failover peer %s: %s", state
-> name
,
3088 isc_result_totext (status
));
3089 #if defined (DEBUG_FAILOVER_TIMING)
3090 log_info ("add_timeout +90 %s",
3091 "dhcp_failover_listener_restart");
3093 tv
. tv_sec
= cur_time
+ 90;
3096 dhcp_failover_listener_restart
, state
,
3097 (tvref_t
)dhcp_failover_state_reference
,
3098 (tvunref_t
)dhcp_failover_state_dereference
);
3103 dhcp_failover_auto_partner_down(void *vs
)
3105 dhcp_failover_state_t
*state
= vs
;
3107 #if defined (DEBUG_FAILOVER_TIMING)
3108 log_info("dhcp_failover_auto_partner_down");
3111 dhcp_failover_set_state(state
, partner_down
);
3114 isc_result_t
dhcp_failover_state_get_value (omapi_object_t
*h
,
3116 omapi_data_string_t
*name
,
3117 omapi_value_t
**value
)
3119 dhcp_failover_state_t
*s
;
3120 struct option_cache
*oc
;
3121 struct data_string ds
;
3122 isc_result_t status
;
3124 if (h
-> type
!= dhcp_type_failover_state
)
3125 return DHCP_R_INVALIDARG
;
3126 s
= (dhcp_failover_state_t
*)h
;
3128 if (!omapi_ds_strcmp (name
, "name")) {
3130 return omapi_make_string_value (value
,
3131 name
, s
-> name
, MDL
);
3132 return ISC_R_NOTFOUND
;
3133 } else if (!omapi_ds_strcmp (name
, "partner-address")) {
3134 oc
= s
-> partner
.address
;
3136 memset (&ds
, 0, sizeof ds
);
3137 if (!evaluate_option_cache (&ds
, (struct packet
*)0,
3139 (struct client_state
*)0,
3140 (struct option_state
*)0,
3141 (struct option_state
*)0,
3142 &global_scope
, oc
, MDL
)) {
3143 return ISC_R_NOTFOUND
;
3145 status
= omapi_make_const_value (value
,
3146 name
, ds
.data
, ds
.len
, MDL
);
3147 /* Disgusting kludge: */
3148 if (oc
== s
-> me
.address
&& !s
-> server_identifier
.len
)
3149 data_string_copy (&s
-> server_identifier
, &ds
, MDL
);
3150 data_string_forget (&ds
, MDL
);
3152 } else if (!omapi_ds_strcmp (name
, "local-address")) {
3153 oc
= s
-> me
.address
;
3155 } else if (!omapi_ds_strcmp (name
, "partner-port")) {
3156 return omapi_make_int_value (value
, name
,
3157 s
-> partner
.port
, MDL
);
3158 } else if (!omapi_ds_strcmp (name
, "local-port")) {
3159 return omapi_make_int_value (value
,
3160 name
, s
-> me
.port
, MDL
);
3161 } else if (!omapi_ds_strcmp (name
, "max-outstanding-updates")) {
3162 return omapi_make_uint_value (value
, name
,
3163 s
-> me
.max_flying_updates
,
3165 } else if (!omapi_ds_strcmp (name
, "mclt")) {
3166 return omapi_make_uint_value (value
, name
, s
-> mclt
, MDL
);
3167 } else if (!omapi_ds_strcmp (name
, "load-balance-max-secs")) {
3168 return omapi_make_int_value (value
, name
,
3169 s
-> load_balance_max_secs
, MDL
);
3170 } else if (!omapi_ds_strcmp (name
, "load-balance-hba")) {
3172 return omapi_make_const_value (value
, name
,
3174 return ISC_R_NOTFOUND
;
3175 } else if (!omapi_ds_strcmp (name
, "partner-state")) {
3176 return omapi_make_uint_value (value
, name
,
3177 s
-> partner
.state
, MDL
);
3178 } else if (!omapi_ds_strcmp (name
, "local-state")) {
3179 return omapi_make_uint_value (value
, name
,
3180 s
-> me
.state
, MDL
);
3181 } else if (!omapi_ds_strcmp (name
, "partner-stos")) {
3182 return omapi_make_int_value (value
, name
,
3183 s
-> partner
.stos
, MDL
);
3184 } else if (!omapi_ds_strcmp (name
, "local-stos")) {
3185 return omapi_make_int_value (value
, name
,
3187 } else if (!omapi_ds_strcmp (name
, "hierarchy")) {
3188 return omapi_make_uint_value (value
, name
, s
-> i_am
, MDL
);
3189 } else if (!omapi_ds_strcmp (name
, "last-packet-sent")) {
3190 return omapi_make_int_value (value
, name
,
3191 s
-> last_packet_sent
, MDL
);
3192 } else if (!omapi_ds_strcmp (name
, "last-timestamp-received")) {
3193 return omapi_make_int_value (value
, name
,
3194 s
-> last_timestamp_received
,
3196 } else if (!omapi_ds_strcmp (name
, "skew")) {
3197 return omapi_make_int_value (value
, name
, s
-> skew
, MDL
);
3198 } else if (!omapi_ds_strcmp (name
, "max-response-delay")) {
3199 return omapi_make_uint_value (value
, name
,
3200 s
-> me
.max_response_delay
,
3202 } else if (!omapi_ds_strcmp (name
, "cur-unacked-updates")) {
3203 return omapi_make_int_value (value
, name
,
3204 s
-> cur_unacked_updates
, MDL
);
3207 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
3208 return (*(h
-> inner
-> type
-> get_value
))
3209 (h
-> inner
, id
, name
, value
);
3210 return ISC_R_NOTFOUND
;
3213 isc_result_t
dhcp_failover_state_destroy (omapi_object_t
*h
,
3214 const char *file
, int line
)
3216 dhcp_failover_state_t
*s
;
3218 if (h
-> type
!= dhcp_type_failover_state
)
3219 return DHCP_R_INVALIDARG
;
3220 s
= (dhcp_failover_state_t
*)h
;
3222 if (s
-> link_to_peer
)
3223 dhcp_failover_link_dereference (&s
-> link_to_peer
, file
, line
);
3225 dfree (s
-> name
, MDL
);
3226 s
-> name
= (char *)0;
3228 if (s
-> partner
.address
)
3229 option_cache_dereference (&s
-> partner
.address
, file
, line
);
3230 if (s
-> me
.address
)
3231 option_cache_dereference (&s
-> me
.address
, file
, line
);
3233 dfree (s
-> hba
, file
, line
);
3234 s
-> hba
= (u_int8_t
*)0;
3236 if (s
-> update_queue_head
)
3237 lease_dereference (&s
-> update_queue_head
, file
, line
);
3238 if (s
-> update_queue_tail
)
3239 lease_dereference (&s
-> update_queue_tail
, file
, line
);
3240 if (s
-> ack_queue_head
)
3241 lease_dereference (&s
-> ack_queue_head
, file
, line
);
3242 if (s
-> ack_queue_tail
)
3243 lease_dereference (&s
-> ack_queue_tail
, file
, line
);
3244 if (s
-> send_update_done
)
3245 lease_dereference (&s
-> send_update_done
, file
, line
);
3246 if (s
-> toack_queue_head
)
3247 failover_message_dereference (&s
-> toack_queue_head
,
3249 if (s
-> toack_queue_tail
)
3250 failover_message_dereference (&s
-> toack_queue_tail
,
3252 return ISC_R_SUCCESS
;
3255 /* Write all the published values associated with the object through the
3256 specified connection. */
3258 isc_result_t
dhcp_failover_state_stuff (omapi_object_t
*c
,
3262 /* In this function c should be a (omapi_connection_object_t *) */
3264 dhcp_failover_state_t
*s
;
3265 isc_result_t status
;
3267 if (c
-> type
!= omapi_type_connection
)
3268 return DHCP_R_INVALIDARG
;
3270 if (h
-> type
!= dhcp_type_failover_state
)
3271 return DHCP_R_INVALIDARG
;
3272 s
= (dhcp_failover_state_t
*)h
;
3274 status
= omapi_connection_put_name (c
, "name");
3275 if (status
!= ISC_R_SUCCESS
)
3277 status
= omapi_connection_put_string (c
, s
-> name
);
3278 if (status
!= ISC_R_SUCCESS
)
3281 status
= omapi_connection_put_name (c
, "partner-address");
3282 if (status
!= ISC_R_SUCCESS
)
3284 status
= omapi_connection_put_uint32 (c
, sizeof s
-> partner
.address
);
3285 if (status
!= ISC_R_SUCCESS
)
3287 status
= omapi_connection_copyin (c
, (u_int8_t
*)&s
-> partner
.address
,
3288 sizeof s
-> partner
.address
);
3289 if (status
!= ISC_R_SUCCESS
)
3292 status
= omapi_connection_put_name (c
, "partner-port");
3293 if (status
!= ISC_R_SUCCESS
)
3295 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3296 if (status
!= ISC_R_SUCCESS
)
3298 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> partner
.port
);
3299 if (status
!= ISC_R_SUCCESS
)
3302 status
= omapi_connection_put_name (c
, "local-address");
3303 if (status
!= ISC_R_SUCCESS
)
3305 status
= omapi_connection_put_uint32 (c
, sizeof s
-> me
.address
);
3306 if (status
!= ISC_R_SUCCESS
)
3308 status
= omapi_connection_copyin (c
, (u_int8_t
*)&s
-> me
.address
,
3309 sizeof s
-> me
.address
);
3310 if (status
!= ISC_R_SUCCESS
)
3313 status
= omapi_connection_put_name (c
, "local-port");
3314 if (status
!= ISC_R_SUCCESS
)
3316 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3317 if (status
!= ISC_R_SUCCESS
)
3319 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> me
.port
);
3320 if (status
!= ISC_R_SUCCESS
)
3323 status
= omapi_connection_put_name (c
, "max-outstanding-updates");
3324 if (status
!= ISC_R_SUCCESS
)
3326 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3327 if (status
!= ISC_R_SUCCESS
)
3329 status
= omapi_connection_put_uint32 (c
,
3330 s
-> me
.max_flying_updates
);
3331 if (status
!= ISC_R_SUCCESS
)
3334 status
= omapi_connection_put_name (c
, "mclt");
3335 if (status
!= ISC_R_SUCCESS
)
3337 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3338 if (status
!= ISC_R_SUCCESS
)
3340 status
= omapi_connection_put_uint32 (c
, s
-> mclt
);
3341 if (status
!= ISC_R_SUCCESS
)
3344 status
= omapi_connection_put_name (c
, "load-balance-max-secs");
3345 if (status
!= ISC_R_SUCCESS
)
3347 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3348 if (status
!= ISC_R_SUCCESS
)
3350 status
= (omapi_connection_put_uint32
3351 (c
, (u_int32_t
)s
-> load_balance_max_secs
));
3352 if (status
!= ISC_R_SUCCESS
)
3357 status
= omapi_connection_put_name (c
, "load-balance-hba");
3358 if (status
!= ISC_R_SUCCESS
)
3360 status
= omapi_connection_put_uint32 (c
, 32);
3361 if (status
!= ISC_R_SUCCESS
)
3363 status
= omapi_connection_copyin (c
, s
-> hba
, 32);
3364 if (status
!= ISC_R_SUCCESS
)
3368 status
= omapi_connection_put_name (c
, "partner-state");
3369 if (status
!= ISC_R_SUCCESS
)
3371 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3372 if (status
!= ISC_R_SUCCESS
)
3374 status
= omapi_connection_put_uint32 (c
, s
-> partner
.state
);
3375 if (status
!= ISC_R_SUCCESS
)
3378 status
= omapi_connection_put_name (c
, "local-state");
3379 if (status
!= ISC_R_SUCCESS
)
3381 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3382 if (status
!= ISC_R_SUCCESS
)
3384 status
= omapi_connection_put_uint32 (c
, s
-> me
.state
);
3385 if (status
!= ISC_R_SUCCESS
)
3388 status
= omapi_connection_put_name (c
, "partner-stos");
3389 if (status
!= ISC_R_SUCCESS
)
3391 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3392 if (status
!= ISC_R_SUCCESS
)
3394 status
= omapi_connection_put_uint32 (c
,
3395 (u_int32_t
)s
-> partner
.stos
);
3396 if (status
!= ISC_R_SUCCESS
)
3399 status
= omapi_connection_put_name (c
, "local-stos");
3400 if (status
!= ISC_R_SUCCESS
)
3402 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3403 if (status
!= ISC_R_SUCCESS
)
3405 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> me
.stos
);
3406 if (status
!= ISC_R_SUCCESS
)
3409 status
= omapi_connection_put_name (c
, "hierarchy");
3410 if (status
!= ISC_R_SUCCESS
)
3412 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3413 if (status
!= ISC_R_SUCCESS
)
3415 status
= omapi_connection_put_uint32 (c
, s
-> i_am
);
3416 if (status
!= ISC_R_SUCCESS
)
3419 status
= omapi_connection_put_name (c
, "last-packet-sent");
3420 if (status
!= ISC_R_SUCCESS
)
3422 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3423 if (status
!= ISC_R_SUCCESS
)
3425 status
= (omapi_connection_put_uint32
3426 (c
, (u_int32_t
)s
-> last_packet_sent
));
3427 if (status
!= ISC_R_SUCCESS
)
3430 status
= omapi_connection_put_name (c
, "last-timestamp-received");
3431 if (status
!= ISC_R_SUCCESS
)
3433 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3434 if (status
!= ISC_R_SUCCESS
)
3436 status
= (omapi_connection_put_uint32
3437 (c
, (u_int32_t
)s
-> last_timestamp_received
));
3438 if (status
!= ISC_R_SUCCESS
)
3441 status
= omapi_connection_put_name (c
, "skew");
3442 if (status
!= ISC_R_SUCCESS
)
3444 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3445 if (status
!= ISC_R_SUCCESS
)
3447 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> skew
);
3448 if (status
!= ISC_R_SUCCESS
)
3451 status
= omapi_connection_put_name (c
, "max-response-delay");
3452 if (status
!= ISC_R_SUCCESS
)
3454 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3455 if (status
!= ISC_R_SUCCESS
)
3457 status
= (omapi_connection_put_uint32
3458 (c
, (u_int32_t
)s
-> me
.max_response_delay
));
3459 if (status
!= ISC_R_SUCCESS
)
3462 status
= omapi_connection_put_name (c
, "cur-unacked-updates");
3463 if (status
!= ISC_R_SUCCESS
)
3465 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3466 if (status
!= ISC_R_SUCCESS
)
3468 status
= (omapi_connection_put_uint32
3469 (c
, (u_int32_t
)s
-> cur_unacked_updates
));
3470 if (status
!= ISC_R_SUCCESS
)
3473 if (h
-> inner
&& h
-> inner
-> type
-> stuff_values
)
3474 return (*(h
-> inner
-> type
-> stuff_values
)) (c
, id
,
3476 return ISC_R_SUCCESS
;
3479 isc_result_t
dhcp_failover_state_lookup (omapi_object_t
**sp
,
3481 omapi_object_t
*ref
)
3483 omapi_value_t
*tv
= (omapi_value_t
*)0;
3484 isc_result_t status
;
3485 dhcp_failover_state_t
*s
;
3488 return DHCP_R_NOKEYS
;
3490 /* First see if we were sent a handle. */
3491 status
= omapi_get_value_str (ref
, id
, "handle", &tv
);
3492 if (status
== ISC_R_SUCCESS
) {
3493 status
= omapi_handle_td_lookup (sp
, tv
-> value
);
3495 omapi_value_dereference (&tv
, MDL
);
3496 if (status
!= ISC_R_SUCCESS
)
3499 /* Don't return the object if the type is wrong. */
3500 if ((*sp
) -> type
!= dhcp_type_failover_state
) {
3501 omapi_object_dereference (sp
, MDL
);
3502 return DHCP_R_INVALIDARG
;
3506 /* Look the failover state up by peer name. */
3507 status
= omapi_get_value_str (ref
, id
, "name", &tv
);
3508 if (status
== ISC_R_SUCCESS
) {
3509 for (s
= failover_states
; s
; s
= s
-> next
) {
3510 unsigned l
= strlen (s
-> name
);
3511 if (l
== tv
-> value
-> u
.buffer
.len
&&
3513 tv
-> value
-> u
.buffer
.value
, l
))
3516 omapi_value_dereference (&tv
, MDL
);
3518 /* If we already have a lease, and it's not the same one,
3519 then the query was invalid. */
3520 if (*sp
&& *sp
!= (omapi_object_t
*)s
) {
3521 omapi_object_dereference (sp
, MDL
);
3522 return DHCP_R_KEYCONFLICT
;
3525 omapi_object_dereference (sp
, MDL
);
3526 return ISC_R_NOTFOUND
;
3528 /* XXX fix so that hash lookup itself creates
3529 XXX the reference. */
3530 omapi_object_reference (sp
, (omapi_object_t
*)s
, MDL
);
3533 /* If we get to here without finding a lease, no valid key was
3536 return DHCP_R_NOKEYS
;
3537 return ISC_R_SUCCESS
;
3540 isc_result_t
dhcp_failover_state_create (omapi_object_t
**sp
,
3543 return ISC_R_NOTIMPLEMENTED
;
3546 isc_result_t
dhcp_failover_state_remove (omapi_object_t
*sp
,
3549 return ISC_R_NOTIMPLEMENTED
;
3552 int dhcp_failover_state_match (dhcp_failover_state_t
*state
,
3553 u_int8_t
*addr
, unsigned addrlen
)
3555 struct data_string ds
;
3558 memset (&ds
, 0, sizeof ds
);
3559 if (evaluate_option_cache (&ds
, (struct packet
*)0,
3561 (struct client_state
*)0,
3562 (struct option_state
*)0,
3563 (struct option_state
*)0,
3565 state
-> partner
.address
, MDL
)) {
3566 for (i
= 0; i
+ addrlen
- 1 < ds
.len
; i
+= addrlen
) {
3567 if (!memcmp (&ds
.data
[i
],
3569 data_string_forget (&ds
, MDL
);
3573 data_string_forget (&ds
, MDL
);
3579 dhcp_failover_state_match_by_name(state
, name
)
3580 dhcp_failover_state_t
*state
;
3581 failover_option_t
*name
;
3583 if ((strlen(state
->name
) == name
->count
) &&
3584 (memcmp(state
->name
, name
->data
, name
->count
) == 0))
3590 const char *dhcp_failover_reject_reason_print (int reason
)
3592 static char resbuf
[sizeof("Undefined-255: This reason code is not defined "
3593 "in the protocol standard.")];
3595 if ((reason
> 0xff) || (reason
< 0))
3596 return "Reason code out of range.";
3599 case FTR_ILLEGAL_IP_ADDR
:
3600 return "Illegal IP address (not part of any address pool).";
3602 case FTR_FATAL_CONFLICT
:
3603 return "Fatal conflict exists: address in use by other client.";
3605 case FTR_MISSING_BINDINFO
:
3606 return "Missing binding information.";
3608 case FTR_TIMEMISMATCH
:
3609 return "Connection rejected, time mismatch too great.";
3611 case FTR_INVALID_MCLT
:
3612 return "Connection rejected, invalid MCLT.";
3614 case FTR_MISC_REJECT
:
3615 return "Connection rejected, unknown reason.";
3617 case FTR_DUP_CONNECTION
:
3618 return "Connection rejected, duplicate connection.";
3620 case FTR_INVALID_PARTNER
:
3621 return "Connection rejected, invalid failover partner.";
3623 case FTR_TLS_UNSUPPORTED
:
3624 return "TLS not supported.";
3626 case FTR_TLS_UNCONFIGURED
:
3627 return "TLS supported but not configured.";
3629 case FTR_TLS_REQUIRED
:
3630 return "TLS required but not supported by partner.";
3632 case FTR_DIGEST_UNSUPPORTED
:
3633 return "Message digest not supported.";
3635 case FTR_DIGEST_UNCONFIGURED
:
3636 return "Message digest not configured.";
3638 case FTR_VERSION_MISMATCH
:
3639 return "Protocol version mismatch.";
3641 case FTR_OUTDATED_BIND_INFO
:
3642 return "Outdated binding information.";
3644 case FTR_LESS_CRIT_BIND_INFO
:
3645 return "Less critical binding information.";
3647 case FTR_NO_TRAFFIC
:
3648 return "No traffic within sufficient time.";
3650 case FTR_HBA_CONFLICT
:
3651 return "Hash bucket assignment conflict.";
3653 case FTR_IP_NOT_RESERVED
:
3654 return "IP not reserved on this server.";
3656 case FTR_IP_DIGEST_FAILURE
:
3657 return "Message digest failed to compare.";
3659 case FTR_IP_MISSING_DIGEST
:
3660 return "Missing message digest.";
3663 return "Unknown Error.";
3666 sprintf(resbuf
, "Undefined-%d: This reason code is not defined in the "
3667 "protocol standard.", reason
);
3672 const char *dhcp_failover_state_name_print (enum failover_state state
)
3677 return "unknown-state";
3680 return "partner-down";
3686 return "conflict-done";
3688 case communications_interrupted
:
3689 return "communications-interrupted";
3691 case resolution_interrupted
:
3692 return "resolution-interrupted";
3694 case potential_conflict
:
3695 return "potential-conflict";
3701 return "recover-done";
3704 return "recover-wait";
3717 const char *dhcp_failover_message_name (unsigned type
)
3719 static char messbuf
[sizeof("unknown-message-255")];
3722 return "invalid-message";
3726 return "pool-request";
3729 return "pool-response";
3732 return "bind-update";
3740 case FTM_CONNECTACK
:
3741 return "connect-ack";
3744 return "update-request";
3747 return "update-done";
3750 return "update-request-all";
3758 case FTM_DISCONNECT
:
3759 return "disconnect";
3762 sprintf(messbuf
, "unknown-message-%u", type
);
3767 const char *dhcp_failover_option_name (unsigned type
)
3769 static char optbuf
[sizeof("unknown-option-65535")];
3772 return "invalid-option";
3775 case FTO_ADDRESSES_TRANSFERRED
:
3776 return "addresses-transferred";
3778 case FTO_ASSIGNED_IP_ADDRESS
:
3779 return "assigned-ip-address";
3781 case FTO_BINDING_STATUS
:
3782 return "binding-status";
3784 case FTO_CLIENT_IDENTIFIER
:
3785 return "client-identifier";
3796 case FTO_DELAYED_SERVICE
:
3797 return "delayed-service";
3805 case FTO_LEASE_EXPIRY
:
3806 return "lease-expiry";
3808 case FTO_MAX_UNACKED
:
3809 return "max-unacked";
3817 case FTO_MESSAGE_DIGEST
:
3818 return "message-digest";
3820 case FTO_POTENTIAL_EXPIRY
:
3821 return "potential-expiry";
3823 case FTO_PROTOCOL_VERSION
:
3824 return "protocol-version";
3826 case FTO_RECEIVE_TIMER
:
3827 return "receive-timer";
3829 case FTO_REJECT_REASON
:
3830 return "reject-reason";
3832 case FTO_RELATIONSHIP_NAME
:
3833 return "relationship-name";
3835 case FTO_REPLY_OPTIONS
:
3836 return "reply-options";
3838 case FTO_REQUEST_OPTIONS
:
3839 return "request-options";
3841 case FTO_SERVER_FLAGS
:
3842 return "server-flags";
3844 case FTO_SERVER_STATE
:
3845 return "server-state";
3853 case FTO_TLS_REQUEST
:
3854 return "tls-request";
3856 case FTO_VENDOR_CLASS
:
3857 return "vendor-class";
3859 case FTO_VENDOR_OPTIONS
:
3860 return "vendor-options";
3863 sprintf(optbuf
, "unknown-option-%u", type
);
3868 failover_option_t
*dhcp_failover_option_printf (unsigned code
,
3872 const char *fmt
, ...)
3877 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3878 * It is unclear what the effects of truncation here are, or
3879 * how that condition should be handled. It seems that this
3880 * function is used for formatting messages in the failover
3881 * command channel. For now the safest thing is for
3882 * overflow-truncation to cause a fatal log.
3885 if (vsnprintf (tbuf
, sizeof tbuf
, fmt
, va
) >= sizeof tbuf
)
3886 log_fatal ("%s: vsnprintf would truncate",
3887 "dhcp_failover_make_option");
3890 return dhcp_failover_make_option (code
, obuf
, obufix
, obufmax
,
3891 strlen (tbuf
), tbuf
);
3894 failover_option_t
*dhcp_failover_make_option (unsigned code
,
3895 char *obuf
, unsigned *obufix
,
3896 unsigned obufmax
, ...)
3899 struct failover_option_info
*info
;
3901 unsigned size
, count
;
3907 #if defined (DEBUG_FAILOVER_MESSAGES)
3911 /* Note that the failover_option structure is used differently on
3912 input than on output - on input, count is an element count, and
3913 on output it's the number of bytes total in the option, including
3914 the option code and option length. */
3915 failover_option_t option
, *op
;
3918 /* Bogus option code? */
3919 if (code
< 1 || code
> FTO_MAX
|| ft_options
[code
].type
== FT_UNDEF
) {
3920 return &null_failover_option
;
3922 info
= &ft_options
[code
];
3924 va_start (va
, obufmax
);
3926 /* Get the number of elements and the size of the buffer we need
3928 if (info
-> type
== FT_DDNS
|| info
-> type
== FT_DDNS1
) {
3929 count
= info
-> type
== FT_DDNS
? 1 : 2;
3930 size
= va_arg (va
, int) + count
;
3932 /* Find out how many items in this list. */
3933 if (info
-> num_present
)
3934 count
= info
-> num_present
;
3936 count
= va_arg (va
, int);
3938 /* Figure out size. */
3939 switch (info
-> type
) {
3946 case FT_TEXT_OR_BYTES
:
3948 txt
= va_arg (va
, char *);
3953 ilen
= va_arg (va
, unsigned);
3954 size
= count
* ilen
;
3966 /* shouldn't get here. */
3967 log_fatal ("bogus type in failover_make_option: %d",
3969 return &null_failover_option
;
3975 /* Allocate a buffer for the option. */
3976 option
.count
= size
;
3977 option
.data
= dmalloc (option
.count
, MDL
);
3980 return &null_failover_option
;
3983 /* Put in the option code and option length. */
3984 putUShort (option
.data
, code
);
3985 putUShort (&option
.data
[2], size
- 4);
3987 #if defined (DEBUG_FAILOVER_MESSAGES)
3988 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3989 * It is unclear what the effects of truncation here are, or
3990 * how that condition should be handled. It seems that this
3991 * message may be sent over the failover command channel.
3992 * For now the safest thing is for overflow-truncation to cause
3995 if (snprintf (tbuf
, sizeof tbuf
, " (%s<%d>", info
-> name
,
3996 option
.count
) >= sizeof tbuf
)
3997 log_fatal ("dhcp_failover_make_option: tbuf overflow");
3998 failover_print (obuf
, obufix
, obufmax
, tbuf
);
4001 /* Now put in the data. */
4002 switch (info
-> type
) {
4004 for (i
= 0; i
< count
; i
++) {
4005 val
= va_arg (va
, unsigned);
4006 #if defined (DEBUG_FAILOVER_MESSAGES)
4007 /* %Audit% Cannot exceed 24 bytes. %2004.06.17,Safe% */
4008 sprintf (tbuf
, " %d", val
);
4009 failover_print (obuf
, obufix
, obufmax
, tbuf
);
4011 option
.data
[i
+ 4] = val
;
4016 for (i
= 0; i
< count
; i
++) {
4017 iaddr
= va_arg (va
, u_int8_t
*);
4019 dfree (option
.data
, MDL
);
4020 log_error ("IP addrlen=%d, should be 4.",
4023 return &null_failover_option
;
4026 #if defined (DEBUG_FAILOVER_MESSAGES)
4027 /*%Audit% Cannot exceed 17 bytes. %2004.06.17,Safe%*/
4028 sprintf (tbuf
, " %u.%u.%u.%u",
4029 iaddr
[0], iaddr
[1], iaddr
[2], iaddr
[3]);
4030 failover_print (obuf
, obufix
, obufmax
, tbuf
);
4032 memcpy (&option
.data
[4 + i
* ilen
], iaddr
, ilen
);
4037 for (i
= 0; i
< count
; i
++) {
4038 val
= va_arg (va
, unsigned);
4039 #if defined (DEBUG_FAILOVER_MESSAGES)
4040 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
4041 sprintf (tbuf
, " %d", val
);
4042 failover_print (obuf
, obufix
, obufmax
, tbuf
);
4044 putULong (&option
.data
[4 + i
* 4], val
);
4050 bval
= va_arg (va
, u_int8_t
*);
4051 #if defined (DEBUG_FAILOVER_MESSAGES)
4052 for (i
= 0; i
< count
; i
++) {
4053 /* 23 bytes plus nul, safe. */
4054 sprintf (tbuf
, " %d", bval
[i
]);
4055 failover_print (obuf
, obufix
, obufmax
, tbuf
);
4058 memcpy (&option
.data
[4], bval
, count
);
4061 /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
4062 terminated. Note that the caller should be careful not
4063 to provide a format and data that amount to more than 256
4064 bytes of data, since it will cause a fatal error. */
4065 case FT_TEXT_OR_BYTES
:
4067 #if defined (DEBUG_FAILOVER_MESSAGES)
4068 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
4069 * It is unclear what the effects of truncation here are, or
4070 * how that condition should be handled. It seems that this
4071 * function is used for formatting messages in the failover
4072 * command channel. For now the safest thing is for
4073 * overflow-truncation to cause a fatal log.
4075 if (snprintf (tbuf
, sizeof tbuf
, "\"%s\"", txt
) >= sizeof tbuf
)
4076 log_fatal ("dhcp_failover_make_option: tbuf overflow");
4077 failover_print (obuf
, obufix
, obufmax
, tbuf
);
4079 memcpy (&option
.data
[4], txt
, count
);
4084 option
.data
[4] = va_arg (va
, unsigned);
4086 option
.data
[5] = va_arg (va
, unsigned);
4087 bval
= va_arg (va
, u_int8_t
*);
4088 memcpy (&option
.data
[4 + count
], bval
, size
- count
- 4);
4089 #if defined (DEBUG_FAILOVER_MESSAGES)
4090 for (i
= 4; i
< size
; i
++) {
4091 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
4092 sprintf (tbuf
, " %d", option
.data
[i
]);
4093 failover_print (obuf
, obufix
, obufmax
, tbuf
);
4099 for (i
= 0; i
< count
; i
++) {
4100 val
= va_arg (va
, u_int32_t
);
4101 #if defined (DEBUG_FAILOVER_MESSAGES)
4102 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
4103 sprintf (tbuf
, " %d", val
);
4104 failover_print (obuf
, obufix
, obufmax
, tbuf
);
4106 putUShort (&option
.data
[4 + i
* 2], val
);
4115 #if defined DEBUG_FAILOVER_MESSAGES
4116 failover_print (obuf
, obufix
, obufmax
, ")");
4120 /* Now allocate a place to store what we just set up. */
4121 op
= dmalloc (sizeof (failover_option_t
), MDL
);
4123 dfree (option
.data
, MDL
);
4124 return &null_failover_option
;
4131 /* Send a failover message header. */
4133 isc_result_t
dhcp_failover_put_message (dhcp_failover_link_t
*link
,
4134 omapi_object_t
*connection
,
4135 int msg_type
, u_int32_t xid
, ...)
4141 failover_option_t
*option
;
4142 unsigned char *opbuf
;
4143 isc_result_t status
= ISC_R_SUCCESS
;
4147 /* Run through the argument list once to compute the length of
4148 the option portion of the message. */
4149 va_start (list
, xid
);
4150 while ((option
= va_arg (list
, failover_option_t
*))) {
4151 if (option
!= &skip_failover_option
)
4152 size
+= option
-> count
;
4153 if (option
== &null_failover_option
)
4158 /* Allocate an option buffer, unless we got an error. */
4159 if (!bad_option
&& size
) {
4160 opbuf
= dmalloc (size
, MDL
);
4162 status
= ISC_R_NOMEMORY
;
4164 opbuf
= (unsigned char *)0;
4166 va_start (list
, xid
);
4167 while ((option
= va_arg (list
, failover_option_t
*))) {
4168 if (option
== &skip_failover_option
)
4170 if (!bad_option
&& opbuf
)
4171 memcpy (&opbuf
[opix
],
4172 option
-> data
, option
-> count
);
4173 if (option
!= &null_failover_option
&&
4174 option
!= &skip_failover_option
) {
4175 opix
+= option
-> count
;
4176 dfree (option
-> data
, MDL
);
4177 dfree (option
, MDL
);
4183 return DHCP_R_INVALIDARG
;
4185 /* Now send the message header. */
4187 /* Message length. */
4188 status
= omapi_connection_put_uint16 (connection
, size
+ 12);
4189 if (status
!= ISC_R_SUCCESS
)
4194 status
= omapi_connection_copyin (connection
, &cbuf
, 1);
4195 if (status
!= ISC_R_SUCCESS
)
4198 /* Payload offset. */
4200 status
= omapi_connection_copyin (connection
, &cbuf
, 1);
4201 if (status
!= ISC_R_SUCCESS
)
4205 status
= omapi_connection_put_uint32 (connection
, (u_int32_t
)cur_time
);
4206 if (status
!= ISC_R_SUCCESS
)
4209 /* Transaction ID. */
4210 status
= omapi_connection_put_uint32(connection
, xid
);
4211 if (status
!= ISC_R_SUCCESS
)
4216 status
= omapi_connection_copyin (connection
, opbuf
, size
);
4217 if (status
!= ISC_R_SUCCESS
)
4221 if (link
-> state_object
&&
4222 link
-> state_object
-> link_to_peer
== link
) {
4223 #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
4224 log_info ("add_timeout +%d %s",
4225 (int)(link
-> state_object
->
4226 partner
.max_response_delay
) / 3,
4227 "dhcp_failover_send_contact");
4229 tv
. tv_sec
= cur_time
+
4230 (int)(link
-> state_object
->
4231 partner
.max_response_delay
) / 3;
4234 dhcp_failover_send_contact
, link
-> state_object
,
4235 (tvref_t
)dhcp_failover_state_reference
,
4236 (tvunref_t
)dhcp_failover_state_dereference
);
4243 log_info ("dhcp_failover_put_message: something went wrong.");
4244 omapi_disconnect (connection
, 1);
4248 void dhcp_failover_timeout (void *vstate
)
4250 dhcp_failover_state_t
*state
= vstate
;
4251 dhcp_failover_link_t
*link
;
4253 #if defined (DEBUG_FAILOVER_TIMING)
4254 log_info ("dhcp_failover_timeout");
4257 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
4259 link
= state
-> link_to_peer
;
4262 link
-> outer
-> type
!= omapi_type_connection
)
4265 log_error ("timeout waiting for failover peer %s", state
-> name
);
4267 /* If we haven't gotten a timely response, blow away the connection.
4268 This will cause the state to change automatically. */
4269 omapi_disconnect (link
-> outer
, 1);
4272 void dhcp_failover_send_contact (void *vstate
)
4274 dhcp_failover_state_t
*state
= vstate
;
4275 dhcp_failover_link_t
*link
;
4276 isc_result_t status
;
4278 #if defined(DEBUG_FAILOVER_MESSAGES) && \
4279 defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
4281 unsigned obufix
= 0;
4283 failover_print(obuf
, &obufix
, sizeof(obuf
), "(contact");
4286 #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
4287 log_info ("dhcp_failover_send_contact");
4290 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
4292 link
= state
-> link_to_peer
;
4295 link
-> outer
-> type
!= omapi_type_connection
)
4298 status
= (dhcp_failover_put_message
4299 (link
, link
-> outer
,
4300 FTM_CONTACT
, link
->xid
++,
4301 (failover_option_t
*)0));
4303 #if defined(DEBUG_FAILOVER_MESSAGES) && \
4304 defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
4305 if (status
!= ISC_R_SUCCESS
)
4306 failover_print(obuf
, &obufix
, sizeof(obuf
), " (failed)");
4307 failover_print(obuf
, &obufix
, sizeof(obuf
), ")");
4309 log_debug ("%s", obuf
);
4312 IGNORE_UNUSED(status
);
4317 isc_result_t
dhcp_failover_send_state (dhcp_failover_state_t
*state
)
4319 dhcp_failover_link_t
*link
;
4320 isc_result_t status
;
4322 #if defined (DEBUG_FAILOVER_MESSAGES)
4324 unsigned obufix
= 0;
4326 # define FMA obuf, &obufix, sizeof obuf
4327 failover_print (FMA
, "(state");
4329 # define FMA (char *)0, (unsigned *)0, 0
4332 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
4333 return DHCP_R_INVALIDARG
;
4334 link
= state
-> link_to_peer
;
4337 link
-> outer
-> type
!= omapi_type_connection
)
4338 return DHCP_R_INVALIDARG
;
4340 status
= (dhcp_failover_put_message
4341 (link
, link
-> outer
,
4342 FTM_STATE
, link
->xid
++,
4343 dhcp_failover_make_option (FTO_SERVER_STATE
, FMA
,
4344 (state
-> me
.state
== startup
4345 ? state
-> saved_state
4346 : state
-> me
.state
)),
4347 dhcp_failover_make_option
4348 (FTO_SERVER_FLAGS
, FMA
,
4349 (state
-> service_state
== service_startup
4350 ? FTF_SERVER_STARTUP
: 0)),
4351 dhcp_failover_make_option (FTO_STOS
, FMA
, state
-> me
.stos
),
4352 (failover_option_t
*)0));
4354 #if defined (DEBUG_FAILOVER_MESSAGES)
4355 if (status
!= ISC_R_SUCCESS
)
4356 failover_print (FMA
, " (failed)");
4357 failover_print (FMA
, ")");
4359 log_debug ("%s", obuf
);
4362 IGNORE_UNUSED(status
);
4364 return ISC_R_SUCCESS
;
4367 /* Send a connect message. */
4369 isc_result_t
dhcp_failover_send_connect (omapi_object_t
*l
)
4371 dhcp_failover_link_t
*link
;
4372 dhcp_failover_state_t
*state
;
4373 isc_result_t status
;
4374 #if defined (DEBUG_FAILOVER_MESSAGES)
4376 unsigned obufix
= 0;
4378 # define FMA obuf, &obufix, sizeof obuf
4379 failover_print (FMA
, "(connect");
4381 # define FMA (char *)0, (unsigned *)0, 0
4384 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
4385 return DHCP_R_INVALIDARG
;
4386 link
= (dhcp_failover_link_t
*)l
;
4387 state
= link
-> state_object
;
4388 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
4389 return DHCP_R_INVALIDARG
;
4392 (dhcp_failover_put_message
4394 FTM_CONNECT
, link
->xid
++,
4395 dhcp_failover_make_option(FTO_RELATIONSHIP_NAME
, FMA
,
4396 strlen(state
->name
), state
->name
),
4397 dhcp_failover_make_option (FTO_MAX_UNACKED
, FMA
,
4398 state
-> me
.max_flying_updates
),
4399 dhcp_failover_make_option (FTO_RECEIVE_TIMER
, FMA
,
4400 state
-> me
.max_response_delay
),
4401 dhcp_failover_option_printf(FTO_VENDOR_CLASS
, FMA
,
4402 "isc-%s", PACKAGE_VERSION
),
4403 dhcp_failover_make_option (FTO_PROTOCOL_VERSION
, FMA
,
4404 DHCP_FAILOVER_VERSION
),
4405 dhcp_failover_make_option (FTO_TLS_REQUEST
, FMA
,
4407 dhcp_failover_make_option (FTO_MCLT
, FMA
,
4410 ? dhcp_failover_make_option (FTO_HBA
, FMA
, 32, state
-> hba
)
4411 : &skip_failover_option
),
4412 (failover_option_t
*)0));
4414 #if defined (DEBUG_FAILOVER_MESSAGES)
4415 if (status
!= ISC_R_SUCCESS
)
4416 failover_print (FMA
, " (failed)");
4417 failover_print (FMA
, ")");
4419 log_debug ("%s", obuf
);
4425 isc_result_t
dhcp_failover_send_connectack (omapi_object_t
*l
,
4426 dhcp_failover_state_t
*state
,
4427 int reason
, const char *errmsg
)
4429 dhcp_failover_link_t
*link
;
4430 isc_result_t status
;
4431 #if defined (DEBUG_FAILOVER_MESSAGES)
4433 unsigned obufix
= 0;
4435 # define FMA obuf, &obufix, sizeof obuf
4436 failover_print (FMA
, "(connectack");
4438 # define FMA (char *)0, (unsigned *)0, 0
4441 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
4442 return DHCP_R_INVALIDARG
;
4443 link
= (dhcp_failover_link_t
*)l
;
4444 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
4445 return DHCP_R_INVALIDARG
;
4448 (dhcp_failover_put_message
4450 FTM_CONNECTACK
, link
->imsg
->xid
,
4452 ? dhcp_failover_make_option(FTO_RELATIONSHIP_NAME
, FMA
,
4453 strlen(state
->name
), state
->name
)
4454 : (link
->imsg
->options_present
& FTB_RELATIONSHIP_NAME
)
4455 ? dhcp_failover_make_option(FTO_RELATIONSHIP_NAME
, FMA
,
4456 link
->imsg
->relationship_name
.count
,
4457 link
->imsg
->relationship_name
.data
)
4458 : &skip_failover_option
,
4460 ? dhcp_failover_make_option (FTO_MAX_UNACKED
, FMA
,
4461 state
-> me
.max_flying_updates
)
4462 : &skip_failover_option
,
4464 ? dhcp_failover_make_option (FTO_RECEIVE_TIMER
, FMA
,
4465 state
-> me
.max_response_delay
)
4466 : &skip_failover_option
,
4467 dhcp_failover_option_printf(FTO_VENDOR_CLASS
, FMA
,
4468 "isc-%s", PACKAGE_VERSION
),
4469 dhcp_failover_make_option (FTO_PROTOCOL_VERSION
, FMA
,
4470 DHCP_FAILOVER_VERSION
),
4471 (link
->imsg
->options_present
& FTB_TLS_REQUEST
)
4472 ? dhcp_failover_make_option(FTO_TLS_REPLY
, FMA
,
4474 : &skip_failover_option
,
4476 ? dhcp_failover_make_option (FTO_REJECT_REASON
,
4478 : &skip_failover_option
,
4480 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4481 strlen (errmsg
), errmsg
)
4482 : &skip_failover_option
,
4483 (failover_option_t
*)0));
4485 #if defined (DEBUG_FAILOVER_MESSAGES)
4486 if (status
!= ISC_R_SUCCESS
)
4487 failover_print (FMA
, " (failed)");
4488 failover_print (FMA
, ")");
4490 log_debug ("%s", obuf
);
4496 isc_result_t
dhcp_failover_send_disconnect (omapi_object_t
*l
,
4498 const char *message
)
4500 dhcp_failover_link_t
*link
;
4501 isc_result_t status
;
4502 #if defined (DEBUG_FAILOVER_MESSAGES)
4504 unsigned obufix
= 0;
4506 # define FMA obuf, &obufix, sizeof obuf
4507 failover_print (FMA
, "(disconnect");
4509 # define FMA (char *)0, (unsigned *)0, 0
4512 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
4513 return DHCP_R_INVALIDARG
;
4514 link
= (dhcp_failover_link_t
*)l
;
4515 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
4516 return DHCP_R_INVALIDARG
;
4518 if (!message
&& reason
)
4519 message
= dhcp_failover_reject_reason_print (reason
);
4521 status
= (dhcp_failover_put_message
4523 FTM_DISCONNECT
, link
->xid
++,
4524 dhcp_failover_make_option (FTO_REJECT_REASON
,
4527 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4528 strlen (message
), message
)
4529 : &skip_failover_option
),
4530 (failover_option_t
*)0));
4532 #if defined (DEBUG_FAILOVER_MESSAGES)
4533 if (status
!= ISC_R_SUCCESS
)
4534 failover_print (FMA
, " (failed)");
4535 failover_print (FMA
, ")");
4537 log_debug ("%s", obuf
);
4543 /* Send a Bind Update message. */
4545 isc_result_t
dhcp_failover_send_bind_update (dhcp_failover_state_t
*state
,
4546 struct lease
*lease
)
4548 dhcp_failover_link_t
*link
;
4549 isc_result_t status
;
4551 binding_state_t transmit_state
;
4552 #if defined (DEBUG_FAILOVER_MESSAGES)
4554 unsigned obufix
= 0;
4556 # define FMA obuf, &obufix, sizeof obuf
4557 failover_print (FMA
, "(bndupd");
4559 # define FMA (char *)0, (unsigned *)0, 0
4562 if (!state
-> link_to_peer
||
4563 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4564 return DHCP_R_INVALIDARG
;
4565 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4567 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4568 return DHCP_R_INVALIDARG
;
4570 transmit_state
= lease
->desired_binding_state
;
4571 if (lease
->flags
& RESERVED_LEASE
) {
4572 /* If we are listing an allocable (not yet ACTIVE etc) lease
4573 * as reserved, toggle to the peer's 'free state', per the
4574 * draft. This gives the peer permission to alloc it to the
4575 * chaddr/uid-named client.
4577 if ((state
->i_am
== primary
) && (transmit_state
== FTS_FREE
))
4578 transmit_state
= FTS_BACKUP
;
4579 else if ((state
->i_am
== secondary
) &&
4580 (transmit_state
== FTS_BACKUP
))
4581 transmit_state
= FTS_FREE
;
4583 flags
|= FTF_IP_FLAG_RESERVE
;
4585 if (lease
->flags
& BOOTP_LEASE
)
4586 flags
|= FTF_IP_FLAG_BOOTP
;
4588 /* last_xid == 0 is illegal, seek past zero if we hit it. */
4592 lease
->last_xid
= link
->xid
++;
4595 * Our very next action is to transmit a binding update relating to
4596 * this lease over the wire, and although there is a BNDACK, there is
4597 * no BNDACKACK or BNDACKACKACK...the basic issue as we send a BNDUPD,
4598 * we may not receive a BNDACK. This non-reception does not imply the
4599 * peer did not receive and process the BNDUPD. So at this point, we
4600 * must divest any state that would be dangerous to retain under the
4601 * impression the peer has been updated. Normally state changes like
4602 * this are processed in supersede_lease(), but in this case we need a
4603 * very late binding.
4605 * In failover rules, a server is permitted to work forward in certain
4606 * directions from a given lease's state; active leases may be
4607 * extended, so forth. There is an 'optimization' in the failover
4608 * draft that permits a server to 'rewind' any work they have not
4609 * informed the peer. Since we can't know if the peer received our
4610 * update but was unable to acknowledge it, we make this change on
4611 * transmit rather than upon receiving the acknowledgement.
4613 * XXX: Frequent lease commits are undesirable. This should hopefully
4614 * only trigger when a server is sending a lease /state change/, and
4615 * not merely an update such as with a renewal.
4617 if (lease
->rewind_binding_state
!= lease
->binding_state
) {
4618 lease
->rewind_binding_state
= lease
->binding_state
;
4624 /* Send the update. */
4625 status
= (dhcp_failover_put_message
4626 (link
, link
-> outer
,
4627 FTM_BNDUPD
, lease
->last_xid
,
4628 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS
, FMA
,
4629 lease
-> ip_addr
.len
,
4630 lease
-> ip_addr
.iabuf
),
4631 dhcp_failover_make_option (FTO_BINDING_STATUS
, FMA
,
4632 lease
-> desired_binding_state
),
4634 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER
, FMA
,
4637 : &skip_failover_option
,
4638 lease
-> hardware_addr
.hlen
4639 ? dhcp_failover_make_option (FTO_CHADDR
, FMA
,
4640 lease
-> hardware_addr
.hlen
,
4641 lease
-> hardware_addr
.hbuf
)
4642 : &skip_failover_option
,
4643 dhcp_failover_make_option (FTO_LEASE_EXPIRY
, FMA
,
4645 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY
, FMA
,
4647 dhcp_failover_make_option (FTO_STOS
, FMA
,
4649 (lease
->cltt
!= 0) ?
4650 dhcp_failover_make_option(FTO_CLTT
, FMA
, lease
->cltt
) :
4651 &skip_failover_option
, /* No CLTT */
4652 flags
? dhcp_failover_make_option(FTO_IP_FLAGS
, FMA
,
4654 &skip_failover_option
, /* No IP_FLAGS */
4655 &skip_failover_option
, /* XXX DDNS */
4656 &skip_failover_option
, /* XXX request options */
4657 &skip_failover_option
, /* XXX reply options */
4658 (failover_option_t
*)0));
4660 #if defined (DEBUG_FAILOVER_MESSAGES)
4661 if (status
!= ISC_R_SUCCESS
)
4662 failover_print (FMA
, " (failed)");
4663 failover_print (FMA
, ")");
4665 log_debug ("%s", obuf
);
4671 /* Send a Bind ACK message. */
4673 isc_result_t
dhcp_failover_send_bind_ack (dhcp_failover_state_t
*state
,
4674 failover_message_t
*msg
,
4675 int reason
, const char *message
)
4677 dhcp_failover_link_t
*link
;
4678 isc_result_t status
;
4679 #if defined (DEBUG_FAILOVER_MESSAGES)
4681 unsigned obufix
= 0;
4683 # define FMA obuf, &obufix, sizeof obuf
4684 failover_print (FMA
, "(bndack");
4686 # define FMA (char *)0, (unsigned *)0, 0
4689 if (!state
-> link_to_peer
||
4690 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4691 return DHCP_R_INVALIDARG
;
4692 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4694 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4695 return DHCP_R_INVALIDARG
;
4697 if (!message
&& reason
)
4698 message
= dhcp_failover_reject_reason_print (reason
);
4700 /* Send the update. */
4701 status
= (dhcp_failover_put_message
4702 (link
, link
-> outer
,
4703 FTM_BNDACK
, msg
->xid
,
4704 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS
, FMA
,
4705 sizeof msg
-> assigned_addr
,
4706 &msg
-> assigned_addr
),
4707 #ifdef DO_BNDACK_SHOULD_NOT
4708 dhcp_failover_make_option (FTO_BINDING_STATUS
, FMA
,
4709 msg
-> binding_status
),
4710 (msg
-> options_present
& FTB_CLIENT_IDENTIFIER
)
4711 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER
, FMA
,
4712 msg
-> client_identifier
.count
,
4713 msg
-> client_identifier
.data
)
4714 : &skip_failover_option
,
4715 (msg
-> options_present
& FTB_CHADDR
)
4716 ? dhcp_failover_make_option (FTO_CHADDR
, FMA
,
4717 msg
-> chaddr
.count
,
4719 : &skip_failover_option
,
4720 dhcp_failover_make_option (FTO_LEASE_EXPIRY
, FMA
,
4722 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY
, FMA
,
4723 msg
-> potential_expiry
),
4724 dhcp_failover_make_option (FTO_STOS
, FMA
,
4726 (msg
->options_present
& FTB_CLTT
) ?
4727 dhcp_failover_make_option(FTO_CLTT
, FMA
, msg
->cltt
) :
4728 &skip_failover_option
, /* No CLTT in the msg to ack. */
4729 ((msg
->options_present
& FTB_IP_FLAGS
) && msg
->ip_flags
) ?
4730 dhcp_failover_make_option(FTO_IP_FLAGS
, FMA
,
4732 : &skip_failover_option
,
4733 #endif /* DO_BNDACK_SHOULD_NOT */
4735 ? dhcp_failover_make_option(FTO_REJECT_REASON
, FMA
, reason
)
4736 : &skip_failover_option
,
4738 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4739 strlen (message
), message
)
4740 : &skip_failover_option
,
4741 #ifdef DO_BNDACK_SHOULD_NOT
4742 &skip_failover_option
, /* XXX DDNS */
4743 &skip_failover_option
, /* XXX request options */
4744 &skip_failover_option
, /* XXX reply options */
4745 #endif /* DO_BNDACK_SHOULD_NOT */
4746 (failover_option_t
*)0));
4748 #if defined (DEBUG_FAILOVER_MESSAGES)
4749 if (status
!= ISC_R_SUCCESS
)
4750 failover_print (FMA
, " (failed)");
4751 failover_print (FMA
, ")");
4753 log_debug ("%s", obuf
);
4759 isc_result_t
dhcp_failover_send_poolreq (dhcp_failover_state_t
*state
)
4761 dhcp_failover_link_t
*link
;
4762 isc_result_t status
;
4763 #if defined (DEBUG_FAILOVER_MESSAGES)
4765 unsigned obufix
= 0;
4767 # define FMA obuf, &obufix, sizeof obuf
4768 failover_print (FMA
, "(poolreq");
4770 # define FMA (char *)0, (unsigned *)0, 0
4773 if (!state
-> link_to_peer
||
4774 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4775 return DHCP_R_INVALIDARG
;
4776 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4778 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4779 return DHCP_R_INVALIDARG
;
4781 status
= (dhcp_failover_put_message
4782 (link
, link
-> outer
,
4783 FTM_POOLREQ
, link
->xid
++,
4784 (failover_option_t
*)0));
4786 #if defined (DEBUG_FAILOVER_MESSAGES)
4787 if (status
!= ISC_R_SUCCESS
)
4788 failover_print (FMA
, " (failed)");
4789 failover_print (FMA
, ")");
4791 log_debug ("%s", obuf
);
4797 isc_result_t
dhcp_failover_send_poolresp (dhcp_failover_state_t
*state
,
4800 dhcp_failover_link_t
*link
;
4801 isc_result_t status
;
4802 #if defined (DEBUG_FAILOVER_MESSAGES)
4804 unsigned obufix
= 0;
4806 # define FMA obuf, &obufix, sizeof obuf
4807 failover_print (FMA
, "(poolresp");
4809 # define FMA (char *)0, (unsigned *)0, 0
4812 if (!state
-> link_to_peer
||
4813 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4814 return DHCP_R_INVALIDARG
;
4815 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4817 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4818 return DHCP_R_INVALIDARG
;
4820 status
= (dhcp_failover_put_message
4821 (link
, link
-> outer
,
4822 FTM_POOLRESP
, link
->imsg
->xid
,
4823 dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED
, FMA
,
4825 (failover_option_t
*)0));
4827 #if defined (DEBUG_FAILOVER_MESSAGES)
4828 if (status
!= ISC_R_SUCCESS
)
4829 failover_print (FMA
, " (failed)");
4830 failover_print (FMA
, ")");
4832 log_debug ("%s", obuf
);
4838 isc_result_t
dhcp_failover_send_update_request (dhcp_failover_state_t
*state
)
4840 dhcp_failover_link_t
*link
;
4841 isc_result_t status
;
4842 #if defined (DEBUG_FAILOVER_MESSAGES)
4844 unsigned obufix
= 0;
4846 # define FMA obuf, &obufix, sizeof obuf
4847 failover_print (FMA
, "(updreq");
4849 # define FMA (char *)0, (unsigned *)0, 0
4852 if (!state
-> link_to_peer
||
4853 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4854 return DHCP_R_INVALIDARG
;
4855 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4857 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4858 return DHCP_R_INVALIDARG
;
4860 if (state
-> curUPD
)
4861 return ISC_R_ALREADYRUNNING
;
4863 status
= (dhcp_failover_put_message
4864 (link
, link
-> outer
,
4865 FTM_UPDREQ
, link
->xid
++,
4866 (failover_option_t
*)0));
4868 if (status
== ISC_R_SUCCESS
)
4869 state
-> curUPD
= FTM_UPDREQ
;
4871 #if defined (DEBUG_FAILOVER_MESSAGES)
4872 if (status
!= ISC_R_SUCCESS
)
4873 failover_print (FMA
, " (failed)");
4874 failover_print (FMA
, ")");
4876 log_debug ("%s", obuf
);
4879 log_info ("Sent update request message to %s", state
-> name
);
4883 isc_result_t
dhcp_failover_send_update_request_all (dhcp_failover_state_t
4886 dhcp_failover_link_t
*link
;
4887 isc_result_t status
;
4888 #if defined (DEBUG_FAILOVER_MESSAGES)
4890 unsigned obufix
= 0;
4892 # define FMA obuf, &obufix, sizeof obuf
4893 failover_print (FMA
, "(updreqall");
4895 # define FMA (char *)0, (unsigned *)0, 0
4898 if (!state
-> link_to_peer
||
4899 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4900 return DHCP_R_INVALIDARG
;
4901 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4903 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4904 return DHCP_R_INVALIDARG
;
4906 /* If there is an UPDREQ in progress, then upgrade to UPDREQALL. */
4907 if (state
-> curUPD
&& (state
-> curUPD
!= FTM_UPDREQ
))
4908 return ISC_R_ALREADYRUNNING
;
4910 status
= (dhcp_failover_put_message
4911 (link
, link
-> outer
,
4912 FTM_UPDREQALL
, link
->xid
++,
4913 (failover_option_t
*)0));
4915 if (status
== ISC_R_SUCCESS
)
4916 state
-> curUPD
= FTM_UPDREQALL
;
4918 #if defined (DEBUG_FAILOVER_MESSAGES)
4919 if (status
!= ISC_R_SUCCESS
)
4920 failover_print (FMA
, " (failed)");
4921 failover_print (FMA
, ")");
4923 log_debug ("%s", obuf
);
4926 log_info ("Sent update request all message to %s", state
-> name
);
4930 isc_result_t
dhcp_failover_send_update_done (dhcp_failover_state_t
*state
)
4932 dhcp_failover_link_t
*link
;
4933 isc_result_t status
;
4934 #if defined (DEBUG_FAILOVER_MESSAGES)
4936 unsigned obufix
= 0;
4938 # define FMA obuf, &obufix, sizeof obuf
4939 failover_print (FMA
, "(upddone");
4941 # define FMA (char *)0, (unsigned *)0, 0
4944 if (!state
-> link_to_peer
||
4945 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4946 return DHCP_R_INVALIDARG
;
4947 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4949 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4950 return DHCP_R_INVALIDARG
;
4952 status
= (dhcp_failover_put_message
4953 (link
, link
-> outer
,
4954 FTM_UPDDONE
, state
->updxid
,
4955 (failover_option_t
*)0));
4957 #if defined (DEBUG_FAILOVER_MESSAGES)
4958 if (status
!= ISC_R_SUCCESS
)
4959 failover_print (FMA
, " (failed)");
4960 failover_print (FMA
, ")");
4962 log_debug ("%s", obuf
);
4966 log_info ("Sent update done message to %s", state
-> name
);
4968 state
->updxid
--; /* Paranoia, just so it mismatches. */
4970 /* There may be uncommitted leases at this point (since
4971 dhcp_failover_process_bind_ack() doesn't commit leases);
4972 commit the lease file. */
4979 * failover_lease_is_better() compares the binding update in 'msg' with
4980 * the current lease in 'lease'. If the determination is that the binding
4981 * update shouldn't be allowed to update/crush more critical binding info
4982 * on the lease, the lease is preferred. A value of true is returned if the
4983 * local lease is preferred, or false if the remote binding update is
4986 * For now this function is hopefully simplistic and trivial. It may be that
4987 * a more detailed system of preferences is required, so this is something we
4988 * should monitor as we gain experience with these dueling events.
4990 static isc_boolean_t
4991 failover_lease_is_better(dhcp_failover_state_t
*state
, struct lease
*lease
,
4992 failover_message_t
*msg
)
4994 binding_state_t local_state
;
4997 if (lease
->binding_state
!= lease
->desired_binding_state
)
4998 local_state
= lease
->desired_binding_state
;
5000 local_state
= lease
->binding_state
;
5002 if ((msg
->options_present
& FTB_CLTT
) != 0)
5003 msg_cltt
= msg
->cltt
;
5007 switch(local_state
) {
5009 if (msg
->binding_status
== FTS_ACTIVE
) {
5010 if (msg_cltt
< lease
->cltt
)
5012 else if (msg_cltt
> lease
->cltt
)
5014 else if (state
->i_am
== primary
)
5018 } else if (msg
->binding_status
== FTS_EXPIRED
) {
5029 if (msg
->binding_status
== FTS_ACTIVE
)
5031 else if (state
->i_am
== primary
)
5035 /* FALL THROUGH to impossible condition */
5038 log_fatal("Impossible condition at %s:%d.", MDL
);
5041 log_fatal("Impossible condition at %s:%d.", MDL
);
5042 /* Silence compiler warning. */
5046 isc_result_t
dhcp_failover_process_bind_update (dhcp_failover_state_t
*state
,
5047 failover_message_t
*msg
)
5049 struct lease
*lt
= NULL
, *lease
= NULL
;
5051 int reason
= FTR_MISC_REJECT
;
5052 const char *message
;
5053 int new_binding_state
;
5054 int send_to_backup
= 0;
5055 int required_options
;
5056 isc_boolean_t chaddr_changed
= ISC_FALSE
;
5057 isc_boolean_t ident_changed
= ISC_FALSE
;
5059 /* Validate the binding update. */
5060 required_options
= FTB_ASSIGNED_IP_ADDRESS
| FTB_BINDING_STATUS
;
5061 if ((msg
->options_present
& required_options
) != required_options
) {
5062 message
= "binding update lacks required options";
5063 reason
= FTR_MISSING_BINDINFO
;
5067 ia
.len
= sizeof msg
-> assigned_addr
;
5068 memcpy (ia
.iabuf
, &msg
-> assigned_addr
, ia
.len
);
5070 if (!find_lease_by_ip_addr (&lease
, ia
, MDL
)) {
5071 message
= "unknown IP address";
5072 reason
= FTR_ILLEGAL_IP_ADDR
;
5077 * If this lease is covered by a different failover peering
5078 * relationship, assert an error.
5080 if ((lease
->pool
== NULL
) || (lease
->pool
->failover_peer
== NULL
) ||
5081 (lease
->pool
->failover_peer
!= state
)) {
5082 message
= "IP address is covered by a different failover "
5083 "relationship state";
5084 reason
= FTR_ILLEGAL_IP_ADDR
;
5089 * Dueling updates: This happens when both servers send a BNDUPD
5090 * at the same time. We want the best update to win, which means
5091 * we reject if we think ours is better, or cancel if we think the
5092 * peer's is better. We only assert a problem if the lease is on
5093 * the ACK queue, not on the UPDATE queue. This means that after
5094 * accepting this server's BNDUPD, we will send our own BNDUPD
5095 * /after/ sending the BNDACK (this order was recently enforced in
5096 * queue processing).
5098 if ((lease
->flags
& ON_ACK_QUEUE
) != 0) {
5099 if (failover_lease_is_better(state
, lease
, msg
)) {
5100 message
= "incoming update is less critical than "
5102 reason
= FTR_LESS_CRIT_BIND_INFO
;
5105 /* This makes it so we ignore any spurious ACKs. */
5106 dhcp_failover_ack_queue_remove(state
, lease
);
5110 /* Install the new info. Start by taking a copy to markup. */
5111 if (!lease_copy (<
, lease
, MDL
)) {
5112 message
= "no memory";
5116 if (msg
-> options_present
& FTB_CHADDR
) {
5117 if (msg
->binding_status
== FTS_ABANDONED
) {
5118 message
= "BNDUPD to ABANDONED with a CHADDR";
5121 if (msg
-> chaddr
.count
> sizeof lt
-> hardware_addr
.hbuf
) {
5122 message
= "chaddr too long";
5126 if ((lt
->hardware_addr
.hlen
!= msg
->chaddr
.count
) ||
5127 (memcmp(lt
->hardware_addr
.hbuf
, msg
->chaddr
.data
,
5128 msg
->chaddr
.count
) != 0))
5129 chaddr_changed
= ISC_TRUE
;
5131 lt
-> hardware_addr
.hlen
= msg
-> chaddr
.count
;
5132 memcpy (lt
-> hardware_addr
.hbuf
, msg
-> chaddr
.data
,
5133 msg
-> chaddr
.count
);
5134 } else if (msg
->binding_status
== FTS_ACTIVE
||
5135 msg
->binding_status
== FTS_EXPIRED
||
5136 msg
->binding_status
== FTS_RELEASED
) {
5137 message
= "BNDUPD without CHADDR";
5138 reason
= FTR_MISSING_BINDINFO
;
5140 } else if (msg
->binding_status
== FTS_ABANDONED
) {
5141 chaddr_changed
= ISC_TRUE
;
5142 lt
->hardware_addr
.hlen
= 0;
5144 binding_scope_dereference(<
->scope
, MDL
);
5147 /* There is no explicit message content to indicate that the client
5148 * supplied no client-identifier. So if we don't hear of a value,
5149 * we discard the last one.
5151 if (msg
->options_present
& FTB_CLIENT_IDENTIFIER
) {
5152 if (msg
->binding_status
== FTS_ABANDONED
) {
5153 message
= "BNDUPD to ABANDONED with client-id";
5157 if ((lt
->uid_len
!= msg
->client_identifier
.count
) ||
5158 (lt
->uid
== NULL
) || /* Sanity; should never happen. */
5159 (memcmp(lt
->uid
, msg
->client_identifier
.data
,
5161 ident_changed
= ISC_TRUE
;
5163 lt
->uid_len
= msg
->client_identifier
.count
;
5165 /* Allocate the lt->uid buffer if we haven't already, or
5166 * re-allocate the lt-uid buffer if we have one that is not
5167 * large enough. Otherwise, just use the extant buffer.
5169 if (!lt
->uid
|| lt
->uid
== lt
->uid_buf
||
5170 lt
->uid_len
> lt
->uid_max
) {
5171 if (lt
->uid
&& lt
->uid
!= lt
->uid_buf
)
5172 dfree(lt
->uid
, MDL
);
5174 if (lt
->uid_len
> sizeof(lt
->uid_buf
)) {
5175 lt
->uid_max
= lt
->uid_len
;
5176 lt
->uid
= dmalloc(lt
->uid_len
, MDL
);
5178 message
= "no memory";
5182 lt
->uid_max
= sizeof(lt
->uid_buf
);
5183 lt
->uid
= lt
->uid_buf
;
5187 msg
-> client_identifier
.data
, lt
-> uid_len
);
5188 } else if (lt
->uid
&& msg
->binding_status
!= FTS_RESET
&&
5189 msg
->binding_status
!= FTS_FREE
&&
5190 msg
->binding_status
!= FTS_BACKUP
) {
5191 ident_changed
= ISC_TRUE
;
5192 if (lt
->uid
!= lt
->uid_buf
)
5193 dfree (lt
->uid
, MDL
);
5195 lt
->uid_max
= lt
->uid_len
= 0;
5199 * A server's configuration can assign a 'binding scope';
5201 * set var = "value";
5203 * The problem with these binding scopes is that they are refreshed
5204 * when the server processes a client's DHCP packet. A local binding
5205 * scope is trash, then, when the lease has been assigned by the
5206 * partner server. There is no real way to detect this, a peer may
5207 * be updating us (as through potential conflict) with a binding we
5208 * sent them, but we can trivially detect the /problematic/ case;
5211 * primary allocates lease to client A, assigns ddns name A.
5213 * secondary enters partner down.
5214 * lease expires, and is set free.
5215 * lease is allocated to client B and given ddns name B.
5218 * The binding update in this case will be active->active, but the
5219 * client identification on the lease will have changed. The ddns
5220 * update on client A will have leaked if we just remove the binding
5223 if (msg
->binding_status
== FTS_ACTIVE
&&
5224 (chaddr_changed
|| ident_changed
)) {
5225 (void) ddns_removals(lease
, NULL
, NULL
, ISC_FALSE
);
5227 if (lease
->scope
!= NULL
)
5228 binding_scope_dereference(&lease
->scope
, MDL
);
5231 /* XXX Times may need to be adjusted based on clock skew! */
5232 if (msg
-> options_present
& FTB_STOS
) {
5233 lt
-> starts
= msg
-> stos
;
5235 if (msg
-> options_present
& FTB_LEASE_EXPIRY
) {
5236 lt
-> ends
= msg
-> expiry
;
5238 if (msg
->options_present
& FTB_POTENTIAL_EXPIRY
) {
5239 lt
->atsfp
= lt
->tsfp
= msg
->potential_expiry
;
5241 if (msg
->options_present
& FTB_IP_FLAGS
) {
5242 if (msg
->ip_flags
& FTF_IP_FLAG_RESERVE
) {
5243 if ((((state
->i_am
== primary
) &&
5244 (lease
->binding_state
== FTS_FREE
)) ||
5245 ((state
->i_am
== secondary
) &&
5246 (lease
->binding_state
== FTS_BACKUP
))) &&
5247 !(lease
->flags
& RESERVED_LEASE
)) {
5248 message
= "Address is not reserved.";
5249 reason
= FTR_IP_NOT_RESERVED
;
5253 lt
->flags
|= RESERVED_LEASE
;
5255 lt
->flags
&= ~RESERVED_LEASE
;
5257 if (msg
->ip_flags
& FTF_IP_FLAG_BOOTP
) {
5258 if ((((state
->i_am
== primary
) &&
5259 (lease
->binding_state
== FTS_FREE
)) ||
5260 ((state
->i_am
== secondary
) &&
5261 (lease
->binding_state
== FTS_BACKUP
))) &&
5262 !(lease
->flags
& BOOTP_LEASE
)) {
5263 message
= "Address is not allocated to BOOTP.";
5266 lt
->flags
|= BOOTP_LEASE
;
5268 lt
->flags
&= ~BOOTP_LEASE
;
5270 if (msg
->ip_flags
& ~(FTF_IP_FLAG_RESERVE
| FTF_IP_FLAG_BOOTP
))
5271 log_info("Unknown IP-flags set in BNDUPD (0x%x).",
5273 } else /* Flags may only not appear if the values are zero. */
5274 lt
->flags
&= ~(RESERVED_LEASE
| BOOTP_LEASE
);
5276 #if defined (DEBUG_LEASE_STATE_TRANSITIONS)
5277 log_info ("processing state transition for %s: %s to %s",
5278 piaddr (lease
-> ip_addr
),
5279 binding_state_print (lease
-> binding_state
),
5280 binding_state_print (msg
-> binding_status
));
5283 /* If we're in normal state, make sure the state transition
5285 if (state
-> me
.state
== normal
) {
5287 (normal_binding_state_transition_check
5288 (lease
, state
, msg
-> binding_status
,
5289 msg
-> potential_expiry
));
5290 /* XXX if the transition the peer asked for isn't
5291 XXX allowed, maybe we should make the transition
5292 XXX into potential-conflict at this point. */
5295 (conflict_binding_state_transition_check
5296 (lease
, state
, msg
-> binding_status
,
5297 msg
-> potential_expiry
));
5299 if (new_binding_state
!= msg
-> binding_status
) {
5302 if (snprintf (outbuf
, sizeof outbuf
,
5303 "%s: invalid state transition: %s to %s",
5304 piaddr (lease
-> ip_addr
),
5305 binding_state_print (lease
-> binding_state
),
5306 binding_state_print (msg
-> binding_status
))
5308 log_fatal ("%s: impossible outbuf overflow",
5309 "dhcp_failover_process_bind_update");
5311 dhcp_failover_send_bind_ack (state
, msg
,
5316 if (new_binding_state
== FTS_EXPIRED
||
5317 new_binding_state
== FTS_RELEASED
||
5318 new_binding_state
== FTS_RESET
) {
5319 lt
-> next_binding_state
= FTS_FREE
;
5321 /* Mac address affinity. Assign the lease to
5322 * BACKUP state if we are the primary and the
5323 * peer is more likely to reallocate this lease
5324 * to a returning client.
5326 if ((state
->i_am
== primary
) &&
5327 !(lt
->flags
& (RESERVED_LEASE
| BOOTP_LEASE
)))
5328 send_to_backup
= peer_wants_lease(lt
);
5330 lt
-> next_binding_state
= new_binding_state
;
5332 msg
-> binding_status
= lt
-> next_binding_state
;
5335 * If we accept a peer's binding update, then we can't rewind a
5336 * lease behind the peer's state.
5338 lease
->rewind_binding_state
= lt
->next_binding_state
;
5340 /* Try to install the new information. */
5341 if (!supersede_lease (lease
, lt
, 0, 0, 0) ||
5342 !write_lease (lease
)) {
5343 message
= "database update failed";
5345 dhcp_failover_send_bind_ack (state
, msg
, reason
, message
);
5348 dhcp_failover_queue_ack (state
, msg
);
5351 /* If it is probably wise, assign lease to backup state if the peer
5352 * is not already hoarding leases.
5354 if (send_to_backup
&& secondary_not_hoarding(state
, lease
->pool
)) {
5355 lease
->next_binding_state
= FTS_BACKUP
;
5356 lease
->tstp
= cur_time
;
5357 lease
->starts
= cur_time
;
5359 if (!supersede_lease(lease
, NULL
, 0, 1, 0) ||
5360 !write_lease(lease
))
5361 log_error("can't commit lease %s for mac addr "
5362 "affinity", piaddr(lease
->ip_addr
));
5364 dhcp_failover_send_updates(state
);
5369 lease_dereference (<
, MDL
);
5371 lease_dereference (&lease
, MDL
);
5373 return ISC_R_SUCCESS
;
5376 /* This was hairy enough I didn't want to do it all in an if statement.
5378 * Returns: Truth is the secondary is allowed to get more leases based upon
5379 * MAC address affinity. False otherwise.
5382 secondary_not_hoarding(dhcp_failover_state_t
*state
, struct pool
*p
) {
5387 total
= p
->free_leases
+ p
->backup_leases
;
5389 /* How many leases is one side or the other allowed to "hold"? */
5390 hold
= ((total
* state
->max_lease_ownership
) + 50) / 100;
5392 /* If we were to send leases (or if the secondary were to send us
5393 * leases in the negative direction), how many would that be?
5395 lts
= (p
->free_leases
- p
->backup_leases
) / 2;
5397 /* The peer is not hoarding leases if we would send them more leases
5398 * (or they would take fewer leases) than the maximum they are allowed
5399 * to hold (the negative hold).
5401 return(lts
> -hold
);
5404 isc_result_t
dhcp_failover_process_bind_ack (dhcp_failover_state_t
*state
,
5405 failover_message_t
*msg
)
5407 struct lease
*lt
= (struct lease
*)0;
5408 struct lease
*lease
= (struct lease
*)0;
5410 const char *message
= "no memory";
5411 u_int32_t pot_expire
;
5412 int send_to_backup
= ISC_FALSE
;
5415 ia
.len
= sizeof msg
-> assigned_addr
;
5416 memcpy (ia
.iabuf
, &msg
-> assigned_addr
, ia
.len
);
5418 if (!find_lease_by_ip_addr (&lease
, ia
, MDL
)) {
5419 message
= "no such lease";
5423 /* XXX check for conflicts. */
5424 if (msg
-> options_present
& FTB_REJECT_REASON
) {
5425 log_error ("bind update on %s from %s rejected: %.*s",
5426 piaddr (ia
), state
-> name
,
5427 (int)((msg
-> options_present
& FTB_MESSAGE
)
5428 ? msg
-> message
.count
5429 : strlen (dhcp_failover_reject_reason_print
5430 (msg
-> reject_reason
))),
5431 (msg
-> options_present
& FTB_MESSAGE
)
5432 ? (const char *)(msg
-> message
.data
)
5433 : (dhcp_failover_reject_reason_print
5434 (msg
-> reject_reason
)));
5438 /* Silently discard acks for leases we did not update (or multiple
5441 if (!lease
->last_xid
)
5444 if (lease
->last_xid
!= msg
->xid
) {
5445 message
= "xid mismatch";
5449 /* XXX Times may need to be adjusted based on clock skew! */
5450 if (msg
->options_present
& FTO_POTENTIAL_EXPIRY
)
5451 pot_expire
= msg
->potential_expiry
;
5453 pot_expire
= lease
->tstp
;
5455 /* If the lease was desired to enter a binding state, we set
5456 * such a value upon transmitting a bndupd. We do not clear it
5457 * if we receive a bndupd in the meantime (or change the state
5458 * of the lease again ourselves), but we do set binding_state
5459 * if we get a bndupd.
5461 * So desired_binding_state tells us what we sent a bndupd for,
5462 * and binding_state tells us what we have since determined in
5465 if (lease
->desired_binding_state
== FTS_EXPIRED
||
5466 lease
->desired_binding_state
== FTS_RESET
||
5467 lease
->desired_binding_state
== FTS_RELEASED
)
5469 /* It is not a problem to do this directly as we call
5470 * supersede_lease immediately after: the lease is requeued
5471 * even if its sort order (tsfp) has changed.
5473 lease
->atsfp
= lease
->tsfp
= pot_expire
;
5474 if ((state
->i_am
== secondary
) &&
5475 (lease
->flags
& RESERVED_LEASE
))
5476 lease
->next_binding_state
= FTS_BACKUP
;
5478 lease
->next_binding_state
= FTS_FREE
;
5480 /* Clear this condition for the next go-round. */
5481 lease
->desired_binding_state
= lease
->next_binding_state
;
5483 /* The peer will have made this state change, so set rewind. */
5484 lease
->rewind_binding_state
= lease
->next_binding_state
;
5486 supersede_lease(lease
, (struct lease
*)0, 0, 0, 0);
5489 /* Lease has returned to FREE state from the
5490 * transitional states. If the lease 'belongs'
5491 * to a client that would be served by the
5492 * peer, process a binding update now to send
5493 * the lease to backup state. But not if we
5494 * think we already have.
5496 if (state
->i_am
== primary
&&
5497 !(lease
->flags
& (RESERVED_LEASE
| BOOTP_LEASE
)) &&
5498 peer_wants_lease(lease
))
5499 send_to_backup
= ISC_TRUE
;
5501 if (!send_to_backup
&& state
->me
.state
== normal
)
5504 /* XXX It could be a problem to do this directly if the lease
5505 * XXX is sorted by tsfp.
5507 lease
->atsfp
= lease
->tsfp
= pot_expire
;
5508 if (lease
->desired_binding_state
!= lease
->binding_state
) {
5509 lease
->next_binding_state
=
5510 lease
->desired_binding_state
;
5511 supersede_lease(lease
,
5512 (struct lease
*)0, 0, 0, 0);
5515 /* Commit the lease only after a two-second timeout,
5516 so that if we get a bunch of acks in quick
5517 succession (e.g., when stealing leases from the
5518 secondary), we do not do an immediate commit for
5520 tv
.tv_sec
= cur_time
+ 2;
5522 add_timeout(&tv
, commit_leases_timeout
, (void *)0, 0, 0);
5526 dhcp_failover_ack_queue_remove (state
, lease
);
5528 /* If we are supposed to send an update done after we send
5529 this lease, go ahead and send it. */
5530 if (state
-> send_update_done
== lease
) {
5531 lease_dereference (&state
-> send_update_done
, MDL
);
5532 dhcp_failover_send_update_done (state
);
5535 /* Now that the lease is off the ack queue, consider putting it
5536 * back on the update queue for mac address affinity.
5538 if (send_to_backup
&& secondary_not_hoarding(state
, lease
->pool
)) {
5539 lease
->next_binding_state
= FTS_BACKUP
;
5540 lease
->tstp
= lease
->starts
= cur_time
;
5542 if (!supersede_lease(lease
, NULL
, 0, 1, 0) ||
5543 !write_lease(lease
))
5544 log_error("can't commit lease %s for "
5545 "client affinity", piaddr(lease
->ip_addr
));
5547 if (state
->me
.state
== normal
)
5551 /* If there are updates pending, we've created space to send at
5553 dhcp_failover_send_updates (state
);
5556 lease_dereference (&lease
, MDL
);
5558 lease_dereference (<
, MDL
);
5560 return ISC_R_SUCCESS
;
5563 log_info ("bind update on %s got ack from %s: %s.",
5564 piaddr (ia
), state
-> name
, message
);
5568 isc_result_t
dhcp_failover_generate_update_queue (dhcp_failover_state_t
*state
,
5571 struct shared_network
*s
;
5575 #define FREE_LEASES 0
5576 #define ACTIVE_LEASES 1
5577 #define EXPIRED_LEASES 2
5578 #define ABANDONED_LEASES 3
5579 #define BACKUP_LEASES 4
5580 #define RESERVED_LEASES 5
5581 struct lease
**lptr
[RESERVED_LEASES
+1];
5583 /* Loop through each pool in each shared network and call the
5584 expiry routine on the pool. */
5585 for (s
= shared_networks
; s
; s
= s
-> next
) {
5586 for (p
= s
-> pools
; p
; p
= p
-> next
) {
5587 if (p
->failover_peer
!= state
)
5590 lptr
[FREE_LEASES
] = &p
->free
;
5591 lptr
[ACTIVE_LEASES
] = &p
->active
;
5592 lptr
[EXPIRED_LEASES
] = &p
->expired
;
5593 lptr
[ABANDONED_LEASES
] = &p
->abandoned
;
5594 lptr
[BACKUP_LEASES
] = &p
->backup
;
5595 lptr
[RESERVED_LEASES
] = &p
->reserved
;
5597 for (i
= FREE_LEASES
; i
<= RESERVED_LEASES
; i
++) {
5598 for (l
= *(lptr
[i
]); l
; l
= l
-> next
) {
5599 if ((l
->flags
& ON_QUEUE
) == 0 &&
5601 (l
->tstp
> l
->atsfp
) ||
5602 (i
== EXPIRED_LEASES
))) {
5603 l
-> desired_binding_state
= l
-> binding_state
;
5604 dhcp_failover_queue_update (l
, 0);
5610 return ISC_R_SUCCESS
;
5614 dhcp_failover_process_update_request (dhcp_failover_state_t
*state
,
5615 failover_message_t
*msg
)
5617 if (state
->send_update_done
) {
5618 log_info("Received update request while old update still "
5619 "flying! Silently discarding old request.");
5620 lease_dereference(&state
->send_update_done
, MDL
);
5623 /* Generate a fresh update queue. */
5624 dhcp_failover_generate_update_queue (state
, 0);
5626 state
->updxid
= msg
->xid
;
5628 /* If there's anything on the update queue (there shouldn't be
5629 anything on the ack queue), trigger an update done message
5630 when we get an ack for that lease. */
5631 if (state
-> update_queue_tail
) {
5632 lease_reference (&state
-> send_update_done
,
5633 state
-> update_queue_tail
, MDL
);
5634 dhcp_failover_send_updates (state
);
5635 log_info ("Update request from %s: sending update",
5638 /* Otherwise, there are no updates to send, so we can
5639 just send an UPDDONE message immediately. */
5640 dhcp_failover_send_update_done (state
);
5641 log_info ("Update request from %s: nothing pending",
5645 return ISC_R_SUCCESS
;
5649 dhcp_failover_process_update_request_all (dhcp_failover_state_t
*state
,
5650 failover_message_t
*msg
)
5652 if (state
->send_update_done
) {
5653 log_info("Received update request while old update still "
5654 "flying! Silently discarding old request.");
5655 lease_dereference(&state
->send_update_done
, MDL
);
5658 /* Generate a fresh update queue that includes every lease. */
5659 dhcp_failover_generate_update_queue (state
, 1);
5661 state
->updxid
= msg
->xid
;
5663 if (state
-> update_queue_tail
) {
5664 lease_reference (&state
-> send_update_done
,
5665 state
-> update_queue_tail
, MDL
);
5666 dhcp_failover_send_updates (state
);
5667 log_info ("Update request all from %s: sending update",
5670 /* This should really never happen, but it could happen
5671 on a server that currently has no leases configured. */
5672 dhcp_failover_send_update_done (state
);
5673 log_info ("Update request all from %s: nothing pending",
5677 return ISC_R_SUCCESS
;
5681 dhcp_failover_process_update_done (dhcp_failover_state_t
*state
,
5682 failover_message_t
*msg
)
5686 log_info ("failover peer %s: peer update completed.",
5689 state
-> curUPD
= 0;
5691 switch (state
-> me
.state
) {
5695 case communications_interrupted
:
5696 case resolution_interrupted
:
5702 break; /* shouldn't happen. */
5704 /* We got the UPDDONE, so we can go into normal state! */
5705 case potential_conflict
:
5706 if (state
->partner
.state
== conflict_done
) {
5707 if (state
->i_am
== secondary
) {
5708 dhcp_failover_set_state (state
, normal
);
5710 log_error("Secondary is in conflict_done "
5711 "state after conflict resolution, "
5712 "this is illegal.");
5713 dhcp_failover_set_state (state
, shut_down
);
5716 if (state
->i_am
== primary
)
5717 dhcp_failover_set_state (state
, conflict_done
);
5719 log_error("Spurious update-done message.");
5725 log_error("Spurious update-done message.");
5729 /* Wait for MCLT to expire before moving to recover_done,
5730 except that if both peers come up in recover, there is
5731 no point in waiting for MCLT to expire - this probably
5732 indicates the initial startup of a newly-configured
5734 if (state
-> me
.stos
+ state
-> mclt
> cur_time
&&
5735 state
-> partner
.state
!= recover
&&
5736 state
-> partner
.state
!= recover_done
) {
5737 dhcp_failover_set_state (state
, recover_wait
);
5738 #if defined (DEBUG_FAILOVER_TIMING)
5739 log_info ("add_timeout +%d %s",
5741 state
-> me
.stos
+ state
-> mclt
),
5742 "dhcp_failover_recover_done");
5744 tv
. tv_sec
= (int)(state
-> me
.stos
+ state
-> mclt
);
5747 dhcp_failover_recover_done
,
5749 (tvref_t
)omapi_object_reference
,
5751 omapi_object_dereference
);
5753 dhcp_failover_recover_done (state
);
5756 return ISC_R_SUCCESS
;
5759 void dhcp_failover_recover_done (void *sp
)
5761 dhcp_failover_state_t
*state
= sp
;
5763 #if defined (DEBUG_FAILOVER_TIMING)
5764 log_info ("dhcp_failover_recover_done");
5767 dhcp_failover_set_state (state
, recover_done
);
5770 #if defined (DEBUG_FAILOVER_MESSAGES)
5771 /* Print hunks of failover messages, doing line breaks as appropriate.
5772 Note that this assumes syslog is being used, rather than, e.g., the
5773 Windows NT logging facility, where just dumping the whole message in
5774 one hunk would be more appropriate. */
5776 void failover_print (char *obuf
,
5777 unsigned *obufix
, unsigned obufmax
, const char *s
)
5779 int len
= strlen (s
);
5781 while (len
+ *obufix
+ 1 >= obufmax
) {
5782 log_debug ("%s", obuf
);
5784 log_debug ("%s", s
);
5790 strcpy (&obuf
[*obufix
], s
);
5793 #endif /* defined (DEBUG_FAILOVER_MESSAGES) */
5795 /* Taken from draft-ietf-dhc-loadb-01.txt: */
5796 /* A "mixing table" of 256 distinct values, in pseudo-random order. */
5797 unsigned char loadb_mx_tbl
[256] = {
5798 251, 175, 119, 215, 81, 14, 79, 191, 103, 49,
5799 181, 143, 186, 157, 0, 232, 31, 32, 55, 60,
5800 152, 58, 17, 237, 174, 70, 160, 144, 220, 90,
5801 57, 223, 59, 3, 18, 140, 111, 166, 203, 196,
5802 134, 243, 124, 95, 222, 179, 197, 65, 180, 48,
5803 36, 15, 107, 46, 233, 130, 165, 30, 123, 161,
5804 209, 23, 97, 16, 40, 91, 219, 61, 100, 10,
5805 210, 109, 250, 127, 22, 138, 29, 108, 244, 67,
5806 207, 9, 178, 204, 74, 98, 126, 249, 167, 116,
5807 34, 77, 193, 200, 121, 5, 20, 113, 71, 35,
5808 128, 13, 182, 94, 25, 226, 227, 199, 75, 27,
5809 41, 245, 230, 224, 43, 225, 177, 26, 155, 150,
5810 212, 142, 218, 115, 241, 73, 88, 105, 39, 114,
5811 62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
5812 154, 122, 12, 84, 82, 163, 44, 139, 228, 236,
5813 205, 242, 217, 11, 187, 146, 159, 64, 86, 239,
5814 195, 42, 106, 198, 118, 112, 184, 172, 87, 2,
5815 173, 117, 176, 229, 247, 253, 137, 185, 99, 164,
5816 102, 147, 45, 66, 231, 52, 141, 211, 194, 206,
5817 246, 238, 56, 110, 78, 248, 63, 240, 189, 93,
5818 92, 51, 53, 183, 19, 171, 72, 50, 33, 104,
5819 101, 69, 8, 252, 83, 120, 76, 135, 85, 54,
5820 202, 125, 188, 213, 96, 235, 136, 208, 162, 129,
5821 190, 132, 156, 38, 47, 1, 7, 254, 24, 4,
5822 216, 131, 89, 21, 28, 133, 37, 153, 149, 80,
5823 170, 68, 6, 169, 234, 151 };
5825 static unsigned char loadb_p_hash (const unsigned char *, unsigned);
5826 static unsigned char loadb_p_hash (const unsigned char *key
, unsigned len
)
5828 unsigned char hash
= len
;
5830 for(i
= len
; i
> 0; )
5831 hash
= loadb_mx_tbl
[hash
^ (key
[--i
])];
5835 int load_balance_mine (struct packet
*packet
, dhcp_failover_state_t
*state
)
5837 struct option_cache
*oc
;
5838 struct data_string ds
;
5839 unsigned char hbaix
;
5843 ec
= ntohs(packet
->raw
->secs
);
5845 #if defined(SECS_BYTEORDER)
5847 * If desired check to see if the secs field may have been byte
5848 * swapped. We assume it has if the high order byte isn't cleared
5849 * while the low order byte is cleared. In this case we swap the
5850 * bytes and continue processing.
5852 if ((ec
> 255) && ((ec
& 0xff) == 0)) {
5853 ec
= (ec
>> 8) | (ec
<< 8);
5857 if (state
->load_balance_max_secs
< ec
) {
5861 /* If we don't have a hash bucket array, we can't tell if this
5862 one's ours, so we assume it's not. */
5866 oc
= lookup_option(&dhcp_universe
, packet
->options
,
5867 DHO_DHCP_CLIENT_IDENTIFIER
);
5868 memset(&ds
, 0, sizeof ds
);
5870 evaluate_option_cache(&ds
, packet
, NULL
, NULL
,
5871 packet
->options
, NULL
,
5872 &global_scope
, oc
, MDL
)) {
5873 hbaix
= loadb_p_hash(ds
.data
, ds
.len
);
5875 data_string_forget(&ds
, MDL
);
5877 hbaix
= loadb_p_hash(packet
->raw
->chaddr
,
5881 hm
= state
->hba
[(hbaix
>> 3) & 0x1F] & (1 << (hbaix
& 0x07));
5883 if (state
->i_am
== primary
)
5889 /* The inverse of load_balance_mine ("load balance theirs"). We can't
5890 * use the regular load_balance_mine() and invert it because of the case
5891 * where there might not be an HBA, and we want to indicate false here
5892 * in this case only.
5895 peer_wants_lease(struct lease
*lp
)
5897 dhcp_failover_state_t
*state
;
5898 unsigned char hbaix
;
5904 state
= lp
->pool
->failover_peer
;
5906 if (!state
|| !state
->hba
)
5910 hbaix
= loadb_p_hash(lp
->uid
, lp
->uid_len
);
5911 else if (lp
->hardware_addr
.hlen
> 1)
5912 /* Skip the first byte, which is the hardware type, and is
5913 * not included during actual load balancing checks above
5914 * since it is separate from the packet header chaddr field.
5915 * The remainder of the hardware address should be identical
5916 * to the chaddr contents.
5918 hbaix
= loadb_p_hash(lp
->hardware_addr
.hbuf
+ 1,
5919 lp
->hardware_addr
.hlen
- 1);
5920 else /* impossible to categorize into LBA */
5923 hm
= state
->hba
[(hbaix
>> 3) & 0x1F] & (1 << (hbaix
& 0x07));
5925 if (state
->i_am
== primary
)
5931 /* This deals with what to do with bind updates when
5932 we're in the normal state
5934 Note that tsfp had better be set from the latest bind update
5935 _before_ this function is called! */
5938 normal_binding_state_transition_check (struct lease
*lease
,
5939 dhcp_failover_state_t
*state
,
5940 binding_state_t binding_state
,
5943 binding_state_t new_state
;
5945 /* If there is no transition, it's no problem. */
5946 if (binding_state
== lease
-> binding_state
)
5947 return binding_state
;
5949 switch (lease
-> binding_state
) {
5952 switch (binding_state
) {
5959 /* If the lease was free, and our peer is primary,
5960 then it can make it active, or abandoned, or
5961 backup. Abandoned is treated like free in
5963 if (state
-> i_am
== secondary
)
5964 return binding_state
;
5966 /* Otherwise, it can't legitimately do any sort of
5967 state transition. Because the lease was free,
5968 and the error has already been made, we allow the
5969 peer to change its state anyway, but log a warning
5970 message in hopes that the error will be fixed. */
5971 case FTS_FREE
: /* for compiler */
5972 new_state
= binding_state
;
5976 log_fatal ("Impossible case at %s:%d.", MDL
);
5980 /* The secondary can't change the state of an active
5982 if (state
-> i_am
== primary
) {
5983 /* Except that the client may send the DHCPRELEASE
5984 to the secondary, and we have to accept that. */
5985 if (binding_state
== FTS_RELEASED
)
5986 return binding_state
;
5987 new_state
= lease
-> binding_state
;
5991 /* So this is only for transitions made by the primary: */
5992 switch (binding_state
) {
5995 /* Can't set a lease to free or backup until the
5996 peer agrees that it's expired. */
5997 if (tsfp
> cur_time
) {
5998 new_state
= lease
-> binding_state
;
6001 return binding_state
;
6004 /* XXX 65 should be the clock skew between the peers
6005 XXX plus a fudge factor. This code will result
6006 XXX in problems if MCLT is really short or the
6007 XXX max-lease-time is really short (less than the
6008 XXX fudge factor. */
6009 if (lease
-> ends
- 65 > cur_time
) {
6010 new_state
= lease
-> binding_state
;
6018 return binding_state
;
6021 log_fatal ("Impossible case at %s:%d.", MDL
);
6026 switch (binding_state
) {
6029 /* Can't set a lease to free or backup until the
6030 peer agrees that it's expired. */
6031 if (tsfp
> cur_time
) {
6032 new_state
= lease
-> binding_state
;
6035 return binding_state
;
6042 return binding_state
;
6045 log_fatal ("Impossible case at %s:%d.", MDL
);
6049 switch (binding_state
) {
6053 /* These are invalid state transitions - should we
6060 return binding_state
;
6063 log_fatal ("Impossible case at %s:%d.", MDL
);
6067 switch (binding_state
) {
6070 /* Can't set a lease to free or backup until the
6071 peer agrees that it's expired. */
6072 if (tsfp
> cur_time
) {
6073 new_state
= lease
-> binding_state
;
6076 return binding_state
;
6083 return binding_state
;
6086 log_fatal ("Impossible case at %s:%d.", MDL
);
6090 switch (binding_state
) {
6096 /* If the lease was in backup, and our peer
6097 is secondary, then it can make it active
6099 if (state
-> i_am
== primary
)
6100 return binding_state
;
6102 /* Either the primary or the secondary can
6103 reasonably move a lease from the backup
6104 state to the free state. */
6106 return binding_state
;
6109 new_state
= lease
-> binding_state
;
6113 log_fatal ("Impossible case at %s:%d.", MDL
);
6118 log_fatal ("Impossible case at %s:%d.", MDL
);
6125 /* Determine whether the state transition is okay when we're potentially
6126 in conflict with the peer. */
6128 conflict_binding_state_transition_check (struct lease
*lease
,
6129 dhcp_failover_state_t
*state
,
6130 binding_state_t binding_state
,
6133 binding_state_t new_state
;
6135 /* If there is no transition, it's no problem. */
6136 if (binding_state
== lease
-> binding_state
)
6137 new_state
= binding_state
;
6139 switch (lease
-> binding_state
) {
6140 /* If we think the lease is not in use, then the
6141 state into which the partner put it is just fine,
6149 new_state
= binding_state
;
6152 /* If we think the lease *is* in use, then we're not
6153 going to take the partner's change if the partner
6154 thinks it's free. */
6156 switch (binding_state
) {
6159 new_state
= lease
-> binding_state
;
6163 /* If we don't agree about expiry, it's
6164 * invalid. 65 should allow for max
6165 * clock skew (60) plus some fudge.
6166 * XXX: should we refetch cur_time?
6168 if ((lease
->ends
- 65) > cur_time
)
6169 new_state
= lease
->binding_state
;
6171 new_state
= binding_state
;
6174 /* RELEASED, RESET, and ABANDONED indicate
6175 * that our partner has information about
6176 * this lease that we did not witness. Our
6182 new_state
= binding_state
;
6186 log_fatal ("Impossible case at %s:%d.", MDL
);
6192 log_fatal ("Impossible case at %s:%d.", MDL
);
6199 /* We can reallocate a lease under the following circumstances:
6201 (1) It belongs to us - it's FTS_FREE, and we're primary, or it's
6202 FTS_BACKUP, and we're secondary.
6203 (2) We're in partner_down, and the lease is not active, and we
6204 can be sure that the other server didn't make it active.
6205 We can only be sure that the server didn't make it active
6206 when we are in the partner_down state and one of the following
6207 two conditions holds:
6208 (a) in the case that the time sent from the peer is earlier than
6209 the time we entered the partner_down state, at least MCLT has
6210 gone by since we entered partner_down, or
6211 (b) in the case that the time sent from the peer is later than
6212 the time when we entered partner_down, the current time is
6213 later than the time sent from the peer by at least MCLT. */
6215 int lease_mine_to_reallocate (struct lease
*lease
)
6217 dhcp_failover_state_t
*peer
;
6219 if (lease
&& lease
->pool
&&
6220 (peer
= lease
->pool
->failover_peer
)) {
6222 * In addition to the normal rules governing wether a server
6223 * is allowed to operate changes on a lease, the server is
6224 * allowed to operate on a lease from the standpoint of the
6225 * most conservative guess of the peer's state for this lease.
6227 switch (lease
->binding_state
) {
6229 /* ACTIVE leases may not be reallocated. */
6234 /* FREE leases may only be allocated by the primary,
6235 * unless the secondary is acting in partner_down
6236 * state and stos+mclt or tsfp+mclt has expired,
6237 * whichever is greater.
6239 * ABANDONED are treated the same as FREE for all
6240 * purposes here. Note that servers will only try
6241 * for ABANDONED leases as a last resort anyway.
6243 if (peer
-> i_am
== primary
)
6246 return(peer
->service_state
== service_partner_down
&&
6247 ((lease
->tsfp
< peer
->me
.stos
) ?
6248 (peer
->me
.stos
+ peer
->mclt
< cur_time
) :
6249 (lease
->tsfp
+ peer
->mclt
< cur_time
)));
6254 * These leases are generally untouchable until the
6255 * peer acknowledges their state change. However, as
6256 * this is impossible if the peer is offline, the
6257 * failover protocol permits an 'optimization' to
6258 * rewind the lease to a previous state that the server
6259 * is allowed to operate on, if that was the state that
6260 * was last acknowledged by the peer.
6262 * So if a lease was free, was allocated by this
6263 * server, and expired without ever being transmitted
6264 * to the peer, it can be returned to free and given
6265 * to any new client legally.
6267 if ((peer
->i_am
== primary
) &&
6268 (lease
->rewind_binding_state
== FTS_FREE
))
6270 if ((peer
->i_am
== secondary
) &&
6271 (lease
->rewind_binding_state
== FTS_BACKUP
))
6274 /* FALL THROUGH (released, expired, reset) */
6277 * Released, expired, and reset leases go onto the
6278 * 'expired' queue all together. Upon entry into
6279 * partner-down state, this queue of leases has their
6280 * tsfp values modified to equal stos+mclt, the point
6281 * at which the server is allowed to remove them from
6282 * these transitional states.
6284 * Note that although tsfp has been possibly extended
6285 * past the actual tsfp we received from the peer, we
6286 * don't have to take any special action. Since tsfp
6287 * will be equal to the current time when the lease
6288 * transitions to free, tsfp will not be used to grant
6289 * lease-times longer than the MCLT to clients, which
6290 * is the only danger for this sort of modification.
6292 return((peer
->service_state
== service_partner_down
) &&
6293 (lease
->tsfp
< cur_time
));
6296 /* Only the secondary may allocate BACKUP leases,
6297 * unless in partner_down state in which case at
6298 * least TSFP+MCLT or STOS+MCLT must have expired,
6299 * whichever is greater.
6301 if (peer
->i_am
== secondary
)
6304 return((peer
->service_state
== service_partner_down
) &&
6305 ((lease
->tsfp
< peer
->me
.stos
) ?
6306 (peer
->me
.stos
+ peer
->mclt
< cur_time
) :
6307 (lease
->tsfp
+ peer
->mclt
< cur_time
)));
6310 /* All lease states appear above. */
6311 log_fatal("Impossible case at %s:%d.", MDL
);
6317 return(lease
->binding_state
== FTS_FREE
||
6318 lease
->binding_state
== FTS_BACKUP
);
6323 static isc_result_t
failover_message_reference (failover_message_t
**mp
,
6324 failover_message_t
*m
,
6325 const char *file
, int line
)
6329 return ISC_R_SUCCESS
;
6332 static isc_result_t
failover_message_dereference (failover_message_t
**mp
,
6333 const char *file
, int line
)
6335 failover_message_t
*m
;
6338 if (m
-> refcnt
== 0) {
6340 failover_message_dereference (&m
-> next
,
6342 if (m
-> chaddr
.data
)
6343 dfree (m
-> chaddr
.data
, file
, line
);
6344 if (m
-> client_identifier
.data
)
6345 dfree (m
-> client_identifier
.data
, file
, line
);
6347 dfree (m
-> hba
.data
, file
, line
);
6348 if (m
-> message
.data
)
6349 dfree (m
-> message
.data
, file
, line
);
6350 if (m
-> relationship_name
.data
)
6351 dfree (m
-> relationship_name
.data
, file
, line
);
6352 if (m
-> reply_options
.data
)
6353 dfree (m
-> reply_options
.data
, file
, line
);
6354 if (m
-> request_options
.data
)
6355 dfree (m
-> request_options
.data
, file
, line
);
6356 if (m
-> vendor_class
.data
)
6357 dfree (m
-> vendor_class
.data
, file
, line
);
6358 if (m
-> vendor_options
.data
)
6359 dfree (m
-> vendor_options
.data
, file
, line
);
6361 dfree (m
-> ddns
.data
, file
, line
);
6362 dfree (*mp
, file
, line
);
6365 return ISC_R_SUCCESS
;
6368 OMAPI_OBJECT_ALLOC (dhcp_failover_state
, dhcp_failover_state_t
,
6369 dhcp_type_failover_state
)
6370 OMAPI_OBJECT_ALLOC (dhcp_failover_listener
, dhcp_failover_listener_t
,
6371 dhcp_type_failover_listener
)
6372 OMAPI_OBJECT_ALLOC (dhcp_failover_link
, dhcp_failover_link_t
,
6373 dhcp_type_failover_link
)
6374 #endif /* defined (FAILOVER_PROTOCOL) */
6376 const char *binding_state_print (enum failover_state state
)