3 Failover protocol support code... */
6 * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1999-2003 by Internet Software Consortium
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 * Internet Systems Consortium, Inc.
23 * Redwood City, CA 94063
27 * This software has been written for Internet Systems Consortium
28 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29 * To learn more about Internet Systems Consortium, see
30 * ``http://www.isc.org/''. To learn more about Vixie Enterprises,
31 * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
32 * ``http://www.nominum.com''.
36 #include <omapip/omapip_p.h>
38 #if defined (FAILOVER_PROTOCOL)
39 dhcp_failover_state_t
*failover_states
;
40 static isc_result_t
do_a_failover_option (omapi_object_t
*,
41 dhcp_failover_link_t
*);
42 dhcp_failover_listener_t
*failover_listeners
;
44 static isc_result_t
failover_message_reference (failover_message_t
**,
46 const char *file
, int line
);
47 static isc_result_t
failover_message_dereference (failover_message_t
**,
48 const char *file
, int line
);
50 static void dhcp_failover_pool_balance(dhcp_failover_state_t
*state
);
51 static void dhcp_failover_pool_reqbalance(dhcp_failover_state_t
*state
);
52 static int dhcp_failover_pool_dobalance(dhcp_failover_state_t
*state
);
53 static INLINE
int secondary_not_hoarding(dhcp_failover_state_t
*state
,
57 void dhcp_failover_startup ()
59 dhcp_failover_state_t
*state
;
62 for (state
= failover_states
; state
; state
= state
-> next
) {
63 dhcp_failover_state_transition (state
, "startup");
65 if (state
-> pool_count
== 0) {
66 log_error ("failover peer declaration with no %s",
68 log_error ("In order to use failover, you MUST %s",
69 "refer to your main failover declaration");
70 log_error ("in each pool declaration. You MUST %s",
71 "NOT use range declarations outside");
72 log_fatal ("of pool declarations.");
74 /* In case the peer is already running, immediately try
75 to establish a connection with it. */
76 status
= dhcp_failover_link_initiate ((omapi_object_t
*)state
);
77 if (status
!= ISC_R_SUCCESS
&& status
!= ISC_R_INCOMPLETE
) {
78 #if defined (DEBUG_FAILOVER_TIMING)
79 log_info ("add_timeout +90 dhcp_failover_reconnect");
81 add_timeout (cur_time
+ 90,
82 dhcp_failover_reconnect
, state
,
84 dhcp_failover_state_reference
,
86 dhcp_failover_state_dereference
);
87 log_error ("failover peer %s: %s", state
-> name
,
88 isc_result_totext (status
));
91 status
= (dhcp_failover_listen
92 ((omapi_object_t
*)state
));
93 if (status
!= ISC_R_SUCCESS
) {
94 #if defined (DEBUG_FAILOVER_TIMING)
95 log_info ("add_timeout +90 %s",
96 "dhcp_failover_listener_restart");
98 add_timeout (cur_time
+ 90,
99 dhcp_failover_listener_restart
,
101 (tvref_t
)omapi_object_reference
,
102 (tvunref_t
)omapi_object_dereference
);
107 int dhcp_failover_write_all_states ()
109 dhcp_failover_state_t
*state
;
111 for (state
= failover_states
; state
; state
= state
-> next
) {
112 if (!write_failover_state (state
))
118 isc_result_t
enter_failover_peer (peer
)
119 dhcp_failover_state_t
*peer
;
121 dhcp_failover_state_t
*dup
= (dhcp_failover_state_t
*)0;
124 status
= find_failover_peer (&dup
, peer
-> name
, MDL
);
125 if (status
== ISC_R_NOTFOUND
) {
126 if (failover_states
) {
127 dhcp_failover_state_reference (&peer
-> next
,
128 failover_states
, MDL
);
129 dhcp_failover_state_dereference (&failover_states
,
132 dhcp_failover_state_reference (&failover_states
, peer
, MDL
);
133 return ISC_R_SUCCESS
;
135 dhcp_failover_state_dereference (&dup
, MDL
);
136 if (status
== ISC_R_SUCCESS
)
141 isc_result_t
find_failover_peer (peer
, name
, file
, line
)
142 dhcp_failover_state_t
**peer
;
147 dhcp_failover_state_t
*p
;
149 for (p
= failover_states
; p
; p
= p
-> next
)
150 if (!strcmp (name
, p
-> name
))
153 return dhcp_failover_state_reference (peer
, p
, file
, line
);
154 return ISC_R_NOTFOUND
;
157 /* The failover protocol has three objects associated with it. For
158 each failover partner declaration in the dhcpd.conf file, primary
159 or secondary, there is a failover_state object. For any primary or
160 secondary state object that has a connection to its peer, there is
161 also a failover_link object, which has its own input state separate
162 from the failover protocol state for managing the actual bytes
163 coming in off the wire. Finally, there will be one listener object
164 for every distinct port number associated with a secondary
165 failover_state object. Normally all secondary failover_state
166 objects are expected to listen on the same port number, so there
167 need be only one listener object, but if different port numbers are
168 specified for each failover object, there could be as many as one
169 listener object for each secondary failover_state object. */
171 /* This, then, is the implemention of the failover link object. */
173 isc_result_t
dhcp_failover_link_initiate (omapi_object_t
*h
)
176 dhcp_failover_link_t
*obj
;
177 dhcp_failover_state_t
*state
;
180 struct data_string ds
;
181 omapi_addr_list_t
*addrs
= (omapi_addr_list_t
*)0;
182 omapi_addr_t local_addr
;
184 /* Find the failover state in the object chain. */
185 for (o
= h
; o
-> outer
; o
= o
-> outer
)
187 for (; o
; o
= o
-> inner
) {
188 if (o
-> type
== dhcp_type_failover_state
)
192 return ISC_R_INVALIDARG
;
193 state
= (dhcp_failover_state_t
*)o
;
195 obj
= (dhcp_failover_link_t
*)0;
196 status
= dhcp_failover_link_allocate (&obj
, MDL
);
197 if (status
!= ISC_R_SUCCESS
)
199 option_cache_reference (&obj
-> peer_address
,
200 state
-> partner
.address
, MDL
);
201 obj
-> peer_port
= state
-> partner
.port
;
202 dhcp_failover_state_reference (&obj
-> state_object
, state
, MDL
);
204 memset (&ds
, 0, sizeof ds
);
205 if (!evaluate_option_cache (&ds
, (struct packet
*)0, (struct lease
*)0,
206 (struct client_state
*)0,
207 (struct option_state
*)0,
208 (struct option_state
*)0,
209 &global_scope
, obj
-> peer_address
, MDL
)) {
210 dhcp_failover_link_dereference (&obj
, MDL
);
211 return ISC_R_UNEXPECTED
;
214 /* Make an omapi address list out of a buffer containing zero or more
216 status
= omapi_addr_list_new (&addrs
, ds
.len
/ 4, MDL
);
217 if (status
!= ISC_R_SUCCESS
) {
218 dhcp_failover_link_dereference (&obj
, MDL
);
222 for (i
= 0; i
< addrs
-> count
; i
++) {
223 addrs
-> addresses
[i
].addrtype
= AF_INET
;
224 addrs
-> addresses
[i
].addrlen
= sizeof (struct in_addr
);
225 memcpy (addrs
-> addresses
[i
].address
,
226 &ds
.data
[i
* 4], sizeof (struct in_addr
));
227 addrs
-> addresses
[i
].port
= obj
-> peer_port
;
229 data_string_forget (&ds
, MDL
);
231 /* Now figure out the local address that we're supposed to use. */
232 if (!state
-> me
.address
||
233 !evaluate_option_cache (&ds
, (struct packet
*)0,
235 (struct client_state
*)0,
236 (struct option_state
*)0,
237 (struct option_state
*)0,
238 &global_scope
, state
-> me
.address
,
240 memset (&local_addr
, 0, sizeof local_addr
);
241 local_addr
.addrtype
= AF_INET
;
242 local_addr
.addrlen
= sizeof (struct in_addr
);
243 if (!state
-> server_identifier
.len
) {
244 log_fatal ("failover peer %s: no local address.",
248 if (ds
.len
!= sizeof (struct in_addr
)) {
249 data_string_forget (&ds
, MDL
);
250 dhcp_failover_link_dereference (&obj
, MDL
);
251 omapi_addr_list_dereference (&addrs
, MDL
);
252 return ISC_R_INVALIDARG
;
254 local_addr
.addrtype
= AF_INET
;
255 local_addr
.addrlen
= ds
.len
;
256 memcpy (local_addr
.address
, ds
.data
, ds
.len
);
257 if (!state
-> server_identifier
.len
)
258 data_string_copy (&state
-> server_identifier
,
260 data_string_forget (&ds
, MDL
);
261 local_addr
.port
= 0; /* Let the O.S. choose. */
264 status
= omapi_connect_list ((omapi_object_t
*)obj
,
266 omapi_addr_list_dereference (&addrs
, MDL
);
268 dhcp_failover_link_dereference (&obj
, MDL
);
272 isc_result_t
dhcp_failover_link_signal (omapi_object_t
*h
,
273 const char *name
, va_list ap
)
276 dhcp_failover_link_t
*link
;
278 dhcp_failover_state_t
*s
, *state
= (dhcp_failover_state_t
*)0;
282 if (h
-> type
!= dhcp_type_failover_link
) {
283 /* XXX shouldn't happen. Put an assert here? */
284 return ISC_R_UNEXPECTED
;
286 link
= (dhcp_failover_link_t
*)h
;
288 if (!strcmp (name
, "connect")) {
289 if (link
-> state_object
-> i_am
== primary
) {
290 status
= dhcp_failover_send_connect (h
);
291 if (status
!= ISC_R_SUCCESS
) {
292 log_info ("dhcp_failover_send_connect: %s",
293 isc_result_totext (status
));
294 omapi_disconnect (h
-> outer
, 1);
297 status
= ISC_R_SUCCESS
;
298 /* Allow the peer fifteen seconds to send us a
300 #if defined (DEBUG_FAILOVER_TIMING)
301 log_info ("add_timeout +15 %s",
302 "dhcp_failover_link_startup_timeout");
304 add_timeout (cur_time
+ 15,
305 dhcp_failover_link_startup_timeout
,
307 (tvref_t
)dhcp_failover_link_reference
,
308 (tvunref_t
)dhcp_failover_link_dereference
);
312 if (!strcmp (name
, "disconnect")) {
313 if (link
-> state_object
) {
314 dhcp_failover_state_reference (&state
,
315 link
-> state_object
, MDL
);
316 link
-> state
= dhcp_flink_disconnected
;
318 /* Make the transition. */
319 if (state
-> link_to_peer
== link
) {
320 dhcp_failover_state_transition (link
-> state_object
,
323 /* Start trying to reconnect. */
324 #if defined (DEBUG_FAILOVER_TIMING)
325 log_info ("add_timeout +5 %s",
326 "dhcp_failover_reconnect");
328 add_timeout (cur_time
+ 5, dhcp_failover_reconnect
,
330 (tvref_t
)dhcp_failover_state_reference
,
331 (tvunref_t
)dhcp_failover_state_dereference
);
333 dhcp_failover_state_dereference (&state
, MDL
);
335 return ISC_R_SUCCESS
;
338 if (!strcmp (name
, "status")) {
339 if (link
-> state_object
) {
342 status
= va_arg(ap
, isc_result_t
);
344 if ((status
== ISC_R_HOSTUNREACH
) || (status
== ISC_R_TIMEDOUT
)) {
345 dhcp_failover_state_reference (&state
,
346 link
-> state_object
, MDL
);
347 link
-> state
= dhcp_flink_disconnected
;
349 /* Make the transition. */
350 dhcp_failover_state_transition (link
-> state_object
,
353 /* Start trying to reconnect. */
354 #if defined (DEBUG_FAILOVER_TIMING)
355 log_info ("add_timeout +5 %s",
356 "dhcp_failover_reconnect");
358 add_timeout (cur_time
+ 5, dhcp_failover_reconnect
,
360 (tvref_t
)dhcp_failover_state_reference
,
361 (tvunref_t
)dhcp_failover_state_dereference
);
363 dhcp_failover_state_dereference (&state
, MDL
);
365 return ISC_R_SUCCESS
;
368 /* Not a signal we recognize? */
369 if (strcmp (name
, "ready")) {
370 if (h
-> inner
&& h
-> inner
-> type
-> signal_handler
)
371 return (*(h
-> inner
-> type
-> signal_handler
))
372 (h
-> inner
, name
, ap
);
373 return ISC_R_NOTFOUND
;
376 if (!h
-> outer
|| h
-> outer
-> type
!= omapi_type_connection
)
377 return ISC_R_INVALIDARG
;
380 /* We get here because we requested that we be woken up after
381 some number of bytes were read, and that number of bytes
382 has in fact been read. */
383 switch (link
-> state
) {
384 case dhcp_flink_start
:
385 link
-> state
= dhcp_flink_message_length_wait
;
386 if ((omapi_connection_require (c
, 2)) != ISC_R_SUCCESS
)
388 case dhcp_flink_message_length_wait
:
390 link
-> state
= dhcp_flink_message_wait
;
391 link
-> imsg
= dmalloc (sizeof (failover_message_t
), MDL
);
393 status
= ISC_R_NOMEMORY
;
396 failover_message_dereference (&link
->imsg
,
399 link
-> state
= dhcp_flink_disconnected
;
400 log_info ("message length wait: %s",
401 isc_result_totext (status
));
402 omapi_disconnect (c
, 1);
403 /* XXX just blow away the protocol state now?
404 XXX or will disconnect blow it away? */
405 return ISC_R_UNEXPECTED
;
407 memset (link
-> imsg
, 0, sizeof (failover_message_t
));
408 link
-> imsg
-> refcnt
= 1;
409 /* Get the length: */
410 omapi_connection_get_uint16 (c
, &link
-> imsg_len
);
411 link
-> imsg_count
= 0; /* Bytes read. */
413 /* Ensure the message is of valid length. */
414 if (link
->imsg_len
< DHCP_FAILOVER_MIN_MESSAGE_SIZE
||
415 link
->imsg_len
> DHCP_FAILOVER_MAX_MESSAGE_SIZE
) {
416 status
= ISC_R_UNEXPECTED
;
417 goto dhcp_flink_fail
;
420 if ((omapi_connection_require (c
, link
-> imsg_len
- 2U)) !=
423 case dhcp_flink_message_wait
:
424 /* Read in the message. At this point we have the
425 entire message in the input buffer. For each
426 incoming value ID, set a bit in the bitmask
427 indicating that we've gotten it. Maybe flag an
428 error message if the bit is already set. Once
429 we're done reading, we can check the bitmask to
430 make sure that the required fields for each message
431 have been included. */
433 link
-> imsg_count
+= 2; /* Count the length as read. */
435 /* Get message type. */
436 omapi_connection_copyout (&link
-> imsg
-> type
, c
, 1);
437 link
-> imsg_count
++;
439 /* Get message payload offset. */
440 omapi_connection_copyout (&link
-> imsg_payoff
, c
, 1);
441 link
-> imsg_count
++;
443 /* Get message time. */
444 omapi_connection_get_uint32 (c
, &link
-> imsg
-> time
);
445 link
-> imsg_count
+= 4;
447 /* Get transaction ID. */
448 omapi_connection_get_uint32 (c
, &link
-> imsg
-> xid
);
449 link
-> imsg_count
+= 4;
451 #if defined (DEBUG_FAILOVER_MESSAGES)
452 log_info ("link: message %s payoff %d time %ld xid %ld",
453 dhcp_failover_message_name (link
-> imsg
-> type
),
455 (unsigned long)link
-> imsg
-> time
,
456 (unsigned long)link
-> imsg
-> xid
);
458 /* Skip over any portions of the message header that we
460 if (link
-> imsg_payoff
- link
-> imsg_count
) {
461 omapi_connection_copyout ((unsigned char *)0, c
,
462 (link
-> imsg_payoff
-
463 link
-> imsg_count
));
464 link
-> imsg_count
= link
-> imsg_payoff
;
467 /* Now start sucking options off the wire. */
468 while (link
-> imsg_count
< link
-> imsg_len
) {
469 status
= do_a_failover_option (c
, link
);
470 if (status
!= ISC_R_SUCCESS
)
471 goto dhcp_flink_fail
;
474 /* If it's a connect message, try to associate it with
476 /* XXX this should be authenticated! */
477 if (link
-> imsg
-> type
== FTM_CONNECT
) {
481 if (!(link
->imsg
->options_present
&
482 FTB_RELATIONSHIP_NAME
)) {
483 errmsg
= "missing relationship-name";
484 reason
= FTR_INVALID_PARTNER
;
488 /* See if we can find a failover_state object that
489 matches this connection. This message should only
490 be received by a secondary from a primary. */
491 for (s
= failover_states
; s
; s
= s
-> next
) {
492 if (dhcp_failover_state_match_by_name(s
,
493 &link
->imsg
->relationship_name
))
497 /* If we can't find a failover protocol state
498 for this remote host, drop the connection */
500 errmsg
= "unknown failover relationship name";
501 reason
= FTR_INVALID_PARTNER
;
504 /* XXX Send a refusal message first?
505 XXX Look in protocol spec for guidance. */
509 slen
= strlen(sname
);
510 } else if (link
->imsg
->options_present
&
511 FTB_RELATIONSHIP_NAME
) {
512 sname
= link
->imsg
->relationship_name
.data
;
513 slen
= link
->imsg
->relationship_name
.count
;
516 slen
= strlen(sname
);
519 log_error("Failover CONNECT from %.*s: %s",
520 slen
, sname
, errmsg
);
521 dhcp_failover_send_connectack
522 ((omapi_object_t
*)link
, state
,
524 log_info ("failover: disconnect: %s", errmsg
);
525 omapi_disconnect (c
, 0);
526 link
-> state
= dhcp_flink_disconnected
;
527 return ISC_R_SUCCESS
;
530 if ((cur_time
> link
-> imsg
-> time
&&
531 cur_time
- link
-> imsg
-> time
> 60) ||
532 (cur_time
< link
-> imsg
-> time
&&
533 link
-> imsg
-> time
- cur_time
> 60)) {
534 errmsg
= "time offset too large";
535 reason
= FTR_TIMEMISMATCH
;
539 if (!(link
-> imsg
-> options_present
& FTB_HBA
) ||
540 link
-> imsg
-> hba
.count
!= 32) {
541 errmsg
= "invalid HBA";
542 reason
= FTR_HBA_CONFLICT
; /* XXX */
546 dfree (state
-> hba
, MDL
);
547 state
-> hba
= dmalloc (32, MDL
);
549 errmsg
= "no memory";
550 reason
= FTR_MISC_REJECT
;
553 memcpy (state
-> hba
, link
-> imsg
-> hba
.data
, 32);
555 if (!link
-> state_object
)
556 dhcp_failover_state_reference
557 (&link
-> state_object
, state
, MDL
);
558 if (!link
-> peer_address
)
559 option_cache_reference
560 (&link
-> peer_address
,
561 state
-> partner
.address
, MDL
);
564 /* If we don't have a state object at this point, it's
565 some kind of bogus situation, so just drop the
567 if (!link
-> state_object
) {
568 log_info ("failover: connect: no matching state.");
569 omapi_disconnect (c
, 1);
570 link
-> state
= dhcp_flink_disconnected
;
571 return ISC_R_INVALIDARG
;
574 /* Once we have the entire message, and we've validated
575 it as best we can here, pass it to the parent. */
576 omapi_signal ((omapi_object_t
*)link
-> state_object
,
578 link
-> state
= dhcp_flink_message_length_wait
;
579 failover_message_dereference (&link
-> imsg
, MDL
);
580 /* XXX This is dangerous because we could get into a tight
581 XXX loop reading input without servicing any other stuff.
582 XXX There needs to be a way to relinquish control but
583 XXX get it back immediately if there's no other work to
585 if ((omapi_connection_require (c
, 2)) == ISC_R_SUCCESS
)
590 log_fatal("Impossible case at %s:%d.", MDL
);
593 return ISC_R_SUCCESS
;
596 static isc_result_t
do_a_failover_option (c
, link
)
598 dhcp_failover_link_t
*link
;
600 u_int16_t option_code
;
601 u_int16_t option_len
;
607 if (link
-> imsg_count
+ 2 > link
-> imsg_len
) {
608 log_error ("FAILOVER: message overflow at option code.");
609 return ISC_R_PROTOCOLERROR
;
612 /* Get option code. */
613 omapi_connection_get_uint16 (c
, &option_code
);
614 link
-> imsg_count
+= 2;
616 if (link
-> imsg_count
+ 2 > link
-> imsg_len
) {
617 log_error ("FAILOVER: message overflow at length.");
618 return ISC_R_PROTOCOLERROR
;
621 /* Get option length. */
622 omapi_connection_get_uint16 (c
, &option_len
);
623 link
-> imsg_count
+= 2;
625 if (link
-> imsg_count
+ option_len
> link
-> imsg_len
) {
626 log_error ("FAILOVER: message overflow at data.");
627 return ISC_R_PROTOCOLERROR
;
630 /* If it's an unknown code, skip over it. */
631 if (option_code
> FTO_MAX
) {
632 #if defined (DEBUG_FAILOVER_MESSAGES)
633 log_debug (" option code %d (%s) len %d (not recognized)",
635 dhcp_failover_option_name (option_code
),
638 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
639 link
-> imsg_count
+= option_len
;
640 return ISC_R_SUCCESS
;
643 /* If it's the digest, do it now. */
644 if (ft_options
[option_code
].type
== FT_DIGEST
) {
645 link
-> imsg_count
+= option_len
;
646 if (link
-> imsg_count
!= link
-> imsg_len
) {
647 log_error ("FAILOVER: digest not at end of message");
648 return ISC_R_PROTOCOLERROR
;
650 #if defined (DEBUG_FAILOVER_MESSAGES)
651 log_debug (" option %s len %d",
652 ft_options
[option_code
].name
, option_len
);
654 /* For now, just dump it. */
655 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
656 return ISC_R_SUCCESS
;
659 /* Only accept an option once. */
660 if (link
-> imsg
-> options_present
& ft_options
[option_code
].bit
) {
661 log_error ("FAILOVER: duplicate option %s",
662 ft_options
[option_code
].name
);
663 return ISC_R_PROTOCOLERROR
;
666 /* Make sure the option is appropriate for this type of message.
667 Really, any option is generally allowed for any message, and the
668 cases where this is not true are too complicated to represent in
669 this way - what this code is doing is to just avoid saving the
670 value of an option we don't have any way to use, which allows
671 us to make the failover_message structure smaller. */
672 if (ft_options
[option_code
].bit
&&
673 !(fto_allowed
[link
-> imsg
-> type
] &
674 ft_options
[option_code
].bit
)) {
675 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
676 link
-> imsg_count
+= option_len
;
677 return ISC_R_SUCCESS
;
680 /* Figure out how many elements, how big they are, and where
682 if (ft_options
[option_code
].num_present
) {
683 /* If this option takes a fixed number of elements,
684 we expect the space for them to be preallocated,
685 and we can just read the data in. */
687 op
= ((unsigned char *)link
-> imsg
) +
688 ft_options
[option_code
].offset
;
689 op_size
= ft_sizes
[ft_options
[option_code
].type
];
690 op_count
= ft_options
[option_code
].num_present
;
692 if (option_len
!= op_size
* op_count
) {
693 log_error ("FAILOVER: option size (%d:%d), option %s",
695 (ft_sizes
[ft_options
[option_code
].type
] *
696 ft_options
[option_code
].num_present
),
697 ft_options
[option_code
].name
);
698 return ISC_R_PROTOCOLERROR
;
701 failover_option_t
*fo
;
703 /* FT_DDNS* are special - one or two bytes of status
704 followed by the client FQDN. */
705 if (ft_options
[option_code
].type
== FT_DDNS1
||
706 ft_options
[option_code
].type
== FT_DDNS1
) {
709 (((char *)link
-> imsg
) +
710 ft_options
[option_code
].offset
));
712 op_count
= (ft_options
[option_code
].type
== FT_DDNS1
715 omapi_connection_copyout (&ddns
-> codes
[0],
717 link
-> imsg_count
+= op_count
;
719 ddns
-> codes
[1] = 0;
721 op_count
= option_len
- op_count
;
723 ddns
-> length
= op_count
;
724 ddns
-> data
= dmalloc (op_count
, MDL
);
726 log_error ("FAILOVER: no memory getting%s(%d)",
727 " DNS data ", op_count
);
729 /* Actually, NO_MEMORY, but if we lose here
730 we have to drop the connection. */
731 return ISC_R_PROTOCOLERROR
;
733 omapi_connection_copyout (ddns
-> data
, c
, op_count
);
737 /* A zero for num_present means that any number of
738 elements can appear, so we have to figure out how
739 many we got from the length of the option, and then
740 fill out a failover_option structure describing the
742 op_size
= ft_sizes
[ft_options
[option_code
].type
];
744 /* Make sure that option data length is a multiple of the
745 size of the data type being sent. */
746 if (op_size
> 1 && option_len
% op_size
) {
747 log_error ("FAILOVER: option_len %d not %s%d",
748 option_len
, "multiple of ", op_size
);
749 return ISC_R_PROTOCOLERROR
;
752 op_count
= option_len
/ op_size
;
754 fo
= ((failover_option_t
*)
755 (((char *)link
-> imsg
) +
756 ft_options
[option_code
].offset
));
758 fo
-> count
= op_count
;
759 fo
-> data
= dmalloc (option_len
, MDL
);
761 log_error ("FAILOVER: no memory getting %s (%d)",
762 "option data", op_count
);
764 return ISC_R_PROTOCOLERROR
;
769 /* For single-byte message values and multi-byte values that
770 don't need swapping, just read them in all at once. */
771 if (op_size
== 1 || ft_options
[option_code
].type
== FT_IPADDR
) {
772 omapi_connection_copyout ((unsigned char *)op
, c
, option_len
);
773 link
-> imsg_count
+= option_len
;
777 /* For values that require swapping, read them in one at a time
778 using routines that swap bytes. */
779 for (i
= 0; i
< op_count
; i
++) {
780 switch (ft_options
[option_code
].type
) {
782 omapi_connection_get_uint32 (c
, (u_int32_t
*)op
);
784 link
-> imsg_count
+= 4;
788 omapi_connection_get_uint16 (c
, (u_int16_t
*)op
);
790 link
-> imsg_count
+= 2;
794 /* Everything else should have been handled
796 log_error ("FAILOVER: option %s: bad type %d",
797 ft_options
[option_code
].name
,
798 ft_options
[option_code
].type
);
799 return ISC_R_PROTOCOLERROR
;
803 /* Remember that we got this option. */
804 link
-> imsg
-> options_present
|= ft_options
[option_code
].bit
;
805 return ISC_R_SUCCESS
;
808 isc_result_t
dhcp_failover_link_set_value (omapi_object_t
*h
,
810 omapi_data_string_t
*name
,
811 omapi_typed_data_t
*value
)
813 if (h
-> type
!= omapi_type_protocol
)
814 return ISC_R_INVALIDARG
;
816 /* Never valid to set these. */
817 if (!omapi_ds_strcmp (name
, "link-port") ||
818 !omapi_ds_strcmp (name
, "link-name") ||
819 !omapi_ds_strcmp (name
, "link-state"))
822 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
823 return (*(h
-> inner
-> type
-> set_value
))
824 (h
-> inner
, id
, name
, value
);
825 return ISC_R_NOTFOUND
;
828 isc_result_t
dhcp_failover_link_get_value (omapi_object_t
*h
,
830 omapi_data_string_t
*name
,
831 omapi_value_t
**value
)
833 dhcp_failover_link_t
*link
;
835 if (h
-> type
!= omapi_type_protocol
)
836 return ISC_R_INVALIDARG
;
837 link
= (dhcp_failover_link_t
*)h
;
839 if (!omapi_ds_strcmp (name
, "link-port")) {
840 return omapi_make_int_value (value
, name
,
841 (int)link
-> peer_port
, MDL
);
842 } else if (!omapi_ds_strcmp (name
, "link-state")) {
843 if (link
-> state
< 0 ||
844 link
-> state
>= dhcp_flink_state_max
)
845 return omapi_make_string_value (value
, name
,
846 "invalid link state",
848 return omapi_make_string_value
850 dhcp_flink_state_names
[link
-> state
], MDL
);
853 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
854 return (*(h
-> inner
-> type
-> get_value
))
855 (h
-> inner
, id
, name
, value
);
856 return ISC_R_NOTFOUND
;
859 isc_result_t
dhcp_failover_link_destroy (omapi_object_t
*h
,
860 const char *file
, int line
)
862 dhcp_failover_link_t
*link
;
863 if (h
-> type
!= dhcp_type_failover_link
)
864 return ISC_R_INVALIDARG
;
865 link
= (dhcp_failover_link_t
*)h
;
867 if (link
-> peer_address
)
868 option_cache_dereference (&link
-> peer_address
, file
, line
);
870 failover_message_dereference (&link
-> imsg
, file
, line
);
871 if (link
-> state_object
)
872 dhcp_failover_state_dereference (&link
-> state_object
,
874 return ISC_R_SUCCESS
;
877 /* Write all the published values associated with the object through the
878 specified connection. */
880 isc_result_t
dhcp_failover_link_stuff_values (omapi_object_t
*c
,
884 dhcp_failover_link_t
*link
;
887 if (l
-> type
!= dhcp_type_failover_link
)
888 return ISC_R_INVALIDARG
;
889 link
= (dhcp_failover_link_t
*)l
;
891 status
= omapi_connection_put_name (c
, "link-port");
892 if (status
!= ISC_R_SUCCESS
)
894 status
= omapi_connection_put_uint32 (c
, sizeof (int));
895 if (status
!= ISC_R_SUCCESS
)
897 status
= omapi_connection_put_uint32 (c
, link
-> peer_port
);
898 if (status
!= ISC_R_SUCCESS
)
901 status
= omapi_connection_put_name (c
, "link-state");
902 if (status
!= ISC_R_SUCCESS
)
904 if (link
-> state
< 0 ||
905 link
-> state
>= dhcp_flink_state_max
)
906 status
= omapi_connection_put_string (c
, "invalid link state");
908 status
= (omapi_connection_put_string
909 (c
, dhcp_flink_state_names
[link
-> state
]));
910 if (status
!= ISC_R_SUCCESS
)
913 if (link
-> inner
&& link
-> inner
-> type
-> stuff_values
)
914 return (*(link
-> inner
-> type
-> stuff_values
)) (c
, id
,
916 return ISC_R_SUCCESS
;
919 /* Set up a listener for the omapi protocol. The handle stored points to
920 a listener object, not a protocol object. */
922 isc_result_t
dhcp_failover_listen (omapi_object_t
*h
)
925 dhcp_failover_listener_t
*obj
, *l
;
926 omapi_value_t
*value
= (omapi_value_t
*)0;
927 omapi_addr_t local_addr
;
930 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
931 "local-port", &value
);
932 if (status
!= ISC_R_SUCCESS
)
934 if (!value
-> value
) {
935 omapi_value_dereference (&value
, MDL
);
936 return ISC_R_INVALIDARG
;
939 status
= omapi_get_int_value (&port
, value
-> value
);
940 omapi_value_dereference (&value
, MDL
);
941 if (status
!= ISC_R_SUCCESS
)
943 local_addr
.port
= port
;
945 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
946 "local-address", &value
);
947 if (status
!= ISC_R_SUCCESS
)
949 if (!value
-> value
) {
951 omapi_value_dereference (&value
, MDL
);
952 return ISC_R_INVALIDARG
;
955 if (value
-> value
-> type
!= omapi_datatype_data
||
956 value
-> value
-> u
.buffer
.len
!= sizeof (struct in_addr
))
959 memcpy (local_addr
.address
, value
-> value
-> u
.buffer
.value
,
960 value
-> value
-> u
.buffer
.len
);
961 local_addr
.addrlen
= value
-> value
-> u
.buffer
.len
;
962 local_addr
.addrtype
= AF_INET
;
964 omapi_value_dereference (&value
, MDL
);
966 /* Are we already listening on this port and address? */
967 for (l
= failover_listeners
; l
; l
= l
-> next
) {
968 if (l
-> address
.port
== local_addr
.port
&&
969 l
-> address
.addrtype
== local_addr
.addrtype
&&
970 l
-> address
.addrlen
== local_addr
.addrlen
&&
971 !memcmp (l
-> address
.address
, local_addr
.address
,
975 /* Already listening. */
977 return ISC_R_SUCCESS
;
979 obj
= (dhcp_failover_listener_t
*)0;
980 status
= dhcp_failover_listener_allocate (&obj
, MDL
);
981 if (status
!= ISC_R_SUCCESS
)
983 obj
-> address
= local_addr
;
985 status
= omapi_listen_addr ((omapi_object_t
*)obj
, &obj
-> address
, 1);
986 if (status
!= ISC_R_SUCCESS
)
989 status
= omapi_object_reference (&h
-> outer
,
990 (omapi_object_t
*)obj
, MDL
);
991 if (status
!= ISC_R_SUCCESS
) {
992 dhcp_failover_listener_dereference (&obj
, MDL
);
995 status
= omapi_object_reference (&obj
-> inner
, h
, MDL
);
996 if (status
!= ISC_R_SUCCESS
) {
997 dhcp_failover_listener_dereference (&obj
, MDL
);
1001 /* Put this listener on the list. */
1002 if (failover_listeners
) {
1003 dhcp_failover_listener_reference (&obj
-> next
,
1004 failover_listeners
, MDL
);
1005 dhcp_failover_listener_dereference (&failover_listeners
, MDL
);
1007 dhcp_failover_listener_reference (&failover_listeners
, obj
, MDL
);
1009 return dhcp_failover_listener_dereference (&obj
, MDL
);
1012 /* Signal handler for protocol listener - if we get a connect signal,
1013 create a new protocol connection, otherwise pass the signal down. */
1015 isc_result_t
dhcp_failover_listener_signal (omapi_object_t
*o
,
1016 const char *name
, va_list ap
)
1018 isc_result_t status
;
1019 omapi_connection_object_t
*c
;
1020 dhcp_failover_link_t
*obj
;
1021 dhcp_failover_listener_t
*p
;
1022 dhcp_failover_state_t
*s
, *state
= (dhcp_failover_state_t
*)0;
1024 if (!o
|| o
-> type
!= dhcp_type_failover_listener
)
1025 return ISC_R_INVALIDARG
;
1026 p
= (dhcp_failover_listener_t
*)o
;
1028 /* Not a signal we recognize? */
1029 if (strcmp (name
, "connect")) {
1030 if (p
-> inner
&& p
-> inner
-> type
-> signal_handler
)
1031 return (*(p
-> inner
-> type
-> signal_handler
))
1032 (p
-> inner
, name
, ap
);
1033 return ISC_R_NOTFOUND
;
1036 c
= va_arg (ap
, omapi_connection_object_t
*);
1037 if (!c
|| c
-> type
!= omapi_type_connection
)
1038 return ISC_R_INVALIDARG
;
1040 /* See if we can find a failover_state object that
1041 matches this connection. */
1042 for (s
= failover_states
; s
; s
= s
-> next
) {
1043 if (dhcp_failover_state_match
1044 (s
, (u_int8_t
*)&c
-> remote_addr
.sin_addr
,
1045 sizeof c
-> remote_addr
.sin_addr
)) {
1051 log_info ("failover: listener: no matching state");
1052 return omapi_disconnect ((omapi_object_t
*)c
, 1);
1055 obj
= (dhcp_failover_link_t
*)0;
1056 status
= dhcp_failover_link_allocate (&obj
, MDL
);
1057 if (status
!= ISC_R_SUCCESS
)
1059 obj
-> peer_port
= ntohs (c
-> remote_addr
.sin_port
);
1061 status
= omapi_object_reference (&obj
-> outer
,
1062 (omapi_object_t
*)c
, MDL
);
1063 if (status
!= ISC_R_SUCCESS
) {
1065 dhcp_failover_link_dereference (&obj
, MDL
);
1066 log_info ("failover: listener: picayune failure.");
1067 omapi_disconnect ((omapi_object_t
*)c
, 1);
1071 status
= omapi_object_reference (&c
-> inner
,
1072 (omapi_object_t
*)obj
, MDL
);
1073 if (status
!= ISC_R_SUCCESS
)
1076 status
= dhcp_failover_state_reference (&obj
-> state_object
,
1078 if (status
!= ISC_R_SUCCESS
)
1081 omapi_signal_in ((omapi_object_t
*)obj
, "connect");
1083 return dhcp_failover_link_dereference (&obj
, MDL
);
1086 isc_result_t
dhcp_failover_listener_set_value (omapi_object_t
*h
,
1088 omapi_data_string_t
*name
,
1089 omapi_typed_data_t
*value
)
1091 if (h
-> type
!= dhcp_type_failover_listener
)
1092 return ISC_R_INVALIDARG
;
1094 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
1095 return (*(h
-> inner
-> type
-> set_value
))
1096 (h
-> inner
, id
, name
, value
);
1097 return ISC_R_NOTFOUND
;
1100 isc_result_t
dhcp_failover_listener_get_value (omapi_object_t
*h
,
1102 omapi_data_string_t
*name
,
1103 omapi_value_t
**value
)
1105 if (h
-> type
!= dhcp_type_failover_listener
)
1106 return ISC_R_INVALIDARG
;
1108 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
1109 return (*(h
-> inner
-> type
-> get_value
))
1110 (h
-> inner
, id
, name
, value
);
1111 return ISC_R_NOTFOUND
;
1114 isc_result_t
dhcp_failover_listener_destroy (omapi_object_t
*h
,
1115 const char *file
, int line
)
1117 dhcp_failover_listener_t
*l
;
1119 if (h
-> type
!= dhcp_type_failover_listener
)
1120 return ISC_R_INVALIDARG
;
1121 l
= (dhcp_failover_listener_t
*)h
;
1123 dhcp_failover_listener_dereference (&l
-> next
, file
, line
);
1125 return ISC_R_SUCCESS
;
1128 /* Write all the published values associated with the object through the
1129 specified connection. */
1131 isc_result_t
dhcp_failover_listener_stuff (omapi_object_t
*c
,
1135 if (p
-> type
!= dhcp_type_failover_listener
)
1136 return ISC_R_INVALIDARG
;
1138 if (p
-> inner
&& p
-> inner
-> type
-> stuff_values
)
1139 return (*(p
-> inner
-> type
-> stuff_values
)) (c
, id
,
1141 return ISC_R_SUCCESS
;
1144 /* Set up master state machine for the failover protocol. */
1146 isc_result_t
dhcp_failover_register (omapi_object_t
*h
)
1148 isc_result_t status
;
1149 dhcp_failover_state_t
*obj
;
1151 omapi_value_t
*value
= (omapi_value_t
*)0;
1153 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
1154 "local-port", &value
);
1155 if (status
!= ISC_R_SUCCESS
)
1157 if (!value
-> value
) {
1158 omapi_value_dereference (&value
, MDL
);
1159 return ISC_R_INVALIDARG
;
1162 status
= omapi_get_int_value (&port
, value
-> value
);
1163 omapi_value_dereference (&value
, MDL
);
1164 if (status
!= ISC_R_SUCCESS
)
1167 obj
= (dhcp_failover_state_t
*)0;
1168 dhcp_failover_state_allocate (&obj
, MDL
);
1169 obj
-> me
.port
= port
;
1171 status
= omapi_listen ((omapi_object_t
*)obj
, port
, 1);
1172 if (status
!= ISC_R_SUCCESS
) {
1173 dhcp_failover_state_dereference (&obj
, MDL
);
1177 status
= omapi_object_reference (&h
-> outer
, (omapi_object_t
*)obj
,
1179 if (status
!= ISC_R_SUCCESS
) {
1180 dhcp_failover_state_dereference (&obj
, MDL
);
1183 status
= omapi_object_reference (&obj
-> inner
, h
, MDL
);
1184 dhcp_failover_state_dereference (&obj
, MDL
);
1188 /* Signal handler for protocol state machine. */
1190 isc_result_t
dhcp_failover_state_signal (omapi_object_t
*o
,
1191 const char *name
, va_list ap
)
1193 isc_result_t status
;
1194 dhcp_failover_state_t
*state
;
1195 dhcp_failover_link_t
*link
;
1197 if (!o
|| o
-> type
!= dhcp_type_failover_state
)
1198 return ISC_R_INVALIDARG
;
1199 state
= (dhcp_failover_state_t
*)o
;
1201 /* Not a signal we recognize? */
1202 if (strcmp (name
, "disconnect") &&
1203 strcmp (name
, "message")) {
1204 if (state
-> inner
&& state
-> inner
-> type
-> signal_handler
)
1205 return (*(state
-> inner
-> type
-> signal_handler
))
1206 (state
-> inner
, name
, ap
);
1207 return ISC_R_NOTFOUND
;
1210 /* Handle connect signals by seeing what state we're in
1211 and potentially doing a state transition. */
1212 if (!strcmp (name
, "disconnect")) {
1213 link
= va_arg (ap
, dhcp_failover_link_t
*);
1215 dhcp_failover_link_dereference (&state
-> link_to_peer
, MDL
);
1216 dhcp_failover_state_transition (state
, "disconnect");
1217 if (state
-> i_am
== primary
) {
1218 #if defined (DEBUG_FAILOVER_TIMING)
1219 log_info ("add_timeout +90 %s",
1220 "dhcp_failover_reconnect");
1222 add_timeout (cur_time
+ 90, dhcp_failover_reconnect
,
1224 (tvref_t
)dhcp_failover_state_reference
,
1226 dhcp_failover_state_dereference
);
1228 } else if (!strcmp (name
, "message")) {
1229 link
= va_arg (ap
, dhcp_failover_link_t
*);
1231 if (link
-> imsg
-> type
== FTM_CONNECT
) {
1232 /* If we already have a link to the peer, it must be
1234 XXX Is this the right thing to do?
1235 XXX Probably not - what if both peers start at
1236 XXX the same time? */
1237 if (state
-> link_to_peer
) {
1238 dhcp_failover_send_connectack
1239 ((omapi_object_t
*)link
, state
,
1241 "already connected");
1242 omapi_disconnect (link
-> outer
, 1);
1243 return ISC_R_SUCCESS
;
1245 if (!(link
-> imsg
-> options_present
& FTB_MCLT
)) {
1246 dhcp_failover_send_connectack
1247 ((omapi_object_t
*)link
, state
,
1249 "no MCLT provided");
1250 omapi_disconnect (link
-> outer
, 1);
1251 return ISC_R_SUCCESS
;
1254 dhcp_failover_link_reference (&state
-> link_to_peer
,
1256 status
= (dhcp_failover_send_connectack
1257 ((omapi_object_t
*)link
, state
, 0, 0));
1258 if (status
!= ISC_R_SUCCESS
) {
1259 dhcp_failover_link_dereference
1260 (&state
-> link_to_peer
, MDL
);
1261 log_info ("dhcp_failover_send_connectack: %s",
1262 isc_result_totext (status
));
1263 omapi_disconnect (link
-> outer
, 1);
1264 return ISC_R_SUCCESS
;
1266 if (link
-> imsg
-> options_present
& FTB_MAX_UNACKED
)
1267 state
-> partner
.max_flying_updates
=
1268 link
-> imsg
-> max_unacked
;
1269 if (link
-> imsg
-> options_present
& FTB_RECEIVE_TIMER
)
1270 state
-> partner
.max_response_delay
=
1271 link
-> imsg
-> receive_timer
;
1272 state
-> mclt
= link
-> imsg
-> mclt
;
1273 dhcp_failover_send_state (state
);
1274 cancel_timeout (dhcp_failover_link_startup_timeout
,
1276 } else if (link
-> imsg
-> type
== FTM_CONNECTACK
) {
1281 cancel_timeout (dhcp_failover_link_startup_timeout
,
1284 if (!(link
->imsg
->options_present
&
1285 FTB_RELATIONSHIP_NAME
)) {
1286 errmsg
= "missing relationship-name";
1287 reason
= FTR_INVALID_PARTNER
;
1291 if (link
->imsg
->options_present
& FTB_REJECT_REASON
) {
1292 /* XXX: add message option to text output. */
1293 log_error ("Failover CONNECT to %s rejected: %s",
1294 state
? state
->name
: "unknown",
1295 (dhcp_failover_reject_reason_print
1296 (link
-> imsg
-> reject_reason
)));
1297 /* XXX print message from peer if peer sent message. */
1298 omapi_disconnect (link
-> outer
, 1);
1299 return ISC_R_SUCCESS
;
1302 if (!dhcp_failover_state_match_by_name(state
,
1303 &link
->imsg
->relationship_name
)) {
1304 /* XXX: Overflow results in log truncation, safe. */
1305 snprintf(errbuf
, sizeof(errbuf
), "remote failover "
1306 "relationship name %.*s does not match",
1307 link
->imsg
->relationship_name
.count
,
1308 link
->imsg
->relationship_name
.data
);
1310 reason
= FTR_INVALID_PARTNER
;
1312 log_error("Failover CONNECTACK from %s: %s",
1313 state
->name
, errmsg
);
1314 dhcp_failover_send_disconnect ((omapi_object_t
*)link
,
1316 omapi_disconnect (link
-> outer
, 0);
1317 return ISC_R_SUCCESS
;
1320 if (state
-> link_to_peer
) {
1321 errmsg
= "already connected";
1322 reason
= FTR_DUP_CONNECTION
;
1326 if ((cur_time
> link
-> imsg
-> time
&&
1327 cur_time
- link
-> imsg
-> time
> 60) ||
1328 (cur_time
< link
-> imsg
-> time
&&
1329 link
-> imsg
-> time
- cur_time
> 60)) {
1330 errmsg
= "time offset too large";
1331 reason
= FTR_TIMEMISMATCH
;
1335 dhcp_failover_link_reference (&state
-> link_to_peer
,
1338 /* XXX This is probably the right thing to do, but
1339 XXX for release three, to make the smallest possible
1340 XXX change, we are doing this when the peer state
1341 XXX changes instead. */
1342 if (state
-> me
.state
== startup
)
1343 dhcp_failover_set_state (state
,
1344 state
-> saved_state
);
1347 dhcp_failover_send_state (state
);
1349 if (link
-> imsg
-> options_present
& FTB_MAX_UNACKED
)
1350 state
-> partner
.max_flying_updates
=
1351 link
-> imsg
-> max_unacked
;
1352 if (link
-> imsg
-> options_present
& FTB_RECEIVE_TIMER
)
1353 state
-> partner
.max_response_delay
=
1354 link
-> imsg
-> receive_timer
;
1355 #if defined (DEBUG_FAILOVER_TIMING)
1356 log_info ("add_timeout +%d %s",
1357 (int)state
-> partner
.max_response_delay
/ 3,
1358 "dhcp_failover_send_contact");
1360 add_timeout (cur_time
+
1361 (int)state
-> partner
.max_response_delay
/ 3,
1362 dhcp_failover_send_contact
, state
,
1363 (tvref_t
)dhcp_failover_state_reference
,
1364 (tvunref_t
)dhcp_failover_state_dereference
);
1365 #if defined (DEBUG_FAILOVER_TIMING)
1366 log_info ("add_timeout +%d %s",
1367 (int)state
-> me
.max_response_delay
,
1368 "dhcp_failover_timeout");
1370 add_timeout (cur_time
+
1371 (int)state
-> me
.max_response_delay
,
1372 dhcp_failover_timeout
, state
,
1373 (tvref_t
)dhcp_failover_state_reference
,
1374 (tvunref_t
)dhcp_failover_state_dereference
);
1375 } else if (link
-> imsg
-> type
== FTM_DISCONNECT
) {
1376 if (link
-> imsg
-> reject_reason
) {
1377 log_error ("Failover DISCONNECT from %s: %s",
1378 state
? state
->name
: "unknown",
1379 (dhcp_failover_reject_reason_print
1380 (link
-> imsg
-> reject_reason
)));
1382 omapi_disconnect (link
-> outer
, 1);
1383 } else if (link
-> imsg
-> type
== FTM_BNDUPD
) {
1384 dhcp_failover_process_bind_update (state
,
1386 } else if (link
-> imsg
-> type
== FTM_BNDACK
) {
1387 dhcp_failover_process_bind_ack (state
, link
-> imsg
);
1388 } else if (link
-> imsg
-> type
== FTM_UPDREQ
) {
1389 dhcp_failover_process_update_request (state
,
1391 } else if (link
-> imsg
-> type
== FTM_UPDREQALL
) {
1392 dhcp_failover_process_update_request_all
1393 (state
, link
-> imsg
);
1394 } else if (link
-> imsg
-> type
== FTM_UPDDONE
) {
1395 dhcp_failover_process_update_done (state
,
1397 } else if (link
-> imsg
-> type
== FTM_POOLREQ
) {
1398 dhcp_failover_pool_reqbalance(state
);
1399 } else if (link
-> imsg
-> type
== FTM_POOLRESP
) {
1400 log_info ("pool response: %ld leases",
1402 link
-> imsg
-> addresses_transferred
);
1403 } else if (link
-> imsg
-> type
== FTM_STATE
) {
1404 dhcp_failover_peer_state_changed (state
,
1408 /* Add a timeout so that if the partner doesn't send
1409 another message for the maximum transmit idle time
1410 plus a grace of one second, we close the
1412 if (state
-> link_to_peer
&&
1413 state
-> link_to_peer
== link
&&
1414 state
-> link_to_peer
-> state
!= dhcp_flink_disconnected
)
1416 #if defined (DEBUG_FAILOVER_TIMING)
1417 log_info ("add_timeout +%d %s",
1418 (int)state
-> me
.max_response_delay
,
1419 "dhcp_failover_timeout");
1421 add_timeout (cur_time
+
1422 (int)state
-> me
.max_response_delay
,
1423 dhcp_failover_timeout
, state
,
1424 (tvref_t
)dhcp_failover_state_reference
,
1425 (tvunref_t
)dhcp_failover_state_dereference
);
1430 /* Handle all the events we care about... */
1431 return ISC_R_SUCCESS
;
1434 isc_result_t
dhcp_failover_state_transition (dhcp_failover_state_t
*state
,
1437 isc_result_t status
;
1439 /* XXX Check these state transitions against the spec! */
1440 if (!strcmp (name
, "disconnect")) {
1441 if (state
-> link_to_peer
) {
1442 log_info ("peer %s: disconnected", state
-> name
);
1443 if (state
-> link_to_peer
-> state_object
)
1444 dhcp_failover_state_dereference
1445 (&state
-> link_to_peer
-> state_object
, MDL
);
1446 dhcp_failover_link_dereference (&state
-> link_to_peer
,
1449 cancel_timeout (dhcp_failover_send_contact
, state
);
1450 cancel_timeout (dhcp_failover_timeout
, state
);
1451 cancel_timeout (dhcp_failover_startup_timeout
, state
);
1453 switch (state
-> me
.state
== startup
?
1454 state
-> saved_state
: state
-> me
.state
) {
1455 /* In these situations, we remain in the current
1456 * state, or if in startup enter those states.
1458 case communications_interrupted
:
1465 case resolution_interrupted
:
1467 /* Already in the right state? */
1468 if (state
-> me
.state
== startup
)
1469 return (dhcp_failover_set_state
1470 (state
, state
-> saved_state
));
1471 return ISC_R_SUCCESS
;
1473 case potential_conflict
:
1474 return dhcp_failover_set_state
1475 (state
, resolution_interrupted
);
1478 return dhcp_failover_set_state
1479 (state
, communications_interrupted
);
1482 return dhcp_failover_set_state
1483 (state
, resolution_interrupted
);
1486 log_fatal("Impossible case at %s:%d.", MDL
);
1487 break; /* can't happen. */
1489 } else if (!strcmp (name
, "connect")) {
1490 switch (state
-> me
.state
) {
1491 case communications_interrupted
:
1492 status
= dhcp_failover_set_state (state
, normal
);
1493 dhcp_failover_send_updates (state
);
1496 case resolution_interrupted
:
1497 return dhcp_failover_set_state (state
,
1498 potential_conflict
);
1502 case potential_conflict
:
1511 return dhcp_failover_send_state (state
);
1514 log_fatal("Impossible case at %s:%d.", MDL
);
1517 } else if (!strcmp (name
, "startup")) {
1518 dhcp_failover_set_state (state
, startup
);
1519 return ISC_R_SUCCESS
;
1520 } else if (!strcmp (name
, "connect-timeout")) {
1521 switch (state
-> me
.state
) {
1522 case communications_interrupted
:
1524 case resolution_interrupted
:
1529 return ISC_R_SUCCESS
;
1536 return dhcp_failover_set_state
1537 (state
, communications_interrupted
);
1539 case potential_conflict
:
1540 return dhcp_failover_set_state
1541 (state
, resolution_interrupted
);
1544 log_fatal("Impossible case at %s:%d.", MDL
);
1548 return ISC_R_INVALIDARG
;
1551 isc_result_t
dhcp_failover_set_service_state (dhcp_failover_state_t
*state
)
1553 switch (state
-> me
.state
) {
1555 state
-> service_state
= not_responding
;
1556 state
-> nrr
= " (my state unknown)";
1560 state
-> service_state
= service_partner_down
;
1565 state
-> service_state
= cooperating
;
1569 case communications_interrupted
:
1570 state
-> service_state
= not_cooperating
;
1574 case resolution_interrupted
:
1575 case potential_conflict
:
1577 state
-> service_state
= not_responding
;
1578 state
-> nrr
= " (resolving conflicts)";
1582 state
-> service_state
= not_responding
;
1583 state
-> nrr
= " (recovering)";
1587 state
-> service_state
= not_responding
;
1588 state
-> nrr
= " (shut down)";
1592 state
-> service_state
= not_responding
;
1593 state
-> nrr
= " (paused)";
1597 state
-> service_state
= not_responding
;
1598 state
-> nrr
= " (recover wait)";
1602 state
-> service_state
= not_responding
;
1603 state
-> nrr
= " (recover done)";
1607 state
-> service_state
= service_startup
;
1608 state
-> nrr
= " (startup)";
1612 log_fatal("Impossible case at %s:%d.\n", MDL
);
1616 /* Some peer states can require us not to respond, even if our
1618 /* XXX hm. I suspect this isn't true anymore. */
1619 if (state
-> service_state
!= not_responding
) {
1620 switch (state
-> partner
.state
) {
1622 state
-> service_state
= not_responding
;
1623 state
-> nrr
= " (peer demands: recovering)";
1626 case potential_conflict
:
1628 case resolution_interrupted
:
1629 state
-> service_state
= not_responding
;
1630 state
-> nrr
= " (peer demands: resolving conflicts)";
1633 /* Other peer states don't affect our behaviour. */
1639 return ISC_R_SUCCESS
;
1642 isc_result_t
dhcp_failover_set_state (dhcp_failover_state_t
*state
,
1643 enum failover_state new_state
)
1645 enum failover_state saved_state
;
1648 struct shared_network
*s
;
1651 /* If we're in certain states where we're sending updates, and the peer
1652 * state changes, we need to re-schedule any pending updates just to
1653 * be on the safe side. This results in retransmission.
1655 switch (state
-> me
.state
) {
1657 case potential_conflict
:
1659 if (state
-> ack_queue_tail
) {
1662 /* Zap the flags. */
1663 for (lp
= state
-> ack_queue_head
; lp
; lp
= lp
-> next_pending
)
1664 lp
-> flags
= ((lp
-> flags
& ~ON_ACK_QUEUE
) |
1667 /* Now hook the ack queue to the beginning of the update
1669 if (state
-> update_queue_head
) {
1670 lease_reference (&state
-> ack_queue_tail
-> next_pending
,
1671 state
-> update_queue_head
, MDL
);
1672 lease_dereference (&state
-> update_queue_head
, MDL
);
1674 lease_reference (&state
-> update_queue_head
,
1675 state
-> ack_queue_head
, MDL
);
1676 if (!state
-> update_queue_tail
) {
1677 #if defined (POINTER_DEBUG)
1678 if (state
-> ack_queue_tail
-> next_pending
) {
1679 log_error ("next pending on ack queue tail.");
1683 lease_reference (&state
-> update_queue_tail
,
1684 state
-> ack_queue_tail
, MDL
);
1686 lease_dereference (&state
-> ack_queue_tail
, MDL
);
1687 lease_dereference (&state
-> ack_queue_head
, MDL
);
1688 state
-> cur_unacked_updates
= 0;
1690 /* We will re-queue a timeout later, if applicable. */
1691 cancel_timeout (dhcp_failover_keepalive
, state
);
1698 /* Tentatively make the transition. */
1699 saved_state
= state
-> me
.state
;
1700 saved_stos
= state
-> me
.stos
;
1702 /* Keep the old stos if we're going into recover_wait or if we're
1703 coming into or out of startup. */
1704 if (new_state
!= recover_wait
&& new_state
!= startup
&&
1705 saved_state
!= startup
)
1706 state
-> me
.stos
= cur_time
;
1708 /* If we're in shutdown, peer is in partner_down, and we're moving
1709 to recover, we can skip waiting for MCLT to expire. This happens
1710 when a server is moved administratively into shutdown prior to
1711 actually shutting down. Of course, if there are any updates
1712 pending we can't actually do this. */
1713 if (new_state
== recover
&& saved_state
== shut_down
&&
1714 state
-> partner
.state
== partner_down
&&
1715 !state
-> update_queue_head
&& !state
-> ack_queue_head
)
1716 state
-> me
.stos
= cur_time
- state
-> mclt
;
1718 state
-> me
.state
= new_state
;
1719 if (new_state
== startup
&& saved_state
!= startup
)
1720 state
-> saved_state
= saved_state
;
1722 /* If we can't record the new state, we can't make a state transition. */
1723 if (!write_failover_state (state
) || !commit_leases ()) {
1724 log_error ("Unable to record current failover state for %s",
1726 state
-> me
.state
= saved_state
;
1727 state
-> me
.stos
= saved_stos
;
1728 return ISC_R_IOERROR
;
1731 log_info ("failover peer %s: I move from %s to %s",
1732 state
-> name
, dhcp_failover_state_name_print (saved_state
),
1733 dhcp_failover_state_name_print (state
-> me
.state
));
1735 /* If we were in startup and we just left it, cancel the timeout. */
1736 if (new_state
!= startup
&& saved_state
== startup
)
1737 cancel_timeout (dhcp_failover_startup_timeout
, state
);
1739 /* Set our service state. */
1740 dhcp_failover_set_service_state (state
);
1742 /* Tell the peer about it. */
1743 if (state
-> link_to_peer
)
1744 dhcp_failover_send_state (state
);
1746 switch (new_state
) {
1748 /* Upon entering normal state, the server is expected to retransmit
1749 * all pending binding updates. This is a good opportunity to
1750 * rebalance the pool (potentially making new pending updates),
1751 * which also schedules the next pool rebalance.
1753 dhcp_failover_pool_balance(state
);
1754 dhcp_failover_generate_update_queue(state
, 0);
1756 if (state
->update_queue_tail
!= NULL
) {
1757 dhcp_failover_send_updates(state
);
1758 log_info("Sending updates to %s.", state
->name
);
1763 case potential_conflict
:
1764 if (state
-> i_am
== primary
)
1765 dhcp_failover_send_update_request (state
);
1769 #if defined (DEBUG_FAILOVER_TIMING)
1770 log_info ("add_timeout +15 %s",
1771 "dhcp_failover_startup_timeout");
1773 add_timeout (cur_time
+ 15,
1774 dhcp_failover_startup_timeout
,
1776 (tvref_t
)omapi_object_reference
,
1778 omapi_object_dereference
);
1781 /* If we come back in recover_wait and there's still waiting
1782 to do, set a timeout. */
1784 if (state
-> me
.stos
+ state
-> mclt
> cur_time
) {
1785 #if defined (DEBUG_FAILOVER_TIMING)
1786 log_info ("add_timeout +%d %s",
1788 state
-> me
.stos
+ state
-> mclt
),
1789 "dhcp_failover_startup_timeout");
1791 add_timeout ((int)(state
-> me
.stos
+ state
-> mclt
),
1792 dhcp_failover_recover_done
,
1794 (tvref_t
)omapi_object_reference
,
1796 omapi_object_dereference
);
1798 dhcp_failover_recover_done (state
);
1802 /* XXX: We're supposed to calculate if updreq or updreqall is
1803 * needed. In theory, we should only have to updreqall if we
1804 * are positive we lost our stable storage.
1806 if (state
-> link_to_peer
)
1807 dhcp_failover_send_update_request_all (state
);
1811 /* For every expired lease, set a timeout for it to become free. */
1812 for (s
= shared_networks
; s
; s
= s
-> next
) {
1813 for (p
= s
-> pools
; p
; p
= p
-> next
) {
1814 if (p
-> failover_peer
== state
) {
1815 for (l
= p
->expired
; l
; l
= l
->next
) {
1816 l
->tsfp
= state
->me
.stos
+ state
->mclt
;
1817 l
->sort_time
= (l
->tsfp
> l
->ends
) ?
1821 (p
->expired
->sort_time
< p
->next_event_time
)) {
1823 p
->next_event_time
= p
->expired
->sort_time
;
1824 #if defined (DEBUG_FAILOVER_TIMING)
1825 log_info ("add_timeout +%d %s",
1826 (int)(cur_time
- p
->next_event_time
),
1829 add_timeout(p
->next_event_time
, pool_timer
, p
,
1830 (tvref_t
)pool_reference
,
1831 (tvunref_t
)pool_dereference
);
1843 return ISC_R_SUCCESS
;
1846 isc_result_t
dhcp_failover_peer_state_changed (dhcp_failover_state_t
*state
,
1847 failover_message_t
*msg
)
1849 enum failover_state previous_state
= state
-> partner
.state
;
1850 enum failover_state new_state
;
1853 new_state
= msg
-> server_state
;
1854 startupp
= (msg
-> server_flags
& FTF_SERVER_STARTUP
) ? 1 : 0;
1856 if (state
-> partner
.state
== new_state
&& state
-> me
.state
) {
1857 switch (state
-> me
.state
) {
1859 dhcp_failover_set_state (state
, state
-> saved_state
);
1860 return ISC_R_SUCCESS
;
1864 case potential_conflict
:
1869 return ISC_R_SUCCESS
;
1871 /* If we get a peer state change when we're
1872 disconnected, we always process it. */
1874 case communications_interrupted
:
1875 case resolution_interrupted
:
1881 log_fatal("Impossible case at %s:%d.", MDL
);
1886 state
-> partner
.state
= new_state
;
1888 log_info ("failover peer %s: peer moves from %s to %s",
1890 dhcp_failover_state_name_print (previous_state
),
1891 dhcp_failover_state_name_print (state
-> partner
.state
));
1893 if (!write_failover_state (state
) || !commit_leases ()) {
1894 /* This is bad, but it's not fatal. Of course, if we
1895 can't write to the lease database, we're not going to
1896 get much done anyway. */
1897 log_error ("Unable to record current failover state for %s",
1901 /* Quickly validate the new state as being one of the 13 known
1904 switch (new_state
) {
1908 case communications_interrupted
:
1910 case potential_conflict
:
1915 case resolution_interrupted
:
1921 log_error("failover peer %s: Invalid state: %d", state
->name
,
1923 dhcp_failover_set_state(state
, shut_down
);
1924 return ISC_R_SUCCESS
;
1927 /* Do any state transitions that are required as a result of the
1928 peer's state transition. */
1930 switch (state
-> me
.state
== startup
?
1931 state
-> saved_state
: state
-> me
.state
) {
1933 switch (new_state
) {
1935 dhcp_failover_state_pool_check (state
);
1939 if (state
-> me
.state
== startup
)
1940 dhcp_failover_set_state (state
, recover
);
1942 dhcp_failover_set_state (state
,
1943 potential_conflict
);
1946 case potential_conflict
:
1947 case resolution_interrupted
:
1949 /* None of these transitions should ever occur. */
1950 log_error("Peer %s: Invalid state transition %s "
1951 "to %s.", state
->name
,
1952 dhcp_failover_state_name_print(previous_state
),
1953 dhcp_failover_state_name_print(new_state
));
1954 dhcp_failover_set_state (state
, shut_down
);
1959 dhcp_failover_set_state (state
, partner_down
);
1963 dhcp_failover_set_state (state
,
1964 communications_interrupted
);
1968 /* recover_wait, recover_done, unknown_state, startup,
1969 * communications_interrupted
1976 switch (new_state
) {
1978 log_info ("failover peer %s: requesting %s",
1979 state
-> name
, "full update from peer");
1980 /* Don't send updreqall if we're really in the
1981 startup state, because that will result in two
1983 if (state
-> me
.state
== recover
)
1984 dhcp_failover_send_update_request_all (state
);
1987 case potential_conflict
:
1988 case resolution_interrupted
:
1991 dhcp_failover_set_state (state
, potential_conflict
);
1995 case communications_interrupted
:
1996 /* We're supposed to send an update request at this
1998 /* XXX we don't currently have code here to do any
1999 XXX clever detection of when we should send an
2000 XXX UPDREQALL message rather than an UPDREQ
2001 XXX message. What to do, what to do? */
2002 /* Currently when we enter recover state, no matter
2003 * the reason, we send an UPDREQALL. So, it makes
2004 * the most sense to stick to that until something
2006 * Furthermore, we only want to send the update
2007 * request if we are not in startup state.
2009 if (state
-> me
.state
== recover
)
2010 dhcp_failover_send_update_request_all (state
);
2014 /* XXX We're not explicitly told what to do in this
2015 XXX case, but this transition is consistent with
2016 XXX what is elsewhere in the draft. */
2017 dhcp_failover_set_state (state
, partner_down
);
2020 /* We can't really do anything in this case. */
2022 /* paused, recover_done, recover_wait, unknown_state,
2029 case potential_conflict
:
2030 switch (new_state
) {
2032 /* This is an illegal transition. */
2033 log_error("Peer %s moves to normal during conflict "
2034 "resolution - panic, shutting down.",
2036 dhcp_failover_set_state(state
, shut_down
);
2040 if (previous_state
== potential_conflict
)
2041 dhcp_failover_send_update_request (state
);
2043 log_error("Peer %s: Unexpected move to "
2044 "conflict-done.", state
->name
);
2049 case potential_conflict
:
2051 case communications_interrupted
:
2052 case resolution_interrupted
:
2057 dhcp_failover_set_state (state
, recover
);
2061 dhcp_failover_set_state (state
, partner_down
);
2065 /* unknown_state, startup */
2071 switch (new_state
) {
2074 dhcp_failover_set_state(state
, new_state
);
2078 log_fatal("Peer %s: Invalid attempt to move from %s "
2079 "to %s while local state is conflict-done.",
2081 dhcp_failover_state_name_print(previous_state
),
2082 dhcp_failover_state_name_print(new_state
));
2087 /* Take no action if other server is starting up. */
2091 switch (new_state
) {
2092 /* This is where we should be. */
2098 dhcp_failover_set_state (state
, normal
);
2102 case potential_conflict
:
2104 case communications_interrupted
:
2105 case resolution_interrupted
:
2107 dhcp_failover_set_state (state
, potential_conflict
);
2111 /* shut_down, paused, unknown_state, startup */
2116 case communications_interrupted
:
2117 switch (new_state
) {
2119 /* Stick with the status quo. */
2122 /* If we're in communications-interrupted and an
2123 amnesiac peer connects, go to the partner_down
2124 state immediately. */
2126 dhcp_failover_set_state (state
, partner_down
);
2130 case communications_interrupted
:
2133 /* XXX so we don't need to do this specially in
2134 XXX the CONNECT and CONNECTACK handlers. */
2135 dhcp_failover_send_updates (state
);
2136 dhcp_failover_set_state (state
, normal
);
2139 case potential_conflict
:
2141 case resolution_interrupted
:
2143 dhcp_failover_set_state (state
, potential_conflict
);
2147 dhcp_failover_set_state (state
, partner_down
);
2151 /* unknown_state, startup */
2156 case resolution_interrupted
:
2157 switch (new_state
) {
2160 case potential_conflict
:
2162 case communications_interrupted
:
2163 case resolution_interrupted
:
2167 dhcp_failover_set_state (state
, potential_conflict
);
2171 dhcp_failover_set_state (state
, partner_down
);
2175 /* paused, unknown_state, startup */
2180 /* Make no transitions while in recover_wait...just wait. */
2185 switch (new_state
) {
2187 log_error("Both servers have entered recover-done!");
2189 dhcp_failover_set_state (state
, normal
);
2193 dhcp_failover_set_state (state
, partner_down
);
2197 /* potential_conflict, partner_down,
2198 * communications_interrupted, resolution_interrupted,
2199 * paused, recover, recover_wait, unknown_state,
2206 /* We are essentially dead in the water when we're in
2207 either shut_down or paused states, and do not do any
2208 automatic state transitions. */
2213 /* XXX: Shouldn't this be a fatal condition? */
2218 log_fatal("Impossible condition at %s:%d.", MDL
);
2223 /* If we didn't make a transition out of startup as a result of
2224 the peer's state change, do it now as a result of the fact that
2225 we got a state change from the peer. */
2226 if (state
-> me
.state
== startup
&& state
-> saved_state
!= startup
)
2227 dhcp_failover_set_state (state
, state
-> saved_state
);
2229 /* For now, just set the service state based on the peer's state
2231 dhcp_failover_set_service_state (state
);
2233 return ISC_R_SUCCESS
;
2236 /* Balance operation manual entry. */
2238 dhcp_failover_pool_balance(dhcp_failover_state_t
*state
)
2240 /* Cancel pending event. */
2241 cancel_timeout(dhcp_failover_pool_rebalance
, state
);
2242 state
->sched_balance
= 0;
2244 dhcp_failover_pool_dobalance(state
);
2247 /* Balance operation entry from timer event. */
2249 dhcp_failover_pool_rebalance(void *failover_state
)
2251 dhcp_failover_state_t
*state
;
2253 state
= (dhcp_failover_state_t
*)failover_state
;
2255 /* Clear scheduled event indicator. */
2256 state
->sched_balance
= 0;
2258 if (dhcp_failover_pool_dobalance(state
))
2259 dhcp_failover_send_updates(state
);
2262 /* Balance operation entry from POOLREQ protocol message. */
2264 dhcp_failover_pool_reqbalance(dhcp_failover_state_t
*state
)
2268 /* Cancel pending event. */
2269 cancel_timeout(dhcp_failover_pool_rebalance
, state
);
2270 state
->sched_balance
= 0;
2272 queued
= dhcp_failover_pool_dobalance(state
);
2274 dhcp_failover_send_poolresp(state
, queued
);
2277 dhcp_failover_send_updates(state
);
2279 log_info("peer %s: Got POOLREQ, answering negatively! "
2280 "Peer may be out of leases or database inconsistent.",
2284 /* Do the meat of the work common to all forms of pool rebalance. */
2285 static int dhcp_failover_pool_dobalance(dhcp_failover_state_t
*state
)
2287 int lts
, total
, thresh
, hold
, pass
;
2288 int leases_queued
= 0;
2290 struct lease
*lp
= (struct lease
*)0;
2291 struct lease
*next
= (struct lease
*)0;
2292 struct shared_network
*s
;
2294 binding_state_t peer_lease_state
;
2295 binding_state_t my_lease_state
;
2297 int (*log_func
)(const char *, ...);
2300 if (state
-> me
.state
!= normal
)
2303 state
->last_balance
= cur_time
;
2305 for (s
= shared_networks
; s
; s
= s
->next
) {
2306 for (p
= s
->pools
; p
; p
= p
->next
) {
2307 if (p
->failover_peer
!= state
)
2310 /* Right now we're giving the peer half of the free leases.
2311 If we have more leases than the peer (i.e., more than
2312 half), then the number of leases we have, less the number
2313 of leases the peer has, will be how many more leases we
2314 have than the peer has. So if we send half that number
2315 to the peer, we should be even. */
2316 if (p
->failover_peer
->i_am
== primary
) {
2317 lts
= (p
->free_leases
- p
->backup_leases
) / 2;
2318 peer_lease_state
= FTS_BACKUP
;
2319 my_lease_state
= FTS_FREE
;
2322 lts
= (p
->backup_leases
- p
->free_leases
) / 2;
2323 peer_lease_state
= FTS_FREE
;
2324 my_lease_state
= FTS_BACKUP
;
2328 total
= p
->backup_leases
+ p
->free_leases
;
2330 thresh
= ((total
* state
->max_lease_misbalance
) + 50) / 100;
2331 hold
= ((total
* state
->max_lease_ownership
) + 50) / 100;
2333 log_info("balancing pool %lx %s total %d free %d "
2334 "backup %d lts %d max-own (+/-)%d",
2336 (p
->shared_network
?
2337 p
->shared_network
->name
: ""), p
->lease_count
,
2338 p
->free_leases
, p
->backup_leases
, lts
, hold
);
2340 /* If lts is in the negatives (we need leases) more than
2341 * negative double the thresh%, panic and send poolreq to
2342 * hopefully wake up the peer.
2344 if (!reqsent
&& (lts
< (thresh
* -2))) {
2345 dhcp_failover_send_poolreq(state
);
2349 /* In the first pass, try to allocate leases to the
2350 * peer which it would normally be responsible for (if
2351 * the lease has a hardware address or client-identifier,
2352 * and the load-balance-algorithm chooses the peer to
2353 * answer that address), up to a hold% excess in the peer's
2354 * favor. In the second pass, just send the oldest (first
2355 * on the list) leases up to a hold% excess in our favor.
2357 * This could make for additional pool rebalance
2358 * events, but preserving MAC possession should be
2362 lease_reference(&lp
, *lq
, MDL
);
2364 /* In the case where there are 2 leases, hold is zero, and
2365 * lts is 1 if both leases are on the local server. If
2366 * there is only 1 lease, both lts and hold are zero. Let's
2367 * not play ping pong.
2369 while (lp
&& (lts
> (pass
? hold
: -hold
))) {
2371 lease_dereference(&next
, MDL
);
2373 lease_reference(&next
, lp
->next
, MDL
);
2375 if (pass
|| peer_wants_lease(lp
)) {
2378 lp
->next_binding_state
= peer_lease_state
;
2379 lp
->tstp
= cur_time
;
2380 lp
->starts
= cur_time
;
2382 if (!supersede_lease(lp
, NULL
, 0, 1, 0) ||
2384 log_error("can't commit lease %s on "
2385 "giveaway", piaddr(lp
->ip_addr
));
2388 lease_dereference(&lp
, MDL
);
2390 lease_reference(&lp
, next
, MDL
);
2393 lease_reference(&lp
, *lq
, MDL
);
2398 lease_dereference(&next
, MDL
);
2400 lease_dereference(&lp
, MDL
);
2403 result
= "IMBALANCED";
2404 log_func
= log_error
;
2406 result
= "balanced";
2407 log_func
= log_info
;
2410 log_func("%s pool %lx %s total %d free %d backup %d "
2411 "lts %d max-misbal %d", result
, (unsigned long)p
,
2412 (p
->shared_network
?
2413 p
->shared_network
->name
: ""), p
->lease_count
,
2414 p
->free_leases
, p
->backup_leases
, lts
, thresh
);
2416 /* Recalculate next rebalance event timer. */
2417 dhcp_failover_pool_check(p
);
2424 return leases_queued
;
2427 /* dhcp_failover_pool_check: Called whenever FREE or BACKUP leases change
2428 * states, on both servers. Check the scheduled time to rebalance the pool
2429 * and lower it if applicable.
2432 dhcp_failover_pool_check(struct pool
*pool
)
2434 dhcp_failover_state_t
*peer
;
2437 peer
= pool
->failover_peer
;
2439 if(!peer
|| peer
->me
.state
!= normal
)
2442 /* Estimate the time left until lease exhaustion.
2443 * The first lease on the backup or free lists is also the oldest
2444 * lease. It is reasonable to guess that it will take at least
2445 * as much time for a pool to run out of leases, as the present
2446 * age of the oldest lease (seconds since it expired).
2448 * Note that this isn't so sane of an assumption if the oldest
2449 * lease is a virgin (ends = 0), we wind up sending this against
2450 * the max_balance bounds check.
2452 if(pool
->free
&& pool
->free
->ends
< cur_time
)
2453 est1
= cur_time
- pool
->free
->ends
;
2457 if(pool
->backup
&& pool
->backup
->ends
< cur_time
)
2458 est2
= cur_time
- pool
->backup
->ends
;
2462 /* We don't want to schedule rebalance for when we think we'll run
2463 * out of leases, we want to schedule the rebalance for when we think
2464 * the disparity will be 'large enough' to warrant action.
2466 est1
= ((est1
* peer
->max_lease_misbalance
) + 50) / 100;
2467 est2
= ((est2
* peer
->max_lease_misbalance
) + 50) / 100;
2469 /* Guess when the local system will begin issuing POOLREQ panic
2470 * attacks because "max_lease_misbalance*2" has been exceeded.
2472 if(peer
->i_am
== primary
)
2477 /* Select the smallest time. */
2481 /* Bounded by the maximum configured value. */
2482 if(est1
> peer
->max_balance
)
2483 est1
= peer
->max_balance
;
2485 /* Project this time into the future. */
2488 /* Do not move the time down under the minimum. */
2489 est2
= peer
->last_balance
+ peer
->min_balance
;
2490 if(peer
->last_balance
&& (est1
< est2
))
2493 /* Introduce a random delay. */
2494 est1
+= random() % 5;
2496 /* Do not move the time forward, or reset to the same time. */
2497 if(peer
->sched_balance
) {
2498 if (est1
>= peer
->sched_balance
)
2501 /* We are about to schedule the time down, cancel the
2504 cancel_timeout(dhcp_failover_pool_rebalance
, peer
);
2507 /* The time is different, and lower, use it. */
2508 peer
->sched_balance
= est1
;
2510 #if defined(DEBUG_FAILOVER_TIMING)
2511 log_info("add_timeout +%d dhcp_failover_pool_rebalance",
2514 add_timeout(est1
, dhcp_failover_pool_rebalance
, peer
,
2515 (tvref_t
)dhcp_failover_state_reference
,
2516 (tvunref_t
)dhcp_failover_state_dereference
);
2519 int dhcp_failover_state_pool_check (dhcp_failover_state_t
*state
)
2521 struct shared_network
*s
;
2524 for (s
= shared_networks
; s
; s
= s
-> next
) {
2525 for (p
= s
-> pools
; p
; p
= p
-> next
) {
2526 if (p
-> failover_peer
!= state
)
2528 dhcp_failover_pool_check (p
);
2534 isc_result_t
dhcp_failover_send_updates (dhcp_failover_state_t
*state
)
2536 struct lease
*lp
= (struct lease
*)0;
2537 isc_result_t status
;
2539 /* Can't update peer if we're not talking to it! */
2540 if (!state
-> link_to_peer
)
2541 return ISC_R_SUCCESS
;
2543 /* If there are acks pending, transmit them prior to potentialy
2544 * sending new updates for the same lease.
2546 if (state
->toack_queue_head
!= NULL
)
2547 dhcp_failover_send_acks(state
);
2549 while ((state
-> partner
.max_flying_updates
>
2550 state
-> cur_unacked_updates
) && state
-> update_queue_head
) {
2551 /* Grab the head of the update queue. */
2552 lease_reference (&lp
, state
-> update_queue_head
, MDL
);
2554 /* Send the update to the peer. */
2555 status
= dhcp_failover_send_bind_update (state
, lp
);
2556 if (status
!= ISC_R_SUCCESS
) {
2557 lease_dereference (&lp
, MDL
);
2560 lp
-> flags
&= ~ON_UPDATE_QUEUE
;
2562 /* Take it off the head of the update queue and put the next
2563 item in the update queue at the head. */
2564 lease_dereference (&state
-> update_queue_head
, MDL
);
2565 if (lp
-> next_pending
) {
2566 lease_reference (&state
-> update_queue_head
,
2567 lp
-> next_pending
, MDL
);
2568 lease_dereference (&lp
-> next_pending
, MDL
);
2570 lease_dereference (&state
-> update_queue_tail
, MDL
);
2573 if (state
-> ack_queue_head
) {
2575 (&state
-> ack_queue_tail
-> next_pending
,
2577 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2579 lease_reference (&state
-> ack_queue_head
, lp
, MDL
);
2581 #if defined (POINTER_DEBUG)
2582 if (lp
-> next_pending
) {
2583 log_error ("ack_queue_tail: lp -> next_pending");
2587 lease_reference (&state
-> ack_queue_tail
, lp
, MDL
);
2588 lp
-> flags
|= ON_ACK_QUEUE
;
2589 lease_dereference (&lp
, MDL
);
2591 /* Count the object as an unacked update. */
2592 state
-> cur_unacked_updates
++;
2594 return ISC_R_SUCCESS
;
2597 /* Queue an update for a lease. Always returns 1 at this point - it's
2598 not an error for this to be called on a lease for which there's no
2601 int dhcp_failover_queue_update (struct lease
*lease
, int immediate
)
2603 dhcp_failover_state_t
*state
;
2605 if (!lease
-> pool
||
2606 !lease
-> pool
-> failover_peer
)
2609 /* If it's already on the update queue, leave it there. */
2610 if (lease
-> flags
& ON_UPDATE_QUEUE
)
2613 /* Get the failover state structure for this lease. */
2614 state
= lease
-> pool
-> failover_peer
;
2616 /* If it's on the ack queue, take it off. */
2617 if (lease
-> flags
& ON_ACK_QUEUE
)
2618 dhcp_failover_ack_queue_remove (state
, lease
);
2620 if (state
-> update_queue_head
) {
2621 lease_reference (&state
-> update_queue_tail
-> next_pending
,
2623 lease_dereference (&state
-> update_queue_tail
, MDL
);
2625 lease_reference (&state
-> update_queue_head
, lease
, MDL
);
2627 #if defined (POINTER_DEBUG)
2628 if (lease
-> next_pending
) {
2629 log_error ("next pending on update queue lease.");
2630 #if defined (DEBUG_RC_HISTORY)
2631 dump_rc_history (lease
);
2636 lease_reference (&state
-> update_queue_tail
, lease
, MDL
);
2637 lease
-> flags
|= ON_UPDATE_QUEUE
;
2639 dhcp_failover_send_updates (state
);
2643 int dhcp_failover_send_acks (dhcp_failover_state_t
*state
)
2645 failover_message_t
*msg
= (failover_message_t
*)0;
2647 /* Must commit all leases prior to acking them. */
2648 if (!commit_leases ())
2651 while (state
-> toack_queue_head
) {
2652 failover_message_reference
2653 (&msg
, state
-> toack_queue_head
, MDL
);
2654 failover_message_dereference
2655 (&state
-> toack_queue_head
, MDL
);
2657 failover_message_reference
2658 (&state
-> toack_queue_head
, msg
-> next
, MDL
);
2661 dhcp_failover_send_bind_ack (state
, msg
, 0, (const char *)0);
2663 failover_message_dereference (&msg
, MDL
);
2666 if (state
-> toack_queue_tail
)
2667 failover_message_dereference (&state
-> toack_queue_tail
, MDL
);
2668 state
-> pending_acks
= 0;
2673 void dhcp_failover_toack_queue_timeout (void *vs
)
2675 dhcp_failover_state_t
*state
= vs
;
2677 #if defined (DEBUG_FAILOVER_TIMING)
2678 log_info ("dhcp_failover_toack_queue_timeout");
2681 dhcp_failover_send_acks (state
);
2684 /* Queue an ack for a message. There is currently no way to queue a
2685 negative ack -- these need to be sent directly. */
2687 int dhcp_failover_queue_ack (dhcp_failover_state_t
*state
,
2688 failover_message_t
*msg
)
2690 if (state
-> toack_queue_head
) {
2691 failover_message_reference
2692 (&state
-> toack_queue_tail
-> next
, msg
, MDL
);
2693 failover_message_dereference (&state
-> toack_queue_tail
, MDL
);
2695 failover_message_reference (&state
-> toack_queue_head
,
2698 failover_message_reference (&state
-> toack_queue_tail
, msg
, MDL
);
2700 state
-> pending_acks
++;
2702 /* Flush the toack queue whenever we exceed half the number of
2703 allowed unacked updates. */
2704 if (state
-> pending_acks
>= state
-> partner
.max_flying_updates
/ 2) {
2705 dhcp_failover_send_acks (state
);
2708 /* Schedule a timeout to flush the ack queue. */
2709 if (state
-> pending_acks
> 0) {
2710 #if defined (DEBUG_FAILOVER_TIMING)
2711 log_info ("add_timeout +2 %s",
2712 "dhcp_failover_toack_queue_timeout");
2714 add_timeout (cur_time
+ 2,
2715 dhcp_failover_toack_queue_timeout
, state
,
2716 (tvref_t
)dhcp_failover_state_reference
,
2717 (tvunref_t
)dhcp_failover_state_dereference
);
2723 void dhcp_failover_ack_queue_remove (dhcp_failover_state_t
*state
,
2724 struct lease
*lease
)
2728 if (!(lease
-> flags
& ON_ACK_QUEUE
))
2731 if (state
-> ack_queue_head
== lease
) {
2732 lease_dereference (&state
-> ack_queue_head
, MDL
);
2733 if (lease
-> next_pending
) {
2734 lease_reference (&state
-> ack_queue_head
,
2735 lease
-> next_pending
, MDL
);
2736 lease_dereference (&lease
-> next_pending
, MDL
);
2738 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2741 for (lp
= state
-> ack_queue_head
;
2742 lp
&& lp
-> next_pending
!= lease
;
2743 lp
= lp
-> next_pending
)
2749 lease_dereference (&lp
-> next_pending
, MDL
);
2750 if (lease
-> next_pending
) {
2751 lease_reference (&lp
-> next_pending
,
2752 lease
-> next_pending
, MDL
);
2753 lease_dereference (&lease
-> next_pending
, MDL
);
2755 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2756 if (lp
-> next_pending
) {
2757 log_error ("state -> ack_queue_tail");
2760 lease_reference (&state
-> ack_queue_tail
, lp
, MDL
);
2764 lease
-> flags
&= ~ON_ACK_QUEUE
;
2765 /* Multiple acks on one XID is an error and may cause badness. */
2766 lease
->last_xid
= 0;
2767 /* XXX: this violates draft-failover. We can't send another
2768 * update just because we forgot about an old one that hasn't
2771 state
-> cur_unacked_updates
--;
2774 * When updating leases as a result of an ack, we defer the commit
2775 * for performance reasons. When there are no more acks pending,
2778 if (state
-> cur_unacked_updates
== 0) {
2783 isc_result_t
dhcp_failover_state_set_value (omapi_object_t
*h
,
2785 omapi_data_string_t
*name
,
2786 omapi_typed_data_t
*value
)
2788 isc_result_t status
;
2790 if (h
-> type
!= dhcp_type_failover_state
)
2791 return ISC_R_INVALIDARG
;
2793 /* This list of successful returns is completely wrong, but the
2794 fastest way to make dhcpctl do something vaguely sane when
2795 you try to change the local state. */
2797 if (!omapi_ds_strcmp (name
, "name")) {
2798 return ISC_R_SUCCESS
;
2799 } else if (!omapi_ds_strcmp (name
, "partner-address")) {
2800 return ISC_R_SUCCESS
;
2801 } else if (!omapi_ds_strcmp (name
, "local-address")) {
2802 return ISC_R_SUCCESS
;
2803 } else if (!omapi_ds_strcmp (name
, "partner-port")) {
2804 return ISC_R_SUCCESS
;
2805 } else if (!omapi_ds_strcmp (name
, "local-port")) {
2806 return ISC_R_SUCCESS
;
2807 } else if (!omapi_ds_strcmp (name
, "max-outstanding-updates")) {
2808 return ISC_R_SUCCESS
;
2809 } else if (!omapi_ds_strcmp (name
, "mclt")) {
2810 return ISC_R_SUCCESS
;
2811 } else if (!omapi_ds_strcmp (name
, "load-balance-max-secs")) {
2812 return ISC_R_SUCCESS
;
2813 } else if (!omapi_ds_strcmp (name
, "load-balance-hba")) {
2814 return ISC_R_SUCCESS
;
2815 } else if (!omapi_ds_strcmp (name
, "partner-state")) {
2816 return ISC_R_SUCCESS
;
2817 } else if (!omapi_ds_strcmp (name
, "local-state")) {
2819 status
= omapi_get_int_value (&l
, value
);
2820 if (status
!= ISC_R_SUCCESS
)
2822 return dhcp_failover_set_state ((dhcp_failover_state_t
*)h
, l
);
2823 } else if (!omapi_ds_strcmp (name
, "partner-stos")) {
2824 return ISC_R_SUCCESS
;
2825 } else if (!omapi_ds_strcmp (name
, "local-stos")) {
2826 return ISC_R_SUCCESS
;
2827 } else if (!omapi_ds_strcmp (name
, "hierarchy")) {
2828 return ISC_R_SUCCESS
;
2829 } else if (!omapi_ds_strcmp (name
, "last-packet-sent")) {
2830 return ISC_R_SUCCESS
;
2831 } else if (!omapi_ds_strcmp (name
, "last-timestamp-received")) {
2832 return ISC_R_SUCCESS
;
2833 } else if (!omapi_ds_strcmp (name
, "skew")) {
2834 return ISC_R_SUCCESS
;
2835 } else if (!omapi_ds_strcmp (name
, "max-response-delay")) {
2836 return ISC_R_SUCCESS
;
2837 } else if (!omapi_ds_strcmp (name
, "cur-unacked-updates")) {
2838 return ISC_R_SUCCESS
;
2841 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
2842 return (*(h
-> inner
-> type
-> set_value
))
2843 (h
-> inner
, id
, name
, value
);
2844 return ISC_R_NOTFOUND
;
2847 void dhcp_failover_keepalive (void *vs
)
2851 void dhcp_failover_reconnect (void *vs
)
2853 dhcp_failover_state_t
*state
= vs
;
2854 isc_result_t status
;
2856 #if defined (DEBUG_FAILOVER_TIMING)
2857 log_info ("dhcp_failover_reconnect");
2859 /* If we already connected the other way, let the connection
2860 recovery code initiate any retry that may be required. */
2861 if (state
-> link_to_peer
)
2864 status
= dhcp_failover_link_initiate ((omapi_object_t
*)state
);
2865 if (status
!= ISC_R_SUCCESS
&& status
!= ISC_R_INCOMPLETE
) {
2866 log_info ("failover peer %s: %s", state
-> name
,
2867 isc_result_totext (status
));
2868 #if defined (DEBUG_FAILOVER_TIMING)
2869 log_info ("add_timeout +90 %s",
2870 "dhcp_failover_listener_restart");
2872 add_timeout (cur_time
+ 90,
2873 dhcp_failover_listener_restart
, state
,
2874 (tvref_t
)dhcp_failover_state_reference
,
2875 (tvunref_t
)dhcp_failover_state_dereference
);
2879 void dhcp_failover_startup_timeout (void *vs
)
2881 dhcp_failover_state_t
*state
= vs
;
2883 #if defined (DEBUG_FAILOVER_TIMING)
2884 log_info ("dhcp_failover_startup_timeout");
2887 dhcp_failover_state_transition (state
, "disconnect");
2890 void dhcp_failover_link_startup_timeout (void *vl
)
2892 dhcp_failover_link_t
*link
= vl
;
2895 for (p
= (omapi_object_t
*)link
; p
-> inner
; p
= p
-> inner
)
2897 for (; p
; p
= p
-> outer
)
2898 if (p
-> type
== omapi_type_connection
)
2901 log_info ("failover: link startup timeout");
2902 omapi_disconnect (p
, 1);
2906 void dhcp_failover_listener_restart (void *vs
)
2908 dhcp_failover_state_t
*state
= vs
;
2909 isc_result_t status
;
2911 #if defined (DEBUG_FAILOVER_TIMING)
2912 log_info ("dhcp_failover_listener_restart");
2915 status
= dhcp_failover_listen ((omapi_object_t
*)state
);
2916 if (status
!= ISC_R_SUCCESS
) {
2917 log_info ("failover peer %s: %s", state
-> name
,
2918 isc_result_totext (status
));
2919 #if defined (DEBUG_FAILOVER_TIMING)
2920 log_info ("add_timeout +90 %s",
2921 "dhcp_failover_listener_restart");
2923 add_timeout (cur_time
+ 90,
2924 dhcp_failover_listener_restart
, state
,
2925 (tvref_t
)dhcp_failover_state_reference
,
2926 (tvunref_t
)dhcp_failover_state_dereference
);
2930 isc_result_t
dhcp_failover_state_get_value (omapi_object_t
*h
,
2932 omapi_data_string_t
*name
,
2933 omapi_value_t
**value
)
2935 dhcp_failover_state_t
*s
;
2936 struct option_cache
*oc
;
2937 struct data_string ds
;
2938 isc_result_t status
;
2940 if (h
-> type
!= dhcp_type_failover_state
)
2941 return ISC_R_INVALIDARG
;
2942 s
= (dhcp_failover_state_t
*)h
;
2944 if (!omapi_ds_strcmp (name
, "name")) {
2946 return omapi_make_string_value (value
,
2947 name
, s
-> name
, MDL
);
2948 return ISC_R_NOTFOUND
;
2949 } else if (!omapi_ds_strcmp (name
, "partner-address")) {
2950 oc
= s
-> partner
.address
;
2952 memset (&ds
, 0, sizeof ds
);
2953 if (!evaluate_option_cache (&ds
, (struct packet
*)0,
2955 (struct client_state
*)0,
2956 (struct option_state
*)0,
2957 (struct option_state
*)0,
2958 &global_scope
, oc
, MDL
)) {
2959 return ISC_R_NOTFOUND
;
2961 status
= omapi_make_const_value (value
,
2962 name
, ds
.data
, ds
.len
, MDL
);
2963 /* Disgusting kludge: */
2964 if (oc
== s
-> me
.address
&& !s
-> server_identifier
.len
)
2965 data_string_copy (&s
-> server_identifier
, &ds
, MDL
);
2966 data_string_forget (&ds
, MDL
);
2968 } else if (!omapi_ds_strcmp (name
, "local-address")) {
2969 oc
= s
-> me
.address
;
2971 } else if (!omapi_ds_strcmp (name
, "partner-port")) {
2972 return omapi_make_int_value (value
, name
,
2973 s
-> partner
.port
, MDL
);
2974 } else if (!omapi_ds_strcmp (name
, "local-port")) {
2975 return omapi_make_int_value (value
,
2976 name
, s
-> me
.port
, MDL
);
2977 } else if (!omapi_ds_strcmp (name
, "max-outstanding-updates")) {
2978 return omapi_make_uint_value (value
, name
,
2979 s
-> me
.max_flying_updates
,
2981 } else if (!omapi_ds_strcmp (name
, "mclt")) {
2982 return omapi_make_uint_value (value
, name
, s
-> mclt
, MDL
);
2983 } else if (!omapi_ds_strcmp (name
, "load-balance-max-secs")) {
2984 return omapi_make_int_value (value
, name
,
2985 s
-> load_balance_max_secs
, MDL
);
2986 } else if (!omapi_ds_strcmp (name
, "load-balance-hba")) {
2988 return omapi_make_const_value (value
, name
,
2990 return ISC_R_NOTFOUND
;
2991 } else if (!omapi_ds_strcmp (name
, "partner-state")) {
2992 return omapi_make_uint_value (value
, name
,
2993 s
-> partner
.state
, MDL
);
2994 } else if (!omapi_ds_strcmp (name
, "local-state")) {
2995 return omapi_make_uint_value (value
, name
,
2996 s
-> me
.state
, MDL
);
2997 } else if (!omapi_ds_strcmp (name
, "partner-stos")) {
2998 return omapi_make_int_value (value
, name
,
2999 s
-> partner
.stos
, MDL
);
3000 } else if (!omapi_ds_strcmp (name
, "local-stos")) {
3001 return omapi_make_int_value (value
, name
,
3003 } else if (!omapi_ds_strcmp (name
, "hierarchy")) {
3004 return omapi_make_uint_value (value
, name
, s
-> i_am
, MDL
);
3005 } else if (!omapi_ds_strcmp (name
, "last-packet-sent")) {
3006 return omapi_make_int_value (value
, name
,
3007 s
-> last_packet_sent
, MDL
);
3008 } else if (!omapi_ds_strcmp (name
, "last-timestamp-received")) {
3009 return omapi_make_int_value (value
, name
,
3010 s
-> last_timestamp_received
,
3012 } else if (!omapi_ds_strcmp (name
, "skew")) {
3013 return omapi_make_int_value (value
, name
, s
-> skew
, MDL
);
3014 } else if (!omapi_ds_strcmp (name
, "max-response-delay")) {
3015 return omapi_make_uint_value (value
, name
,
3016 s
-> me
.max_response_delay
,
3018 } else if (!omapi_ds_strcmp (name
, "cur-unacked-updates")) {
3019 return omapi_make_int_value (value
, name
,
3020 s
-> cur_unacked_updates
, MDL
);
3023 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
3024 return (*(h
-> inner
-> type
-> get_value
))
3025 (h
-> inner
, id
, name
, value
);
3026 return ISC_R_NOTFOUND
;
3029 isc_result_t
dhcp_failover_state_destroy (omapi_object_t
*h
,
3030 const char *file
, int line
)
3032 dhcp_failover_state_t
*s
;
3034 if (h
-> type
!= dhcp_type_failover_state
)
3035 return ISC_R_INVALIDARG
;
3036 s
= (dhcp_failover_state_t
*)h
;
3038 if (s
-> link_to_peer
)
3039 dhcp_failover_link_dereference (&s
-> link_to_peer
, file
, line
);
3041 dfree (s
-> name
, MDL
);
3042 s
-> name
= (char *)0;
3044 if (s
-> partner
.address
)
3045 option_cache_dereference (&s
-> partner
.address
, file
, line
);
3046 if (s
-> me
.address
)
3047 option_cache_dereference (&s
-> me
.address
, file
, line
);
3049 dfree (s
-> hba
, file
, line
);
3050 s
-> hba
= (u_int8_t
*)0;
3052 if (s
-> update_queue_head
)
3053 lease_dereference (&s
-> update_queue_head
, file
, line
);
3054 if (s
-> update_queue_tail
)
3055 lease_dereference (&s
-> update_queue_tail
, file
, line
);
3056 if (s
-> ack_queue_head
)
3057 lease_dereference (&s
-> ack_queue_head
, file
, line
);
3058 if (s
-> ack_queue_tail
)
3059 lease_dereference (&s
-> ack_queue_tail
, file
, line
);
3060 if (s
-> send_update_done
)
3061 lease_dereference (&s
-> send_update_done
, file
, line
);
3062 if (s
-> toack_queue_head
)
3063 failover_message_dereference (&s
-> toack_queue_head
,
3065 if (s
-> toack_queue_tail
)
3066 failover_message_dereference (&s
-> toack_queue_tail
,
3068 return ISC_R_SUCCESS
;
3071 /* Write all the published values associated with the object through the
3072 specified connection. */
3074 isc_result_t
dhcp_failover_state_stuff (omapi_object_t
*c
,
3078 dhcp_failover_state_t
*s
;
3079 omapi_connection_object_t
*conn
;
3080 isc_result_t status
;
3082 if (c
-> type
!= omapi_type_connection
)
3083 return ISC_R_INVALIDARG
;
3084 conn
= (omapi_connection_object_t
*)c
;
3086 if (h
-> type
!= dhcp_type_failover_state
)
3087 return ISC_R_INVALIDARG
;
3088 s
= (dhcp_failover_state_t
*)h
;
3090 status
= omapi_connection_put_name (c
, "name");
3091 if (status
!= ISC_R_SUCCESS
)
3093 status
= omapi_connection_put_string (c
, s
-> name
);
3094 if (status
!= ISC_R_SUCCESS
)
3097 status
= omapi_connection_put_name (c
, "partner-address");
3098 if (status
!= ISC_R_SUCCESS
)
3100 status
= omapi_connection_put_uint32 (c
, sizeof s
-> partner
.address
);
3101 if (status
!= ISC_R_SUCCESS
)
3103 status
= omapi_connection_copyin (c
, (u_int8_t
*)&s
-> partner
.address
,
3104 sizeof s
-> partner
.address
);
3105 if (status
!= ISC_R_SUCCESS
)
3108 status
= omapi_connection_put_name (c
, "partner-port");
3109 if (status
!= ISC_R_SUCCESS
)
3111 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3112 if (status
!= ISC_R_SUCCESS
)
3114 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> partner
.port
);
3115 if (status
!= ISC_R_SUCCESS
)
3118 status
= omapi_connection_put_name (c
, "local-address");
3119 if (status
!= ISC_R_SUCCESS
)
3121 status
= omapi_connection_put_uint32 (c
, sizeof s
-> me
.address
);
3122 if (status
!= ISC_R_SUCCESS
)
3124 status
= omapi_connection_copyin (c
, (u_int8_t
*)&s
-> me
.address
,
3125 sizeof s
-> me
.address
);
3126 if (status
!= ISC_R_SUCCESS
)
3129 status
= omapi_connection_put_name (c
, "local-port");
3130 if (status
!= ISC_R_SUCCESS
)
3132 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3133 if (status
!= ISC_R_SUCCESS
)
3135 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> me
.port
);
3136 if (status
!= ISC_R_SUCCESS
)
3139 status
= omapi_connection_put_name (c
, "max-outstanding-updates");
3140 if (status
!= ISC_R_SUCCESS
)
3142 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3143 if (status
!= ISC_R_SUCCESS
)
3145 status
= omapi_connection_put_uint32 (c
,
3146 s
-> me
.max_flying_updates
);
3147 if (status
!= ISC_R_SUCCESS
)
3150 status
= omapi_connection_put_name (c
, "mclt");
3151 if (status
!= ISC_R_SUCCESS
)
3153 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3154 if (status
!= ISC_R_SUCCESS
)
3156 status
= omapi_connection_put_uint32 (c
, s
-> mclt
);
3157 if (status
!= ISC_R_SUCCESS
)
3160 status
= omapi_connection_put_name (c
, "load-balance-max-secs");
3161 if (status
!= ISC_R_SUCCESS
)
3163 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3164 if (status
!= ISC_R_SUCCESS
)
3166 status
= (omapi_connection_put_uint32
3167 (c
, (u_int32_t
)s
-> load_balance_max_secs
));
3168 if (status
!= ISC_R_SUCCESS
)
3173 status
= omapi_connection_put_name (c
, "load-balance-hba");
3174 if (status
!= ISC_R_SUCCESS
)
3176 status
= omapi_connection_put_uint32 (c
, 32);
3177 if (status
!= ISC_R_SUCCESS
)
3179 status
= omapi_connection_copyin (c
, s
-> hba
, 32);
3180 if (status
!= ISC_R_SUCCESS
)
3184 status
= omapi_connection_put_name (c
, "partner-state");
3185 if (status
!= ISC_R_SUCCESS
)
3187 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3188 if (status
!= ISC_R_SUCCESS
)
3190 status
= omapi_connection_put_uint32 (c
, s
-> partner
.state
);
3191 if (status
!= ISC_R_SUCCESS
)
3194 status
= omapi_connection_put_name (c
, "local-state");
3195 if (status
!= ISC_R_SUCCESS
)
3197 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3198 if (status
!= ISC_R_SUCCESS
)
3200 status
= omapi_connection_put_uint32 (c
, s
-> me
.state
);
3201 if (status
!= ISC_R_SUCCESS
)
3204 status
= omapi_connection_put_name (c
, "partner-stos");
3205 if (status
!= ISC_R_SUCCESS
)
3207 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3208 if (status
!= ISC_R_SUCCESS
)
3210 status
= omapi_connection_put_uint32 (c
,
3211 (u_int32_t
)s
-> partner
.stos
);
3212 if (status
!= ISC_R_SUCCESS
)
3215 status
= omapi_connection_put_name (c
, "local-stos");
3216 if (status
!= ISC_R_SUCCESS
)
3218 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3219 if (status
!= ISC_R_SUCCESS
)
3221 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> me
.stos
);
3222 if (status
!= ISC_R_SUCCESS
)
3225 status
= omapi_connection_put_name (c
, "hierarchy");
3226 if (status
!= ISC_R_SUCCESS
)
3228 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3229 if (status
!= ISC_R_SUCCESS
)
3231 status
= omapi_connection_put_uint32 (c
, s
-> i_am
);
3232 if (status
!= ISC_R_SUCCESS
)
3235 status
= omapi_connection_put_name (c
, "last-packet-sent");
3236 if (status
!= ISC_R_SUCCESS
)
3238 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3239 if (status
!= ISC_R_SUCCESS
)
3241 status
= (omapi_connection_put_uint32
3242 (c
, (u_int32_t
)s
-> last_packet_sent
));
3243 if (status
!= ISC_R_SUCCESS
)
3246 status
= omapi_connection_put_name (c
, "last-timestamp-received");
3247 if (status
!= ISC_R_SUCCESS
)
3249 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3250 if (status
!= ISC_R_SUCCESS
)
3252 status
= (omapi_connection_put_uint32
3253 (c
, (u_int32_t
)s
-> last_timestamp_received
));
3254 if (status
!= ISC_R_SUCCESS
)
3257 status
= omapi_connection_put_name (c
, "skew");
3258 if (status
!= ISC_R_SUCCESS
)
3260 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3261 if (status
!= ISC_R_SUCCESS
)
3263 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> skew
);
3264 if (status
!= ISC_R_SUCCESS
)
3267 status
= omapi_connection_put_name (c
, "max-response-delay");
3268 if (status
!= ISC_R_SUCCESS
)
3270 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3271 if (status
!= ISC_R_SUCCESS
)
3273 status
= (omapi_connection_put_uint32
3274 (c
, (u_int32_t
)s
-> me
.max_response_delay
));
3275 if (status
!= ISC_R_SUCCESS
)
3278 status
= omapi_connection_put_name (c
, "cur-unacked-updates");
3279 if (status
!= ISC_R_SUCCESS
)
3281 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3282 if (status
!= ISC_R_SUCCESS
)
3284 status
= (omapi_connection_put_uint32
3285 (c
, (u_int32_t
)s
-> cur_unacked_updates
));
3286 if (status
!= ISC_R_SUCCESS
)
3289 if (h
-> inner
&& h
-> inner
-> type
-> stuff_values
)
3290 return (*(h
-> inner
-> type
-> stuff_values
)) (c
, id
,
3292 return ISC_R_SUCCESS
;
3295 isc_result_t
dhcp_failover_state_lookup (omapi_object_t
**sp
,
3297 omapi_object_t
*ref
)
3299 omapi_value_t
*tv
= (omapi_value_t
*)0;
3300 isc_result_t status
;
3301 dhcp_failover_state_t
*s
;
3304 return ISC_R_NOKEYS
;
3306 /* First see if we were sent a handle. */
3307 status
= omapi_get_value_str (ref
, id
, "handle", &tv
);
3308 if (status
== ISC_R_SUCCESS
) {
3309 status
= omapi_handle_td_lookup (sp
, tv
-> value
);
3311 omapi_value_dereference (&tv
, MDL
);
3312 if (status
!= ISC_R_SUCCESS
)
3315 /* Don't return the object if the type is wrong. */
3316 if ((*sp
) -> type
!= dhcp_type_failover_state
) {
3317 omapi_object_dereference (sp
, MDL
);
3318 return ISC_R_INVALIDARG
;
3322 /* Look the failover state up by peer name. */
3323 status
= omapi_get_value_str (ref
, id
, "name", &tv
);
3324 if (status
== ISC_R_SUCCESS
) {
3325 for (s
= failover_states
; s
; s
= s
-> next
) {
3326 unsigned l
= strlen (s
-> name
);
3327 if (l
== tv
-> value
-> u
.buffer
.len
&&
3329 tv
-> value
-> u
.buffer
.value
, l
))
3332 omapi_value_dereference (&tv
, MDL
);
3334 /* If we already have a lease, and it's not the same one,
3335 then the query was invalid. */
3336 if (*sp
&& *sp
!= (omapi_object_t
*)s
) {
3337 omapi_object_dereference (sp
, MDL
);
3338 return ISC_R_KEYCONFLICT
;
3341 omapi_object_dereference (sp
, MDL
);
3342 return ISC_R_NOTFOUND
;
3344 /* XXX fix so that hash lookup itself creates
3345 XXX the reference. */
3346 omapi_object_reference (sp
, (omapi_object_t
*)s
, MDL
);
3349 /* If we get to here without finding a lease, no valid key was
3352 return ISC_R_NOKEYS
;
3353 return ISC_R_SUCCESS
;
3356 isc_result_t
dhcp_failover_state_create (omapi_object_t
**sp
,
3359 return ISC_R_NOTIMPLEMENTED
;
3362 isc_result_t
dhcp_failover_state_remove (omapi_object_t
*sp
,
3365 return ISC_R_NOTIMPLEMENTED
;
3368 int dhcp_failover_state_match (dhcp_failover_state_t
*state
,
3369 u_int8_t
*addr
, unsigned addrlen
)
3371 struct data_string ds
;
3374 memset (&ds
, 0, sizeof ds
);
3375 if (evaluate_option_cache (&ds
, (struct packet
*)0,
3377 (struct client_state
*)0,
3378 (struct option_state
*)0,
3379 (struct option_state
*)0,
3381 state
-> partner
.address
, MDL
)) {
3382 for (i
= 0; i
+ addrlen
- 1 < ds
.len
; i
+= addrlen
) {
3383 if (!memcmp (&ds
.data
[i
],
3385 data_string_forget (&ds
, MDL
);
3389 data_string_forget (&ds
, MDL
);
3395 dhcp_failover_state_match_by_name(state
, name
)
3396 dhcp_failover_state_t
*state
;
3397 failover_option_t
*name
;
3399 if ((strlen(state
->name
) == name
->count
) &&
3400 (memcmp(state
->name
, name
->data
, name
->count
) == 0))
3406 const char *dhcp_failover_reject_reason_print (int reason
)
3408 static char resbuf
[sizeof("Undefined-255: This reason code is not defined "
3409 "in the protocol standard.")];
3411 if ((reason
> 0xff) || (reason
< 0))
3412 return "Reason code out of range.";
3415 case FTR_ILLEGAL_IP_ADDR
:
3416 return "Illegal IP address (not part of any address pool).";
3418 case FTR_FATAL_CONFLICT
:
3419 return "Fatal conflict exists: address in use by other client.";
3421 case FTR_MISSING_BINDINFO
:
3422 return "Missing binding information.";
3424 case FTR_TIMEMISMATCH
:
3425 return "Connection rejected, time mismatch too great.";
3427 case FTR_INVALID_MCLT
:
3428 return "Connection rejected, invalid MCLT.";
3430 case FTR_MISC_REJECT
:
3431 return "Connection rejected, unknown reason.";
3433 case FTR_DUP_CONNECTION
:
3434 return "Connection rejected, duplicate connection.";
3436 case FTR_INVALID_PARTNER
:
3437 return "Connection rejected, invalid failover partner.";
3439 case FTR_TLS_UNSUPPORTED
:
3440 return "TLS not supported.";
3442 case FTR_TLS_UNCONFIGURED
:
3443 return "TLS supported but not configured.";
3445 case FTR_TLS_REQUIRED
:
3446 return "TLS required but not supported by partner.";
3448 case FTR_DIGEST_UNSUPPORTED
:
3449 return "Message digest not supported.";
3451 case FTR_DIGEST_UNCONFIGURED
:
3452 return "Message digest not configured.";
3454 case FTR_VERSION_MISMATCH
:
3455 return "Protocol version mismatch.";
3457 case FTR_OUTDATED_BIND_INFO
:
3458 return "Outdated binding information.";
3460 case FTR_LESS_CRIT_BIND_INFO
:
3461 return "Less critical binding information.";
3463 case FTR_NO_TRAFFIC
:
3464 return "No traffic within sufficient time.";
3466 case FTR_HBA_CONFLICT
:
3467 return "Hash bucket assignment conflict.";
3469 case FTR_IP_NOT_RESERVED
:
3470 return "IP not reserved on this server.";
3472 case FTR_IP_DIGEST_FAILURE
:
3473 return "Message digest failed to compare.";
3475 case FTR_IP_MISSING_DIGEST
:
3476 return "Missing message digest.";
3479 return "Unknown Error.";
3482 sprintf(resbuf
, "Undefined-%d: This reason code is not defined in the "
3483 "protocol standard.", reason
);
3488 const char *dhcp_failover_state_name_print (enum failover_state state
)
3493 return "unknown-state";
3496 return "partner-down";
3501 case communications_interrupted
:
3502 return "communications-interrupted";
3504 case resolution_interrupted
:
3505 return "resolution-interrupted";
3507 case potential_conflict
:
3508 return "potential-conflict";
3514 return "recover-done";
3517 return "recover-wait";
3530 const char *dhcp_failover_message_name (unsigned type
)
3532 static char messbuf
[sizeof("unknown-message-255")];
3535 return "invalid-message";
3539 return "pool-request";
3542 return "pool-response";
3545 return "bind-update";
3553 case FTM_CONNECTACK
:
3554 return "connect-ack";
3557 return "update-request";
3560 return "update-done";
3563 return "update-request-all";
3571 case FTM_DISCONNECT
:
3572 return "disconnect";
3575 sprintf(messbuf
, "unknown-message-%u", type
);
3580 const char *dhcp_failover_option_name (unsigned type
)
3582 static char optbuf
[sizeof("unknown-option-65535")];
3585 return "invalid-option";
3588 case FTO_ADDRESSES_TRANSFERRED
:
3589 return "addresses-transferred";
3591 case FTO_ASSIGNED_IP_ADDRESS
:
3592 return "assigned-ip-address";
3594 case FTO_BINDING_STATUS
:
3595 return "binding-status";
3597 case FTO_CLIENT_IDENTIFIER
:
3598 return "client-identifier";
3609 case FTO_DELAYED_SERVICE
:
3610 return "delayed-service";
3618 case FTO_LEASE_EXPIRY
:
3619 return "lease-expiry";
3621 case FTO_MAX_UNACKED
:
3622 return "max-unacked";
3630 case FTO_MESSAGE_DIGEST
:
3631 return "message-digest";
3633 case FTO_POTENTIAL_EXPIRY
:
3634 return "potential-expiry";
3636 case FTO_PROTOCOL_VERSION
:
3637 return "protocol-version";
3639 case FTO_RECEIVE_TIMER
:
3640 return "receive-timer";
3642 case FTO_REJECT_REASON
:
3643 return "reject-reason";
3645 case FTO_RELATIONSHIP_NAME
:
3646 return "relationship-name";
3648 case FTO_REPLY_OPTIONS
:
3649 return "reply-options";
3651 case FTO_REQUEST_OPTIONS
:
3652 return "request-options";
3654 case FTO_SERVER_FLAGS
:
3655 return "server-flags";
3657 case FTO_SERVER_STATE
:
3658 return "server-state";
3666 case FTO_TLS_REQUEST
:
3667 return "tls-request";
3669 case FTO_VENDOR_CLASS
:
3670 return "vendor-class";
3672 case FTO_VENDOR_OPTIONS
:
3673 return "vendor-options";
3676 sprintf(optbuf
, "unknown-option-%u", type
);
3681 failover_option_t
*dhcp_failover_option_printf (unsigned code
,
3685 const char *fmt
, ...)
3690 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3691 * It is unclear what the effects of truncation here are, or
3692 * how that condition should be handled. It seems that this
3693 * function is used for formatting messages in the failover
3694 * command channel. For now the safest thing is for
3695 * overflow-truncation to cause a fatal log.
3698 if (vsnprintf (tbuf
, sizeof tbuf
, fmt
, va
) >= sizeof tbuf
)
3699 log_fatal ("%s: vsnprintf would truncate",
3700 "dhcp_failover_make_option");
3703 return dhcp_failover_make_option (code
, obuf
, obufix
, obufmax
,
3704 strlen (tbuf
), tbuf
);
3707 failover_option_t
*dhcp_failover_make_option (unsigned code
,
3708 char *obuf
, unsigned *obufix
,
3709 unsigned obufmax
, ...)
3712 struct failover_option_info
*info
;
3714 unsigned size
, count
;
3720 #if defined (DEBUG_FAILOVER_MESSAGES)
3724 /* Note that the failover_option structure is used differently on
3725 input than on output - on input, count is an element count, and
3726 on output it's the number of bytes total in the option, including
3727 the option code and option length. */
3728 failover_option_t option
, *op
;
3731 /* Bogus option code? */
3732 if (code
< 1 || code
> FTO_MAX
|| ft_options
[code
].type
== FT_UNDEF
) {
3733 return &null_failover_option
;
3735 info
= &ft_options
[code
];
3737 va_start (va
, obufmax
);
3739 /* Get the number of elements and the size of the buffer we need
3741 if (info
-> type
== FT_DDNS
|| info
-> type
== FT_DDNS1
) {
3742 count
= info
-> type
== FT_DDNS
? 1 : 2;
3743 size
= va_arg (va
, int) + count
;
3745 /* Find out how many items in this list. */
3746 if (info
-> num_present
)
3747 count
= info
-> num_present
;
3749 count
= va_arg (va
, int);
3751 /* Figure out size. */
3752 switch (info
-> type
) {
3759 case FT_TEXT_OR_BYTES
:
3761 txt
= va_arg (va
, char *);
3766 ilen
= va_arg (va
, unsigned);
3767 size
= count
* ilen
;
3779 /* shouldn't get here. */
3780 log_fatal ("bogus type in failover_make_option: %d",
3782 return &null_failover_option
;
3788 /* Allocate a buffer for the option. */
3789 option
.count
= size
;
3790 option
.data
= dmalloc (option
.count
, MDL
);
3793 return &null_failover_option
;
3796 /* Put in the option code and option length. */
3797 putUShort (option
.data
, code
);
3798 putUShort (&option
.data
[2], size
- 4);
3800 #if defined (DEBUG_FAILOVER_MESSAGES)
3801 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3802 * It is unclear what the effects of truncation here are, or
3803 * how that condition should be handled. It seems that this
3804 * message may be sent over the failover command channel.
3805 * For now the safest thing is for overflow-truncation to cause
3808 if (snprintf (tbuf
, sizeof tbuf
, " (%s<%d>", info
-> name
,
3809 option
.count
) >= sizeof tbuf
)
3810 log_fatal ("dhcp_failover_make_option: tbuf overflow");
3811 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3814 /* Now put in the data. */
3815 switch (info
-> type
) {
3817 for (i
= 0; i
< count
; i
++) {
3818 val
= va_arg (va
, unsigned);
3819 #if defined (DEBUG_FAILOVER_MESSAGES)
3820 /* %Audit% Cannot exceed 24 bytes. %2004.06.17,Safe% */
3821 sprintf (tbuf
, " %d", val
);
3822 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3824 option
.data
[i
+ 4] = val
;
3829 for (i
= 0; i
< count
; i
++) {
3830 iaddr
= va_arg (va
, u_int8_t
*);
3832 dfree (option
.data
, MDL
);
3833 log_error ("IP addrlen=%d, should be 4.",
3836 return &null_failover_option
;
3839 #if defined (DEBUG_FAILOVER_MESSAGES)
3840 /*%Audit% Cannot exceed 17 bytes. %2004.06.17,Safe%*/
3841 sprintf (tbuf
, " %u.%u.%u.%u",
3842 iaddr
[0], iaddr
[1], iaddr
[2], iaddr
[3]);
3843 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3845 memcpy (&option
.data
[4 + i
* ilen
], iaddr
, ilen
);
3850 for (i
= 0; i
< count
; i
++) {
3851 val
= va_arg (va
, unsigned);
3852 #if defined (DEBUG_FAILOVER_MESSAGES)
3853 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3854 sprintf (tbuf
, " %d", val
);
3855 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3857 putULong (&option
.data
[4 + i
* 4], val
);
3863 bval
= va_arg (va
, u_int8_t
*);
3864 #if defined (DEBUG_FAILOVER_MESSAGES)
3865 for (i
= 0; i
< count
; i
++) {
3866 /* 23 bytes plus nul, safe. */
3867 sprintf (tbuf
, " %d", bval
[i
]);
3868 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3871 memcpy (&option
.data
[4], bval
, count
);
3874 /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
3875 terminated. Note that the caller should be careful not
3876 to provide a format and data that amount to more than 256
3877 bytes of data, since it will cause a fatal error. */
3878 case FT_TEXT_OR_BYTES
:
3880 #if defined (DEBUG_FAILOVER_MESSAGES)
3881 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3882 * It is unclear what the effects of truncation here are, or
3883 * how that condition should be handled. It seems that this
3884 * function is used for formatting messages in the failover
3885 * command channel. For now the safest thing is for
3886 * overflow-truncation to cause a fatal log.
3888 if (snprintf (tbuf
, sizeof tbuf
, "\"%s\"", txt
) >= sizeof tbuf
)
3889 log_fatal ("dhcp_failover_make_option: tbuf overflow");
3890 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3892 memcpy (&option
.data
[4], txt
, count
);
3897 option
.data
[4] = va_arg (va
, unsigned);
3899 option
.data
[5] = va_arg (va
, unsigned);
3900 bval
= va_arg (va
, u_int8_t
*);
3901 memcpy (&option
.data
[4 + count
], bval
, size
- count
- 4);
3902 #if defined (DEBUG_FAILOVER_MESSAGES)
3903 for (i
= 4; i
< size
; i
++) {
3904 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3905 sprintf (tbuf
, " %d", option
.data
[i
]);
3906 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3912 for (i
= 0; i
< count
; i
++) {
3913 val
= va_arg (va
, u_int32_t
);
3914 #if defined (DEBUG_FAILOVER_MESSAGES)
3915 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3916 sprintf (tbuf
, " %d", val
);
3917 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3919 putUShort (&option
.data
[4 + i
* 2], val
);
3928 #if defined DEBUG_FAILOVER_MESSAGES
3929 failover_print (obuf
, obufix
, obufmax
, ")");
3933 /* Now allocate a place to store what we just set up. */
3934 op
= dmalloc (sizeof (failover_option_t
), MDL
);
3936 dfree (option
.data
, MDL
);
3937 return &null_failover_option
;
3944 /* Send a failover message header. */
3946 isc_result_t
dhcp_failover_put_message (dhcp_failover_link_t
*link
,
3947 omapi_object_t
*connection
,
3948 int msg_type
, u_int32_t xid
, ...)
3954 failover_option_t
*option
;
3955 unsigned char *opbuf
;
3956 isc_result_t status
= ISC_R_SUCCESS
;
3959 /* Run through the argument list once to compute the length of
3960 the option portion of the message. */
3961 va_start (list
, xid
);
3962 while ((option
= va_arg (list
, failover_option_t
*))) {
3963 if (option
!= &skip_failover_option
)
3964 size
+= option
-> count
;
3965 if (option
== &null_failover_option
)
3970 /* Allocate an option buffer, unless we got an error. */
3971 if (!bad_option
&& size
) {
3972 opbuf
= dmalloc (size
, MDL
);
3974 status
= ISC_R_NOMEMORY
;
3976 opbuf
= (unsigned char *)0;
3978 va_start (list
, xid
);
3979 while ((option
= va_arg (list
, failover_option_t
*))) {
3980 if (option
== &skip_failover_option
)
3982 if (!bad_option
&& opbuf
)
3983 memcpy (&opbuf
[opix
],
3984 option
-> data
, option
-> count
);
3985 if (option
!= &null_failover_option
&&
3986 option
!= &skip_failover_option
) {
3987 opix
+= option
-> count
;
3988 dfree (option
-> data
, MDL
);
3989 dfree (option
, MDL
);
3995 return ISC_R_INVALIDARG
;
3997 /* Now send the message header. */
3999 /* Message length. */
4000 status
= omapi_connection_put_uint16 (connection
, size
+ 12);
4001 if (status
!= ISC_R_SUCCESS
)
4006 status
= omapi_connection_copyin (connection
, &cbuf
, 1);
4007 if (status
!= ISC_R_SUCCESS
)
4010 /* Payload offset. */
4012 status
= omapi_connection_copyin (connection
, &cbuf
, 1);
4013 if (status
!= ISC_R_SUCCESS
)
4017 status
= omapi_connection_put_uint32 (connection
, (u_int32_t
)cur_time
);
4018 if (status
!= ISC_R_SUCCESS
)
4021 /* Transaction ID. */
4022 status
= omapi_connection_put_uint32(connection
, xid
);
4023 if (status
!= ISC_R_SUCCESS
)
4028 status
= omapi_connection_copyin (connection
, opbuf
, size
);
4029 if (status
!= ISC_R_SUCCESS
)
4033 if (link
-> state_object
&&
4034 link
-> state_object
-> link_to_peer
== link
) {
4035 #if defined (DEBUG_FAILOVER_TIMING)
4036 log_info ("add_timeout +%d %s",
4037 (int)(link
-> state_object
->
4038 partner
.max_response_delay
) / 3,
4039 "dhcp_failover_send_contact");
4041 add_timeout (cur_time
+
4042 (int)(link
-> state_object
->
4043 partner
.max_response_delay
) / 3,
4044 dhcp_failover_send_contact
, link
-> state_object
,
4045 (tvref_t
)dhcp_failover_state_reference
,
4046 (tvunref_t
)dhcp_failover_state_dereference
);
4053 log_info ("dhcp_failover_put_message: something went wrong.");
4054 omapi_disconnect (connection
, 1);
4058 void dhcp_failover_timeout (void *vstate
)
4060 dhcp_failover_state_t
*state
= vstate
;
4061 dhcp_failover_link_t
*link
;
4063 #if defined (DEBUG_FAILOVER_TIMING)
4064 log_info ("dhcp_failover_timeout");
4067 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
4069 link
= state
-> link_to_peer
;
4072 link
-> outer
-> type
!= omapi_type_connection
)
4075 log_error ("timeout waiting for failover peer %s", state
-> name
);
4077 /* If we haven't gotten a timely response, blow away the connection.
4078 This will cause the state to change automatically. */
4079 omapi_disconnect (link
-> outer
, 1);
4082 void dhcp_failover_send_contact (void *vstate
)
4084 dhcp_failover_state_t
*state
= vstate
;
4085 dhcp_failover_link_t
*link
;
4086 isc_result_t status
;
4088 #if defined (DEBUG_FAILOVER_MESSAGES)
4090 unsigned obufix
= 0;
4092 # define FMA obuf, &obufix, sizeof obuf
4093 failover_print (FMA
, "(contact");
4095 # define FMA (char *)0, (unsigned *)0, 0
4098 #if defined (DEBUG_FAILOVER_TIMING)
4099 log_info ("dhcp_failover_send_contact");
4102 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
4104 link
= state
-> link_to_peer
;
4107 link
-> outer
-> type
!= omapi_type_connection
)
4110 status
= (dhcp_failover_put_message
4111 (link
, link
-> outer
,
4112 FTM_CONTACT
, link
->xid
++,
4113 (failover_option_t
*)0));
4115 #if defined (DEBUG_FAILOVER_MESSAGES)
4116 if (status
!= ISC_R_SUCCESS
)
4117 failover_print (FMA
, " (failed)");
4118 failover_print (FMA
, ")");
4120 log_debug ("%s", obuf
);
4126 isc_result_t
dhcp_failover_send_state (dhcp_failover_state_t
*state
)
4128 dhcp_failover_link_t
*link
;
4129 isc_result_t status
;
4131 #if defined (DEBUG_FAILOVER_MESSAGES)
4133 unsigned obufix
= 0;
4135 # define FMA obuf, &obufix, sizeof obuf
4136 failover_print (FMA
, "(state");
4138 # define FMA (char *)0, (unsigned *)0, 0
4141 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
4142 return ISC_R_INVALIDARG
;
4143 link
= state
-> link_to_peer
;
4146 link
-> outer
-> type
!= omapi_type_connection
)
4147 return ISC_R_INVALIDARG
;
4149 status
= (dhcp_failover_put_message
4150 (link
, link
-> outer
,
4151 FTM_STATE
, link
->xid
++,
4152 dhcp_failover_make_option (FTO_SERVER_STATE
, FMA
,
4153 (state
-> me
.state
== startup
4154 ? state
-> saved_state
4155 : state
-> me
.state
)),
4156 dhcp_failover_make_option
4157 (FTO_SERVER_FLAGS
, FMA
,
4158 (state
-> service_state
== service_startup
4159 ? FTF_SERVER_STARTUP
: 0)),
4160 dhcp_failover_make_option (FTO_STOS
, FMA
, state
-> me
.stos
),
4161 (failover_option_t
*)0));
4163 #if defined (DEBUG_FAILOVER_MESSAGES)
4164 if (status
!= ISC_R_SUCCESS
)
4165 failover_print (FMA
, " (failed)");
4166 failover_print (FMA
, ")");
4168 log_debug ("%s", obuf
);
4171 return ISC_R_SUCCESS
;
4174 /* Send a connect message. */
4176 isc_result_t
dhcp_failover_send_connect (omapi_object_t
*l
)
4178 dhcp_failover_link_t
*link
;
4179 dhcp_failover_state_t
*state
;
4180 isc_result_t status
;
4181 #if defined (DEBUG_FAILOVER_MESSAGES)
4183 unsigned obufix
= 0;
4185 # define FMA obuf, &obufix, sizeof obuf
4186 failover_print (FMA
, "(connect");
4188 # define FMA (char *)0, (unsigned *)0, 0
4191 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
4192 return ISC_R_INVALIDARG
;
4193 link
= (dhcp_failover_link_t
*)l
;
4194 state
= link
-> state_object
;
4195 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
4196 return ISC_R_INVALIDARG
;
4199 (dhcp_failover_put_message
4201 FTM_CONNECT
, link
->xid
++,
4202 dhcp_failover_make_option(FTO_RELATIONSHIP_NAME
, FMA
,
4203 strlen(state
->name
), state
->name
),
4204 dhcp_failover_make_option (FTO_MAX_UNACKED
, FMA
,
4205 state
-> me
.max_flying_updates
),
4206 dhcp_failover_make_option (FTO_RECEIVE_TIMER
, FMA
,
4207 state
-> me
.max_response_delay
),
4208 dhcp_failover_option_printf(FTO_VENDOR_CLASS
, FMA
,
4209 "isc-%s", PACKAGE_VERSION
),
4210 dhcp_failover_make_option (FTO_PROTOCOL_VERSION
, FMA
,
4211 DHCP_FAILOVER_VERSION
),
4212 dhcp_failover_make_option (FTO_TLS_REQUEST
, FMA
,
4214 dhcp_failover_make_option (FTO_MCLT
, FMA
,
4217 ? dhcp_failover_make_option (FTO_HBA
, FMA
, 32, state
-> hba
)
4218 : &skip_failover_option
),
4219 (failover_option_t
*)0));
4221 #if defined (DEBUG_FAILOVER_MESSAGES)
4222 if (status
!= ISC_R_SUCCESS
)
4223 failover_print (FMA
, " (failed)");
4224 failover_print (FMA
, ")");
4226 log_debug ("%s", obuf
);
4232 isc_result_t
dhcp_failover_send_connectack (omapi_object_t
*l
,
4233 dhcp_failover_state_t
*state
,
4234 int reason
, const char *errmsg
)
4236 dhcp_failover_link_t
*link
;
4237 isc_result_t status
;
4238 #if defined (DEBUG_FAILOVER_MESSAGES)
4240 unsigned obufix
= 0;
4242 # define FMA obuf, &obufix, sizeof obuf
4243 failover_print (FMA
, "(connectack");
4245 # define FMA (char *)0, (unsigned *)0, 0
4248 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
4249 return ISC_R_INVALIDARG
;
4250 link
= (dhcp_failover_link_t
*)l
;
4251 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
4252 return ISC_R_INVALIDARG
;
4255 (dhcp_failover_put_message
4257 FTM_CONNECTACK
, link
->imsg
->xid
,
4259 ? dhcp_failover_make_option(FTO_RELATIONSHIP_NAME
, FMA
,
4260 strlen(state
->name
), state
->name
)
4261 : (link
->imsg
->options_present
& FTB_RELATIONSHIP_NAME
)
4262 ? &link
->imsg
->relationship_name
4263 : &skip_failover_option
,
4265 ? dhcp_failover_make_option (FTO_MAX_UNACKED
, FMA
,
4266 state
-> me
.max_flying_updates
)
4267 : &skip_failover_option
,
4269 ? dhcp_failover_make_option (FTO_RECEIVE_TIMER
, FMA
,
4270 state
-> me
.max_response_delay
)
4271 : &skip_failover_option
,
4272 dhcp_failover_option_printf(FTO_VENDOR_CLASS
, FMA
,
4273 "isc-%s", PACKAGE_VERSION
),
4274 dhcp_failover_make_option (FTO_PROTOCOL_VERSION
, FMA
,
4275 DHCP_FAILOVER_VERSION
),
4276 (link
->imsg
->options_present
& FTB_TLS_REQUEST
)
4277 ? dhcp_failover_make_option(FTO_TLS_REPLY
, FMA
,
4279 : &skip_failover_option
,
4281 ? dhcp_failover_make_option (FTO_REJECT_REASON
,
4283 : &skip_failover_option
,
4285 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4286 strlen (errmsg
), errmsg
)
4287 : &skip_failover_option
,
4288 (failover_option_t
*)0));
4290 #if defined (DEBUG_FAILOVER_MESSAGES)
4291 if (status
!= ISC_R_SUCCESS
)
4292 failover_print (FMA
, " (failed)");
4293 failover_print (FMA
, ")");
4295 log_debug ("%s", obuf
);
4301 isc_result_t
dhcp_failover_send_disconnect (omapi_object_t
*l
,
4303 const char *message
)
4305 dhcp_failover_link_t
*link
;
4306 dhcp_failover_state_t
*state
;
4307 isc_result_t status
;
4308 #if defined (DEBUG_FAILOVER_MESSAGES)
4310 unsigned obufix
= 0;
4312 # define FMA obuf, &obufix, sizeof obuf
4313 failover_print (FMA
, "(disconnect");
4315 # define FMA (char *)0, (unsigned *)0, 0
4318 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
4319 return ISC_R_INVALIDARG
;
4320 link
= (dhcp_failover_link_t
*)l
;
4321 state
= link
-> state_object
;
4322 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
4323 return ISC_R_INVALIDARG
;
4325 if (!message
&& reason
)
4326 message
= dhcp_failover_reject_reason_print (reason
);
4328 status
= (dhcp_failover_put_message
4330 FTM_DISCONNECT
, link
->xid
++,
4331 dhcp_failover_make_option (FTO_REJECT_REASON
,
4334 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4335 strlen (message
), message
)
4336 : &skip_failover_option
),
4337 (failover_option_t
*)0));
4339 #if defined (DEBUG_FAILOVER_MESSAGES)
4340 if (status
!= ISC_R_SUCCESS
)
4341 failover_print (FMA
, " (failed)");
4342 failover_print (FMA
, ")");
4344 log_debug ("%s", obuf
);
4350 /* Send a Bind Update message. */
4352 isc_result_t
dhcp_failover_send_bind_update (dhcp_failover_state_t
*state
,
4353 struct lease
*lease
)
4355 dhcp_failover_link_t
*link
;
4356 isc_result_t status
;
4358 binding_state_t transmit_state
;
4359 #if defined (DEBUG_FAILOVER_MESSAGES)
4361 unsigned obufix
= 0;
4363 # define FMA obuf, &obufix, sizeof obuf
4364 failover_print (FMA
, "(bndupd");
4366 # define FMA (char *)0, (unsigned *)0, 0
4369 if (!state
-> link_to_peer
||
4370 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4371 return ISC_R_INVALIDARG
;
4372 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4374 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4375 return ISC_R_INVALIDARG
;
4377 transmit_state
= lease
->desired_binding_state
;
4378 if (lease
->flags
& RESERVED_LEASE
) {
4379 /* If we are listing an allocable (not yet ACTIVE etc) lease
4380 * as reserved, toggle to the peer's 'free state', per the
4381 * draft. This gives the peer permission to alloc it to the
4382 * chaddr/uid-named client.
4384 if ((state
->i_am
== primary
) && (transmit_state
== FTS_FREE
))
4385 transmit_state
= FTS_BACKUP
;
4386 else if ((state
->i_am
== secondary
) &&
4387 (transmit_state
== FTS_BACKUP
))
4388 transmit_state
= FTS_FREE
;
4390 flags
|= FTF_IP_FLAG_RESERVE
;
4392 if (lease
->flags
& BOOTP_LEASE
)
4393 flags
|= FTF_IP_FLAG_BOOTP
;
4395 /* last_xid == 0 is illegal, seek past zero if we hit it. */
4399 lease
->last_xid
= link
->xid
++;
4401 /* Send the update. */
4402 status
= (dhcp_failover_put_message
4403 (link
, link
-> outer
,
4404 FTM_BNDUPD
, lease
->last_xid
,
4405 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS
, FMA
,
4406 lease
-> ip_addr
.len
,
4407 lease
-> ip_addr
.iabuf
),
4408 dhcp_failover_make_option (FTO_BINDING_STATUS
, FMA
,
4409 lease
-> desired_binding_state
),
4411 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER
, FMA
,
4414 : &skip_failover_option
,
4415 lease
-> hardware_addr
.hlen
4416 ? dhcp_failover_make_option (FTO_CHADDR
, FMA
,
4417 lease
-> hardware_addr
.hlen
,
4418 lease
-> hardware_addr
.hbuf
)
4419 : &skip_failover_option
,
4420 dhcp_failover_make_option (FTO_LEASE_EXPIRY
, FMA
,
4422 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY
, FMA
,
4424 dhcp_failover_make_option (FTO_STOS
, FMA
,
4426 dhcp_failover_make_option (FTO_CLTT
, FMA
,
4428 flags
? dhcp_failover_make_option(FTO_IP_FLAGS
, FMA
,
4430 &skip_failover_option
, /* No IP_FLAGS */
4431 &skip_failover_option
, /* XXX DDNS */
4432 &skip_failover_option
, /* XXX request options */
4433 &skip_failover_option
, /* XXX reply options */
4434 (failover_option_t
*)0));
4436 #if defined (DEBUG_FAILOVER_MESSAGES)
4437 if (status
!= ISC_R_SUCCESS
)
4438 failover_print (FMA
, " (failed)");
4439 failover_print (FMA
, ")");
4441 log_debug ("%s", obuf
);
4447 /* Send a Bind ACK message. */
4449 isc_result_t
dhcp_failover_send_bind_ack (dhcp_failover_state_t
*state
,
4450 failover_message_t
*msg
,
4451 int reason
, const char *message
)
4453 dhcp_failover_link_t
*link
;
4454 isc_result_t status
;
4455 #if defined (DEBUG_FAILOVER_MESSAGES)
4457 unsigned obufix
= 0;
4459 # define FMA obuf, &obufix, sizeof obuf
4460 failover_print (FMA
, "(bndack");
4462 # define FMA (char *)0, (unsigned *)0, 0
4465 if (!state
-> link_to_peer
||
4466 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4467 return ISC_R_INVALIDARG
;
4468 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4470 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4471 return ISC_R_INVALIDARG
;
4473 if (!message
&& reason
)
4474 message
= dhcp_failover_reject_reason_print (reason
);
4476 /* Send the update. */
4477 status
= (dhcp_failover_put_message
4478 (link
, link
-> outer
,
4479 FTM_BNDACK
, msg
->xid
,
4480 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS
, FMA
,
4481 sizeof msg
-> assigned_addr
,
4482 &msg
-> assigned_addr
),
4483 #ifdef DO_BNDACK_SHOULD_NOT
4484 dhcp_failover_make_option (FTO_BINDING_STATUS
, FMA
,
4485 msg
-> binding_status
),
4486 (msg
-> options_present
& FTB_CLIENT_IDENTIFIER
)
4487 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER
, FMA
,
4488 msg
-> client_identifier
.count
,
4489 msg
-> client_identifier
.data
)
4490 : &skip_failover_option
,
4491 (msg
-> options_present
& FTB_CHADDR
)
4492 ? dhcp_failover_make_option (FTO_CHADDR
, FMA
,
4493 msg
-> chaddr
.count
,
4495 : &skip_failover_option
,
4496 dhcp_failover_make_option (FTO_LEASE_EXPIRY
, FMA
,
4498 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY
, FMA
,
4499 msg
-> potential_expiry
),
4500 dhcp_failover_make_option (FTO_STOS
, FMA
,
4502 dhcp_failover_make_option (FTO_CLTT
, FMA
,
4504 ((msg
->options_present
& FTB_IP_FLAGS
) && msg
->ip_flags
) ?
4505 dhcp_failover_make_option(FTO_IP_FLAGS
, FMA
,
4507 : &skip_failover_option
,
4508 #endif /* DO_BNDACK_SHOULD_NOT */
4510 ? dhcp_failover_make_option(FTO_REJECT_REASON
, FMA
, reason
)
4511 : &skip_failover_option
,
4513 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4514 strlen (message
), message
)
4515 : &skip_failover_option
,
4516 #ifdef DO_BNDACK_SHOULD_NOT
4517 &skip_failover_option
, /* XXX DDNS */
4518 &skip_failover_option
, /* XXX request options */
4519 &skip_failover_option
, /* XXX reply options */
4520 #endif /* DO_BNDACK_SHOULD_NOT */
4521 (failover_option_t
*)0));
4523 #if defined (DEBUG_FAILOVER_MESSAGES)
4524 if (status
!= ISC_R_SUCCESS
)
4525 failover_print (FMA
, " (failed)");
4526 failover_print (FMA
, ")");
4528 log_debug ("%s", obuf
);
4534 isc_result_t
dhcp_failover_send_poolreq (dhcp_failover_state_t
*state
)
4536 dhcp_failover_link_t
*link
;
4537 isc_result_t status
;
4538 #if defined (DEBUG_FAILOVER_MESSAGES)
4540 unsigned obufix
= 0;
4542 # define FMA obuf, &obufix, sizeof obuf
4543 failover_print (FMA
, "(poolreq");
4545 # define FMA (char *)0, (unsigned *)0, 0
4548 if (!state
-> link_to_peer
||
4549 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4550 return ISC_R_INVALIDARG
;
4551 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4553 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4554 return ISC_R_INVALIDARG
;
4556 status
= (dhcp_failover_put_message
4557 (link
, link
-> outer
,
4558 FTM_POOLREQ
, link
->xid
++,
4559 (failover_option_t
*)0));
4561 #if defined (DEBUG_FAILOVER_MESSAGES)
4562 if (status
!= ISC_R_SUCCESS
)
4563 failover_print (FMA
, " (failed)");
4564 failover_print (FMA
, ")");
4566 log_debug ("%s", obuf
);
4572 isc_result_t
dhcp_failover_send_poolresp (dhcp_failover_state_t
*state
,
4575 dhcp_failover_link_t
*link
;
4576 isc_result_t status
;
4577 #if defined (DEBUG_FAILOVER_MESSAGES)
4579 unsigned obufix
= 0;
4581 # define FMA obuf, &obufix, sizeof obuf
4582 failover_print (FMA
, "(poolresp");
4584 # define FMA (char *)0, (unsigned *)0, 0
4587 if (!state
-> link_to_peer
||
4588 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4589 return ISC_R_INVALIDARG
;
4590 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4592 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4593 return ISC_R_INVALIDARG
;
4595 status
= (dhcp_failover_put_message
4596 (link
, link
-> outer
,
4597 FTM_POOLRESP
, link
->imsg
->xid
,
4598 dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED
, FMA
,
4600 (failover_option_t
*)0));
4602 #if defined (DEBUG_FAILOVER_MESSAGES)
4603 if (status
!= ISC_R_SUCCESS
)
4604 failover_print (FMA
, " (failed)");
4605 failover_print (FMA
, ")");
4607 log_debug ("%s", obuf
);
4613 isc_result_t
dhcp_failover_send_update_request (dhcp_failover_state_t
*state
)
4615 dhcp_failover_link_t
*link
;
4616 isc_result_t status
;
4617 #if defined (DEBUG_FAILOVER_MESSAGES)
4619 unsigned obufix
= 0;
4621 # define FMA obuf, &obufix, sizeof obuf
4622 failover_print (FMA
, "(updreq");
4624 # define FMA (char *)0, (unsigned *)0, 0
4627 if (!state
-> link_to_peer
||
4628 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4629 return ISC_R_INVALIDARG
;
4630 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4632 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4633 return ISC_R_INVALIDARG
;
4635 if (state
-> curUPD
)
4636 return ISC_R_ALREADYRUNNING
;
4638 status
= (dhcp_failover_put_message
4639 (link
, link
-> outer
,
4640 FTM_UPDREQ
, link
->xid
++,
4641 (failover_option_t
*)0));
4643 if (status
== ISC_R_SUCCESS
)
4644 state
-> curUPD
= FTM_UPDREQ
;
4646 #if defined (DEBUG_FAILOVER_MESSAGES)
4647 if (status
!= ISC_R_SUCCESS
)
4648 failover_print (FMA
, " (failed)");
4649 failover_print (FMA
, ")");
4651 log_debug ("%s", obuf
);
4654 log_info ("Sent update request message to %s", state
-> name
);
4658 isc_result_t
dhcp_failover_send_update_request_all (dhcp_failover_state_t
4661 dhcp_failover_link_t
*link
;
4662 isc_result_t status
;
4663 #if defined (DEBUG_FAILOVER_MESSAGES)
4665 unsigned obufix
= 0;
4667 # define FMA obuf, &obufix, sizeof obuf
4668 failover_print (FMA
, "(updreqall");
4670 # define FMA (char *)0, (unsigned *)0, 0
4673 if (!state
-> link_to_peer
||
4674 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4675 return ISC_R_INVALIDARG
;
4676 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4678 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4679 return ISC_R_INVALIDARG
;
4681 /* If there is an UPDREQ in progress, then upgrade to UPDREQALL. */
4682 if (state
-> curUPD
&& (state
-> curUPD
!= FTM_UPDREQ
))
4683 return ISC_R_ALREADYRUNNING
;
4685 status
= (dhcp_failover_put_message
4686 (link
, link
-> outer
,
4687 FTM_UPDREQALL
, link
->xid
++,
4688 (failover_option_t
*)0));
4690 if (status
== ISC_R_SUCCESS
)
4691 state
-> curUPD
= FTM_UPDREQALL
;
4693 #if defined (DEBUG_FAILOVER_MESSAGES)
4694 if (status
!= ISC_R_SUCCESS
)
4695 failover_print (FMA
, " (failed)");
4696 failover_print (FMA
, ")");
4698 log_debug ("%s", obuf
);
4701 log_info ("Sent update request all message to %s", state
-> name
);
4705 isc_result_t
dhcp_failover_send_update_done (dhcp_failover_state_t
*state
)
4707 dhcp_failover_link_t
*link
;
4708 isc_result_t status
;
4709 #if defined (DEBUG_FAILOVER_MESSAGES)
4711 unsigned obufix
= 0;
4713 # define FMA obuf, &obufix, sizeof obuf
4714 failover_print (FMA
, "(upddone");
4716 # define FMA (char *)0, (unsigned *)0, 0
4719 if (!state
-> link_to_peer
||
4720 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4721 return ISC_R_INVALIDARG
;
4722 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4724 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4725 return ISC_R_INVALIDARG
;
4727 status
= (dhcp_failover_put_message
4728 (link
, link
-> outer
,
4729 FTM_UPDDONE
, state
->updxid
,
4730 (failover_option_t
*)0));
4732 #if defined (DEBUG_FAILOVER_MESSAGES)
4733 if (status
!= ISC_R_SUCCESS
)
4734 failover_print (FMA
, " (failed)");
4735 failover_print (FMA
, ")");
4737 log_debug ("%s", obuf
);
4741 log_info ("Sent update done message to %s", state
-> name
);
4743 state
->updxid
--; /* Paranoia, just so it mismatches. */
4745 /* There may be uncommitted leases at this point (since
4746 dhcp_failover_process_bind_ack() doesn't commit leases);
4747 commit the lease file. */
4753 isc_result_t
dhcp_failover_process_bind_update (dhcp_failover_state_t
*state
,
4754 failover_message_t
*msg
)
4756 struct lease
*lt
, *lease
;
4758 int reason
= FTR_MISC_REJECT
;
4759 const char *message
;
4760 int new_binding_state
;
4761 int send_to_backup
= 0;
4763 ia
.len
= sizeof msg
-> assigned_addr
;
4764 memcpy (ia
.iabuf
, &msg
-> assigned_addr
, ia
.len
);
4766 lease
= (struct lease
*)0;
4767 lt
= (struct lease
*)0;
4768 if (!find_lease_by_ip_addr (&lease
, ia
, MDL
)) {
4769 message
= "unknown IP address";
4770 reason
= FTR_ILLEGAL_IP_ADDR
;
4774 /* XXX check for conflicts. */
4776 /* Install the new info. */
4777 if (!lease_copy (<
, lease
, MDL
)) {
4778 message
= "no memory";
4782 if (msg
-> options_present
& FTB_CHADDR
) {
4783 if (msg
->binding_status
== FTS_ABANDONED
) {
4784 message
= "BNDUPD to ABANDONED with a CHADDR";
4787 if (msg
-> chaddr
.count
> sizeof lt
-> hardware_addr
.hbuf
) {
4788 message
= "chaddr too long";
4791 lt
-> hardware_addr
.hlen
= msg
-> chaddr
.count
;
4792 memcpy (lt
-> hardware_addr
.hbuf
, msg
-> chaddr
.data
,
4793 msg
-> chaddr
.count
);
4794 } else if (msg
->binding_status
== FTS_ACTIVE
||
4795 msg
->binding_status
== FTS_EXPIRED
||
4796 msg
->binding_status
== FTS_RELEASED
) {
4797 message
= "BNDUPD without CHADDR";
4799 } else if (msg
->binding_status
== FTS_ABANDONED
) {
4800 lt
->hardware_addr
.hlen
= 0;
4802 binding_scope_dereference(<
->scope
, MDL
);
4805 /* There is no explicit message content to indicate that the client
4806 * supplied no client-identifier. So if we don't hear of a value,
4807 * we discard the last one.
4809 if (msg
->options_present
& FTB_CLIENT_IDENTIFIER
) {
4810 if (msg
->binding_status
== FTS_ABANDONED
) {
4811 message
= "BNDUPD to ABANDONED with client-id";
4815 lt
->uid_len
= msg
->client_identifier
.count
;
4817 /* Allocate the lt->uid buffer if we haven't already, or
4818 * re-allocate the lt-uid buffer if we have one that is not
4819 * large enough. Otherwise, just use the extant buffer.
4821 if (!lt
->uid
|| lt
->uid
== lt
->uid_buf
||
4822 lt
->uid_len
> lt
->uid_max
) {
4823 if (lt
->uid
&& lt
->uid
!= lt
->uid_buf
)
4824 dfree(lt
->uid
, MDL
);
4826 if (lt
->uid_len
> sizeof(lt
->uid_buf
)) {
4827 lt
->uid_max
= lt
->uid_len
;
4828 lt
->uid
= dmalloc(lt
->uid_len
, MDL
);
4830 message
= "no memory";
4834 lt
->uid_max
= sizeof(lt
->uid_buf
);
4835 lt
->uid
= lt
->uid_buf
;
4839 msg
-> client_identifier
.data
, lt
-> uid_len
);
4840 } else if (lt
->uid
&& msg
->binding_status
!= FTS_RESET
&&
4841 msg
->binding_status
!= FTS_FREE
&&
4842 msg
->binding_status
!= FTS_BACKUP
) {
4843 if (lt
->uid
!= lt
->uid_buf
)
4844 dfree (lt
->uid
, MDL
);
4846 lt
->uid_max
= lt
->uid_len
= 0;
4849 /* If the lease was expired, also remove the stale binding scope. */
4850 if (lt
->scope
&& lt
->ends
< cur_time
)
4851 binding_scope_dereference(<
->scope
, MDL
);
4853 /* XXX Times may need to be adjusted based on clock skew! */
4854 if (msg
-> options_present
& FTB_STOS
) {
4855 lt
-> starts
= msg
-> stos
;
4857 if (msg
-> options_present
& FTB_LEASE_EXPIRY
) {
4858 lt
-> ends
= msg
-> expiry
;
4860 if (msg
-> options_present
& FTB_CLTT
) {
4861 lt
-> cltt
= msg
-> cltt
;
4863 if (msg
->options_present
& FTB_POTENTIAL_EXPIRY
) {
4864 lt
->atsfp
= lt
->tsfp
= msg
->potential_expiry
;
4866 if (msg
->options_present
& FTB_IP_FLAGS
) {
4867 if (msg
->ip_flags
& FTF_IP_FLAG_RESERVE
) {
4868 if ((((state
->i_am
== primary
) &&
4869 (lease
->binding_state
== FTS_FREE
)) ||
4870 ((state
->i_am
== secondary
) &&
4871 (lease
->binding_state
== FTS_BACKUP
))) &&
4872 !(lease
->flags
& RESERVED_LEASE
)) {
4873 message
= "Address is not reserved.";
4874 reason
= FTR_IP_NOT_RESERVED
;
4878 lt
->flags
|= RESERVED_LEASE
;
4880 lt
->flags
&= ~RESERVED_LEASE
;
4882 if (msg
->ip_flags
& FTF_IP_FLAG_BOOTP
) {
4883 if ((((state
->i_am
== primary
) &&
4884 (lease
->binding_state
== FTS_FREE
)) ||
4885 ((state
->i_am
== secondary
) &&
4886 (lease
->binding_state
== FTS_BACKUP
))) &&
4887 !(lease
->flags
& BOOTP_LEASE
)) {
4888 message
= "Address is not allocated to BOOTP.";
4891 lt
->flags
|= BOOTP_LEASE
;
4893 lt
->flags
&= ~BOOTP_LEASE
;
4895 if (msg
->ip_flags
& ~(FTF_IP_FLAG_RESERVE
| FTF_IP_FLAG_BOOTP
))
4896 log_info("Unknown IP-flags set in BNDUPD (0x%x).",
4898 } else /* Flags may only not appear if the values are zero. */
4899 lt
->flags
&= ~(RESERVED_LEASE
| BOOTP_LEASE
);
4901 if (msg
-> options_present
& FTB_BINDING_STATUS
) {
4902 #if defined (DEBUG_LEASE_STATE_TRANSITIONS)
4903 log_info ("processing state transition for %s: %s to %s",
4904 piaddr (lease
-> ip_addr
),
4905 binding_state_print (lease
-> binding_state
),
4906 binding_state_print (msg
-> binding_status
));
4909 /* If we're in normal state, make sure the state transition
4911 if (state
-> me
.state
== normal
) {
4913 (normal_binding_state_transition_check
4914 (lease
, state
, msg
-> binding_status
,
4915 msg
-> potential_expiry
));
4916 /* XXX if the transition the peer asked for isn't
4917 XXX allowed, maybe we should make the transition
4918 XXX into potential-conflict at this point. */
4921 (conflict_binding_state_transition_check
4922 (lease
, state
, msg
-> binding_status
,
4923 msg
-> potential_expiry
));
4925 if (new_binding_state
!= msg
-> binding_status
) {
4928 if (snprintf (outbuf
, sizeof outbuf
,
4929 "%s: invalid state transition: %s to %s",
4930 piaddr (lease
-> ip_addr
),
4931 binding_state_print (lease
-> binding_state
),
4932 binding_state_print (msg
-> binding_status
))
4934 log_fatal ("%s: impossible outbuf overflow",
4935 "dhcp_failover_process_bind_update");
4937 dhcp_failover_send_bind_ack (state
, msg
,
4942 if (new_binding_state
== FTS_EXPIRED
||
4943 new_binding_state
== FTS_RELEASED
||
4944 new_binding_state
== FTS_RESET
) {
4945 lt
-> next_binding_state
= FTS_FREE
;
4947 /* Mac address affinity. Assign the lease to
4948 * BACKUP state if we are the primary and the
4949 * peer is more likely to reallocate this lease
4950 * to a returning client.
4952 if ((state
->i_am
== primary
) &&
4953 !(lt
->flags
& (RESERVED_LEASE
| BOOTP_LEASE
)))
4954 send_to_backup
= peer_wants_lease(lt
);
4956 lt
-> next_binding_state
= new_binding_state
;
4958 msg
-> binding_status
= lt
-> next_binding_state
;
4961 /* Try to install the new information. */
4962 if (!supersede_lease (lease
, lt
, 0, 0, 0) ||
4963 !write_lease (lease
)) {
4964 message
= "database update failed";
4966 dhcp_failover_send_bind_ack (state
, msg
, reason
, message
);
4969 dhcp_failover_queue_ack (state
, msg
);
4972 /* If it is probably wise, assign lease to backup state if the peer
4973 * is not already hoarding leases.
4975 if (send_to_backup
&& secondary_not_hoarding(state
, lease
->pool
)) {
4976 lease
->next_binding_state
= FTS_BACKUP
;
4977 lease
->tstp
= cur_time
;
4978 lease
->starts
= cur_time
;
4980 if (!supersede_lease(lease
, NULL
, 0, 1, 0) ||
4981 !write_lease(lease
))
4982 log_error("can't commit lease %s for mac addr "
4983 "affinity", piaddr(lease
->ip_addr
));
4985 dhcp_failover_send_updates(state
);
4990 lease_dereference (<
, MDL
);
4992 lease_dereference (&lease
, MDL
);
4994 return ISC_R_SUCCESS
;
4997 /* This was hairy enough I didn't want to do it all in an if statement.
4999 * Returns: Truth is the secondary is allowed to get more leases based upon
5000 * MAC address affinity. False otherwise.
5003 secondary_not_hoarding(dhcp_failover_state_t
*state
, struct pool
*p
) {
5008 total
= p
->free_leases
+ p
->backup_leases
;
5010 /* How many leases is one side or the other allowed to "hold"? */
5011 hold
= ((total
* state
->max_lease_ownership
) + 50) / 100;
5013 /* If we were to send leases (or if the secondary were to send us
5014 * leases in the negative direction), how many would that be?
5016 lts
= (p
->free_leases
- p
->backup_leases
) / 2;
5018 /* The peer is not hoarding leases if we would send them more leases
5019 * (or they would take fewer leases) than the maximum they are allowed
5020 * to hold (the negative hold).
5022 return(lts
> -hold
);
5025 isc_result_t
dhcp_failover_process_bind_ack (dhcp_failover_state_t
*state
,
5026 failover_message_t
*msg
)
5028 struct lease
*lt
= (struct lease
*)0;
5029 struct lease
*lease
= (struct lease
*)0;
5031 const char *message
= "no memory";
5032 u_int32_t pot_expire
;
5033 int send_to_backup
= ISC_FALSE
;
5035 ia
.len
= sizeof msg
-> assigned_addr
;
5036 memcpy (ia
.iabuf
, &msg
-> assigned_addr
, ia
.len
);
5038 if (!find_lease_by_ip_addr (&lease
, ia
, MDL
)) {
5039 message
= "no such lease";
5043 /* XXX check for conflicts. */
5044 if (msg
-> options_present
& FTB_REJECT_REASON
) {
5045 log_error ("bind update on %s from %s rejected: %.*s",
5046 piaddr (ia
), state
-> name
,
5047 (int)((msg
-> options_present
& FTB_MESSAGE
)
5048 ? msg
-> message
.count
5049 : strlen (dhcp_failover_reject_reason_print
5050 (msg
-> reject_reason
))),
5051 (msg
-> options_present
& FTB_MESSAGE
)
5052 ? (const char *)(msg
-> message
.data
)
5053 : (dhcp_failover_reject_reason_print
5054 (msg
-> reject_reason
)));
5058 /* Silently discard acks for leases we did not update (or multiple
5061 if (!lease
->last_xid
)
5064 if (lease
->last_xid
!= msg
->xid
) {
5065 message
= "xid mismatch";
5069 /* XXX Times may need to be adjusted based on clock skew! */
5070 if (msg
->options_present
& FTO_POTENTIAL_EXPIRY
)
5071 pot_expire
= msg
->potential_expiry
;
5073 pot_expire
= lease
->tstp
;
5075 /* If the lease was desired to enter a binding state, we set
5076 * such a value upon transmitting a bndupd. We do not clear it
5077 * if we receive a bndupd in the meantime (or change the state
5078 * of the lease again ourselves), but we do set binding_state
5079 * if we get a bndupd.
5081 * So desired_binding_state tells us what we sent a bndupd for,
5082 * and binding_state tells us what we have since determined in
5085 if (lease
->desired_binding_state
== FTS_EXPIRED
||
5086 lease
->desired_binding_state
== FTS_RESET
||
5087 lease
->desired_binding_state
== FTS_RELEASED
)
5089 /* It is not a problem to do this directly as we call
5090 * supersede_lease immediately after: the lease is requeued
5091 * even if its sort order (tsfp) has changed.
5093 lease
->atsfp
= lease
->tsfp
= pot_expire
;
5094 if ((state
->i_am
== secondary
) &&
5095 (lease
->flags
& RESERVED_LEASE
))
5096 lease
->next_binding_state
= FTS_BACKUP
;
5098 lease
->next_binding_state
= FTS_FREE
;
5099 /* Clear this condition for the next go-round. */
5100 lease
->desired_binding_state
= lease
->next_binding_state
;
5101 supersede_lease(lease
, (struct lease
*)0, 0, 0, 0);
5104 /* Lease has returned to FREE state from the
5105 * transitional states. If the lease 'belongs'
5106 * to a client that would be served by the
5107 * peer, process a binding update now to send
5108 * the lease to backup state. But not if we
5109 * think we already have.
5111 if (state
->i_am
== primary
&&
5112 !(lease
->flags
& (RESERVED_LEASE
| BOOTP_LEASE
)) &&
5113 peer_wants_lease(lease
))
5114 send_to_backup
= ISC_TRUE
;
5116 if (!send_to_backup
&& state
->me
.state
== normal
)
5119 /* XXX It could be a problem to do this directly if the lease
5120 * XXX is sorted by tsfp.
5122 lease
->atsfp
= lease
->tsfp
= pot_expire
;
5123 if (lease
->desired_binding_state
!= lease
->binding_state
) {
5124 lease
->next_binding_state
=
5125 lease
->desired_binding_state
;
5126 supersede_lease(lease
,
5127 (struct lease
*)0, 0, 0, 0);
5130 /* Commit the lease only after a two-second timeout,
5131 so that if we get a bunch of acks in quick
5132 successtion (e.g., when stealing leases from the
5133 secondary), we do not do an immediate commit for
5135 add_timeout(cur_time
+ 2,
5136 commit_leases_timeout
, (void *)0, 0, 0);
5140 dhcp_failover_ack_queue_remove (state
, lease
);
5142 /* If we are supposed to send an update done after we send
5143 this lease, go ahead and send it. */
5144 if (state
-> send_update_done
== lease
) {
5145 lease_dereference (&state
-> send_update_done
, MDL
);
5146 dhcp_failover_send_update_done (state
);
5149 /* Now that the lease is off the ack queue, consider putting it
5150 * back on the update queue for mac address affinity.
5152 if (send_to_backup
&& secondary_not_hoarding(state
, lease
->pool
)) {
5153 lease
->next_binding_state
= FTS_BACKUP
;
5154 lease
->tstp
= lease
->starts
= cur_time
;
5156 if (!supersede_lease(lease
, NULL
, 0, 1, 0) ||
5157 !write_lease(lease
))
5158 log_error("can't commit lease %s for "
5159 "client affinity", piaddr(lease
->ip_addr
));
5161 if (state
->me
.state
== normal
)
5165 /* If there are updates pending, we've created space to send at
5167 dhcp_failover_send_updates (state
);
5170 lease_dereference (&lease
, MDL
);
5172 lease_dereference (<
, MDL
);
5174 return ISC_R_SUCCESS
;
5177 log_info ("bind update on %s got ack from %s: %s.",
5178 piaddr (ia
), state
-> name
, message
);
5182 isc_result_t
dhcp_failover_generate_update_queue (dhcp_failover_state_t
*state
,
5185 struct shared_network
*s
;
5189 #define FREE_LEASES 0
5190 #define ACTIVE_LEASES 1
5191 #define EXPIRED_LEASES 2
5192 #define ABANDONED_LEASES 3
5193 #define BACKUP_LEASES 4
5194 #define RESERVED_LEASES 5
5195 struct lease
**lptr
[RESERVED_LEASES
+1];
5197 /* Loop through each pool in each shared network and call the
5198 expiry routine on the pool. */
5199 for (s
= shared_networks
; s
; s
= s
-> next
) {
5200 for (p
= s
-> pools
; p
; p
= p
-> next
) {
5201 if (p
->failover_peer
!= state
)
5204 lptr
[FREE_LEASES
] = &p
->free
;
5205 lptr
[ACTIVE_LEASES
] = &p
->active
;
5206 lptr
[EXPIRED_LEASES
] = &p
->expired
;
5207 lptr
[ABANDONED_LEASES
] = &p
->abandoned
;
5208 lptr
[BACKUP_LEASES
] = &p
->backup
;
5209 lptr
[RESERVED_LEASES
] = &p
->reserved
;
5211 for (i
= FREE_LEASES
; i
<= RESERVED_LEASES
; i
++) {
5212 for (l
= *(lptr
[i
]); l
; l
= l
-> next
) {
5213 if ((l
->flags
& ON_QUEUE
) == 0 &&
5215 (l
->tstp
> l
->atsfp
) ||
5216 (i
== EXPIRED_LEASES
))) {
5217 l
-> desired_binding_state
= l
-> binding_state
;
5218 dhcp_failover_queue_update (l
, 0);
5224 return ISC_R_SUCCESS
;
5228 dhcp_failover_process_update_request (dhcp_failover_state_t
*state
,
5229 failover_message_t
*msg
)
5231 if (state
->send_update_done
) {
5232 log_info("Received update request while old update still "
5233 "flying! Silently discarding old request.");
5234 lease_dereference(&state
->send_update_done
, MDL
);
5237 /* Generate a fresh update queue. */
5238 dhcp_failover_generate_update_queue (state
, 0);
5240 state
->updxid
= msg
->xid
;
5242 /* If there's anything on the update queue (there shouldn't be
5243 anything on the ack queue), trigger an update done message
5244 when we get an ack for that lease. */
5245 if (state
-> update_queue_tail
) {
5246 lease_reference (&state
-> send_update_done
,
5247 state
-> update_queue_tail
, MDL
);
5248 dhcp_failover_send_updates (state
);
5249 log_info ("Update request from %s: sending update",
5252 /* Otherwise, there are no updates to send, so we can
5253 just send an UPDDONE message immediately. */
5254 dhcp_failover_send_update_done (state
);
5255 log_info ("Update request from %s: nothing pending",
5259 return ISC_R_SUCCESS
;
5263 dhcp_failover_process_update_request_all (dhcp_failover_state_t
*state
,
5264 failover_message_t
*msg
)
5266 if (state
->send_update_done
) {
5267 log_info("Received update request while old update still "
5268 "flying! Silently discarding old request.");
5269 lease_dereference(&state
->send_update_done
, MDL
);
5272 /* Generate a fresh update queue that includes every lease. */
5273 dhcp_failover_generate_update_queue (state
, 1);
5275 state
->updxid
= msg
->xid
;
5277 if (state
-> update_queue_tail
) {
5278 lease_reference (&state
-> send_update_done
,
5279 state
-> update_queue_tail
, MDL
);
5280 dhcp_failover_send_updates (state
);
5281 log_info ("Update request all from %s: sending update",
5284 /* This should really never happen, but it could happen
5285 on a server that currently has no leases configured. */
5286 dhcp_failover_send_update_done (state
);
5287 log_info ("Update request all from %s: nothing pending",
5291 return ISC_R_SUCCESS
;
5295 dhcp_failover_process_update_done (dhcp_failover_state_t
*state
,
5296 failover_message_t
*msg
)
5298 log_info ("failover peer %s: peer update completed.",
5301 state
-> curUPD
= 0;
5303 switch (state
-> me
.state
) {
5307 case communications_interrupted
:
5308 case resolution_interrupted
:
5314 break; /* shouldn't happen. */
5316 /* We got the UPDDONE, so we can go into normal state! */
5317 case potential_conflict
:
5318 if (state
->partner
.state
== conflict_done
) {
5319 if (state
->i_am
== secondary
) {
5320 dhcp_failover_set_state (state
, normal
);
5322 log_error("Secondary is in conflict_done "
5323 "state after conflict resolution, "
5324 "this is illegal.");
5325 dhcp_failover_set_state (state
, shut_down
);
5328 if (state
->i_am
== primary
)
5329 dhcp_failover_set_state (state
, conflict_done
);
5331 log_error("Spurious update-done message.");
5337 log_error("Spurious update-done message.");
5341 /* Wait for MCLT to expire before moving to recover_done,
5342 except that if both peers come up in recover, there is
5343 no point in waiting for MCLT to expire - this probably
5344 indicates the initial startup of a newly-configured
5346 if (state
-> me
.stos
+ state
-> mclt
> cur_time
&&
5347 state
-> partner
.state
!= recover
&&
5348 state
-> partner
.state
!= recover_done
) {
5349 dhcp_failover_set_state (state
, recover_wait
);
5350 #if defined (DEBUG_FAILOVER_TIMING)
5351 log_info ("add_timeout +%d %s",
5353 state
-> me
.stos
+ state
-> mclt
),
5354 "dhcp_failover_recover_done");
5356 add_timeout ((int)(state
-> me
.stos
+ state
-> mclt
),
5357 dhcp_failover_recover_done
,
5359 (tvref_t
)omapi_object_reference
,
5361 omapi_object_dereference
);
5363 dhcp_failover_recover_done (state
);
5366 return ISC_R_SUCCESS
;
5369 void dhcp_failover_recover_done (void *sp
)
5371 dhcp_failover_state_t
*state
= sp
;
5373 #if defined (DEBUG_FAILOVER_TIMING)
5374 log_info ("dhcp_failover_recover_done");
5377 dhcp_failover_set_state (state
, recover_done
);
5380 #if defined (DEBUG_FAILOVER_MESSAGES)
5381 /* Print hunks of failover messages, doing line breaks as appropriate.
5382 Note that this assumes syslog is being used, rather than, e.g., the
5383 Windows NT logging facility, where just dumping the whole message in
5384 one hunk would be more appropriate. */
5386 void failover_print (char *obuf
,
5387 unsigned *obufix
, unsigned obufmax
, const char *s
)
5389 int len
= strlen (s
);
5391 while (len
+ *obufix
+ 1 >= obufmax
) {
5392 log_debug ("%s", obuf
);
5394 log_debug ("%s", s
);
5400 strcpy (&obuf
[*obufix
], s
);
5403 #endif /* defined (DEBUG_FAILOVER_MESSAGES) */
5405 /* Taken from draft-ietf-dhc-loadb-01.txt: */
5406 /* A "mixing table" of 256 distinct values, in pseudo-random order. */
5407 unsigned char loadb_mx_tbl
[256] = {
5408 251, 175, 119, 215, 81, 14, 79, 191, 103, 49,
5409 181, 143, 186, 157, 0, 232, 31, 32, 55, 60,
5410 152, 58, 17, 237, 174, 70, 160, 144, 220, 90,
5411 57, 223, 59, 3, 18, 140, 111, 166, 203, 196,
5412 134, 243, 124, 95, 222, 179, 197, 65, 180, 48,
5413 36, 15, 107, 46, 233, 130, 165, 30, 123, 161,
5414 209, 23, 97, 16, 40, 91, 219, 61, 100, 10,
5415 210, 109, 250, 127, 22, 138, 29, 108, 244, 67,
5416 207, 9, 178, 204, 74, 98, 126, 249, 167, 116,
5417 34, 77, 193, 200, 121, 5, 20, 113, 71, 35,
5418 128, 13, 182, 94, 25, 226, 227, 199, 75, 27,
5419 41, 245, 230, 224, 43, 225, 177, 26, 155, 150,
5420 212, 142, 218, 115, 241, 73, 88, 105, 39, 114,
5421 62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
5422 154, 122, 12, 84, 82, 163, 44, 139, 228, 236,
5423 205, 242, 217, 11, 187, 146, 159, 64, 86, 239,
5424 195, 42, 106, 198, 118, 112, 184, 172, 87, 2,
5425 173, 117, 176, 229, 247, 253, 137, 185, 99, 164,
5426 102, 147, 45, 66, 231, 52, 141, 211, 194, 206,
5427 246, 238, 56, 110, 78, 248, 63, 240, 189, 93,
5428 92, 51, 53, 183, 19, 171, 72, 50, 33, 104,
5429 101, 69, 8, 252, 83, 120, 76, 135, 85, 54,
5430 202, 125, 188, 213, 96, 235, 136, 208, 162, 129,
5431 190, 132, 156, 38, 47, 1, 7, 254, 24, 4,
5432 216, 131, 89, 21, 28, 133, 37, 153, 149, 80,
5433 170, 68, 6, 169, 234, 151 };
5435 static unsigned char loadb_p_hash (const unsigned char *, unsigned);
5436 static unsigned char loadb_p_hash (const unsigned char *key
, unsigned len
)
5438 unsigned char hash
= len
;
5440 for(i
= len
; i
> 0; )
5441 hash
= loadb_mx_tbl
[hash
^ (key
[--i
])];
5445 int load_balance_mine (struct packet
*packet
, dhcp_failover_state_t
*state
)
5447 struct option_cache
*oc
;
5448 struct data_string ds
;
5449 unsigned char hbaix
;
5452 if (state
-> load_balance_max_secs
< ntohs (packet
-> raw
-> secs
)) {
5456 /* If we don't have a hash bucket array, we can't tell if this
5457 one's ours, so we assume it's not. */
5461 oc
= lookup_option (&dhcp_universe
, packet
-> options
,
5462 DHO_DHCP_CLIENT_IDENTIFIER
);
5463 memset (&ds
, 0, sizeof ds
);
5465 evaluate_option_cache (&ds
, packet
, (struct lease
*)0,
5466 (struct client_state
*)0,
5467 packet
-> options
, (struct option_state
*)0,
5468 &global_scope
, oc
, MDL
)) {
5469 hbaix
= loadb_p_hash (ds
.data
, ds
.len
);
5471 hbaix
= loadb_p_hash (packet
-> raw
-> chaddr
,
5472 packet
-> raw
-> hlen
);
5475 hm
= state
->hba
[(hbaix
>> 3) & 0x1F] & (1 << (hbaix
& 0x07));
5477 if (state
-> i_am
== primary
)
5483 /* The inverse of load_balance_mine ("load balance theirs"). We can't
5484 * use the regular load_balance_mine() and invert it because of the case
5485 * where there might not be an HBA, and we want to indicate false here
5486 * in this case only.
5489 peer_wants_lease(struct lease
*lp
)
5491 dhcp_failover_state_t
*state
;
5492 unsigned char hbaix
;
5498 state
= lp
->pool
->failover_peer
;
5500 if (!state
|| !state
->hba
)
5504 hbaix
= loadb_p_hash (lp
->uid
, lp
->uid_len
);
5506 hbaix
= loadb_p_hash (lp
->hardware_addr
.hbuf
,
5507 lp
->hardware_addr
.hlen
);
5509 hm
= state
->hba
[(hbaix
>> 3) & 0x1F] & (1 << (hbaix
& 0x07));
5511 if (state
->i_am
== primary
)
5517 /* This deals with what to do with bind updates when
5518 we're in the normal state
5520 Note that tsfp had better be set from the latest bind update
5521 _before_ this function is called! */
5524 normal_binding_state_transition_check (struct lease
*lease
,
5525 dhcp_failover_state_t
*state
,
5526 binding_state_t binding_state
,
5529 binding_state_t new_state
;
5531 /* If there is no transition, it's no problem. */
5532 if (binding_state
== lease
-> binding_state
)
5533 return binding_state
;
5535 switch (lease
-> binding_state
) {
5538 switch (binding_state
) {
5545 /* If the lease was free, and our peer is primary,
5546 then it can make it active, or abandoned, or
5547 backup. Abandoned is treated like free in
5549 if (state
-> i_am
== secondary
)
5550 return binding_state
;
5552 /* Otherwise, it can't legitimately do any sort of
5553 state transition. Because the lease was free,
5554 and the error has already been made, we allow the
5555 peer to change its state anyway, but log a warning
5556 message in hopes that the error will be fixed. */
5557 case FTS_FREE
: /* for compiler */
5558 new_state
= binding_state
;
5562 log_fatal ("Impossible case at %s:%d.", MDL
);
5566 /* The secondary can't change the state of an active
5568 if (state
-> i_am
== primary
) {
5569 /* Except that the client may send the DHCPRELEASE
5570 to the secondary, and we have to accept that. */
5571 if (binding_state
== FTS_RELEASED
)
5572 return binding_state
;
5573 new_state
= lease
-> binding_state
;
5577 /* So this is only for transitions made by the primary: */
5578 switch (binding_state
) {
5581 /* Can't set a lease to free or backup until the
5582 peer agrees that it's expired. */
5583 if (tsfp
> cur_time
) {
5584 new_state
= lease
-> binding_state
;
5587 return binding_state
;
5590 /* XXX 65 should be the clock skew between the peers
5591 XXX plus a fudge factor. This code will result
5592 XXX in problems if MCLT is really short or the
5593 XXX max-lease-time is really short (less than the
5594 XXX fudge factor. */
5595 if (lease
-> ends
- 65 > cur_time
) {
5596 new_state
= lease
-> binding_state
;
5604 return binding_state
;
5607 log_fatal ("Impossible case at %s:%d.", MDL
);
5612 switch (binding_state
) {
5615 /* Can't set a lease to free or backup until the
5616 peer agrees that it's expired. */
5617 if (tsfp
> cur_time
) {
5618 new_state
= lease
-> binding_state
;
5621 return binding_state
;
5628 return binding_state
;
5631 log_fatal ("Impossible case at %s:%d.", MDL
);
5635 switch (binding_state
) {
5639 /* These are invalid state transitions - should we
5646 return binding_state
;
5649 log_fatal ("Impossible case at %s:%d.", MDL
);
5653 switch (binding_state
) {
5656 /* Can't set a lease to free or backup until the
5657 peer agrees that it's expired. */
5658 if (tsfp
> cur_time
) {
5659 new_state
= lease
-> binding_state
;
5662 return binding_state
;
5669 return binding_state
;
5672 log_fatal ("Impossible case at %s:%d.", MDL
);
5676 switch (binding_state
) {
5682 /* If the lease was in backup, and our peer
5683 is secondary, then it can make it active
5685 if (state
-> i_am
== primary
)
5686 return binding_state
;
5688 /* Either the primary or the secondary can
5689 reasonably move a lease from the backup
5690 state to the free state. */
5692 return binding_state
;
5695 new_state
= lease
-> binding_state
;
5699 log_fatal ("Impossible case at %s:%d.", MDL
);
5704 log_fatal ("Impossible case at %s:%d.", MDL
);
5711 /* Determine whether the state transition is okay when we're potentially
5712 in conflict with the peer. */
5714 conflict_binding_state_transition_check (struct lease
*lease
,
5715 dhcp_failover_state_t
*state
,
5716 binding_state_t binding_state
,
5719 binding_state_t new_state
;
5721 /* If there is no transition, it's no problem. */
5722 if (binding_state
== lease
-> binding_state
)
5723 new_state
= binding_state
;
5725 switch (lease
-> binding_state
) {
5726 /* If we think the lease is not in use, then the
5727 state into which the partner put it is just fine,
5735 new_state
= binding_state
;
5738 /* If we think the lease *is* in use, then we're not
5739 going to take the partner's change if the partner
5740 thinks it's free. */
5742 switch (binding_state
) {
5745 new_state
= lease
-> binding_state
;
5749 /* If we don't agree about expiry, it's
5750 * invalid. 65 should allow for max
5751 * clock skew (60) plus some fudge.
5752 * XXX: should we refetch cur_time?
5754 if ((lease
->ends
- 65) > cur_time
)
5755 new_state
= lease
->binding_state
;
5757 new_state
= binding_state
;
5760 /* RELEASED, RESET, and ABANDONED indicate
5761 * that our partner has information about
5762 * this lease that we did not witness. Our
5768 new_state
= binding_state
;
5772 log_fatal ("Impossible case at %s:%d.", MDL
);
5778 log_fatal ("Impossible case at %s:%d.", MDL
);
5785 /* We can reallocate a lease under the following circumstances:
5787 (1) It belongs to us - it's FTS_FREE, and we're primary, or it's
5788 FTS_BACKUP, and we're secondary.
5789 (2) We're in partner_down, and the lease is not active, and we
5790 can be sure that the other server didn't make it active.
5791 We can only be sure that the server didn't make it active
5792 when we are in the partner_down state and one of the following
5793 two conditions holds:
5794 (a) in the case that the time sent from the peer is earlier than
5795 the time we entered the partner_down state, at least MCLT has
5796 gone by since we entered partner_down, or
5797 (b) in the case that the time sent from the peer is later than
5798 the time when we entered partner_down, the current time is
5799 later than the time sent from the peer by at least MCLT. */
5801 int lease_mine_to_reallocate (struct lease
*lease
)
5803 dhcp_failover_state_t
*peer
;
5805 if (lease
&& lease
->pool
&&
5806 (peer
= lease
->pool
->failover_peer
)) {
5807 switch (lease
->binding_state
) {
5809 /* ACTIVE leases may not be reallocated. */
5814 /* FREE leases may only be allocated by the primary,
5815 * unless the secondary is acting in partner_down
5816 * state and stos+mclt or tsfp+mclt has expired,
5817 * whichever is greater.
5819 * ABANDONED are treated the same as FREE for all
5820 * purposes here. Note that servers will only try
5821 * for ABANDONED leases as a last resort anyway.
5823 if (peer
-> i_am
== primary
)
5826 return(peer
->service_state
== service_partner_down
&&
5827 ((lease
->tsfp
< peer
->me
.stos
) ?
5828 (peer
->me
.stos
+ peer
->mclt
< cur_time
) :
5829 (lease
->tsfp
+ peer
->mclt
< cur_time
)));
5834 /* These three lease states go onto the 'expired'
5835 * queue. Upon entry into partner-down state, this
5836 * queue of leases has their tsfp values modified
5837 * to equal stos+mclt, the point at which the server
5838 * is allowed to remove them from these transitional
5839 * states without an acknowledgement.
5841 * Note that although tsfp has been possibly extended
5842 * past the actual tsfp we received from the peer, we
5843 * don't have to take any special action. Since tsfp
5844 * is now in the past (or now), we can guarantee that
5845 * this server will only allocate a lease time equal
5846 * to MCLT, rather than a TSFP-optimal lease, which is
5847 * the only danger for a lease in one of these states.
5849 return((peer
->service_state
== service_partner_down
) &&
5850 (lease
->tsfp
< cur_time
));
5853 /* Only the secondary may allocate BACKUP leases,
5854 * unless in partner_down state in which case at
5855 * least TSFP+MCLT or STOS+MCLT must have expired,
5856 * whichever is greater.
5858 if (peer
->i_am
== secondary
)
5861 return((peer
->service_state
== service_partner_down
) &&
5862 ((lease
->tsfp
< peer
->me
.stos
) ?
5863 (peer
->me
.stos
+ peer
->mclt
< cur_time
) :
5864 (lease
->tsfp
+ peer
->mclt
< cur_time
)));
5867 /* All lease states appear above. */
5868 log_fatal("Impossible case at %s:%d.", MDL
);
5874 return(lease
->binding_state
== FTS_FREE
||
5875 lease
->binding_state
== FTS_BACKUP
);
5880 static isc_result_t
failover_message_reference (failover_message_t
**mp
,
5881 failover_message_t
*m
,
5882 const char *file
, int line
)
5886 return ISC_R_SUCCESS
;
5889 static isc_result_t
failover_message_dereference (failover_message_t
**mp
,
5890 const char *file
, int line
)
5892 failover_message_t
*m
;
5895 if (m
-> refcnt
== 0) {
5897 failover_message_dereference (&m
-> next
,
5899 if (m
-> chaddr
.data
)
5900 dfree (m
-> chaddr
.data
, file
, line
);
5901 if (m
-> client_identifier
.data
)
5902 dfree (m
-> client_identifier
.data
, file
, line
);
5904 dfree (m
-> hba
.data
, file
, line
);
5905 if (m
-> message
.data
)
5906 dfree (m
-> message
.data
, file
, line
);
5907 if (m
-> reply_options
.data
)
5908 dfree (m
-> reply_options
.data
, file
, line
);
5909 if (m
-> request_options
.data
)
5910 dfree (m
-> request_options
.data
, file
, line
);
5911 if (m
-> vendor_class
.data
)
5912 dfree (m
-> vendor_class
.data
, file
, line
);
5913 if (m
-> vendor_options
.data
)
5914 dfree (m
-> vendor_options
.data
, file
, line
);
5916 dfree (m
-> ddns
.data
, file
, line
);
5917 dfree (*mp
, file
, line
);
5920 return ISC_R_SUCCESS
;
5923 OMAPI_OBJECT_ALLOC (dhcp_failover_state
, dhcp_failover_state_t
,
5924 dhcp_type_failover_state
)
5925 OMAPI_OBJECT_ALLOC (dhcp_failover_listener
, dhcp_failover_listener_t
,
5926 dhcp_type_failover_listener
)
5927 OMAPI_OBJECT_ALLOC (dhcp_failover_link
, dhcp_failover_link_t
,
5928 dhcp_type_failover_link
)
5929 #endif /* defined (FAILOVER_PROTOCOL) */
5931 const char *binding_state_print (enum failover_state state
)