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 static char copyright
[] =
37 "$Id: failover.c,v 1.73 2007/06/01 22:26:01 dhankins Exp $ Copyright (c) 2004-2007 Internet Systems Consortium. All rights reserved.\n";
41 #include <omapip/omapip_p.h>
43 #if defined (FAILOVER_PROTOCOL)
44 dhcp_failover_state_t
*failover_states
;
45 static isc_result_t
do_a_failover_option (omapi_object_t
*,
46 dhcp_failover_link_t
*);
47 dhcp_failover_listener_t
*failover_listeners
;
49 static isc_result_t
failover_message_reference (failover_message_t
**,
51 const char *file
, int line
);
52 static isc_result_t
failover_message_dereference (failover_message_t
**,
53 const char *file
, int line
);
55 static void dhcp_failover_pool_balance(dhcp_failover_state_t
*state
);
56 static void dhcp_failover_pool_reqbalance(dhcp_failover_state_t
*state
);
57 static int dhcp_failover_pool_dobalance(dhcp_failover_state_t
*state
);
58 static INLINE
int secondary_not_hoarding(dhcp_failover_state_t
*state
,
62 void dhcp_failover_startup ()
64 dhcp_failover_state_t
*state
;
66 dhcp_failover_listener_t
*l
;
68 for (state
= failover_states
; state
; state
= state
-> next
) {
69 dhcp_failover_state_transition (state
, "startup");
71 if (state
-> pool_count
== 0) {
72 log_error ("failover peer declaration with no %s",
74 log_error ("In order to use failover, you MUST %s",
75 "refer to your main failover declaration");
76 log_error ("in each pool declaration. You MUST %s",
77 "NOT use range declarations outside");
78 log_fatal ("of pool declarations.");
80 /* In case the peer is already running, immediately try
81 to establish a connection with it. */
82 status
= dhcp_failover_link_initiate ((omapi_object_t
*)state
);
83 if (status
!= ISC_R_SUCCESS
&& status
!= ISC_R_INCOMPLETE
) {
84 #if defined (DEBUG_FAILOVER_TIMING)
85 log_info ("add_timeout +90 dhcp_failover_reconnect");
87 add_timeout (cur_time
+ 90,
88 dhcp_failover_reconnect
, state
,
90 dhcp_failover_state_reference
,
92 dhcp_failover_state_dereference
);
93 log_error ("failover peer %s: %s", state
-> name
,
94 isc_result_totext (status
));
97 status
= (dhcp_failover_listen
98 ((omapi_object_t
*)state
));
99 if (status
!= ISC_R_SUCCESS
) {
100 #if defined (DEBUG_FAILOVER_TIMING)
101 log_info ("add_timeout +90 %s",
102 "dhcp_failover_listener_restart");
104 add_timeout (cur_time
+ 90,
105 dhcp_failover_listener_restart
,
107 (tvref_t
)omapi_object_reference
,
108 (tvunref_t
)omapi_object_dereference
);
113 int dhcp_failover_write_all_states ()
115 dhcp_failover_state_t
*state
;
117 for (state
= failover_states
; state
; state
= state
-> next
) {
118 if (!write_failover_state (state
))
124 isc_result_t
enter_failover_peer (peer
)
125 dhcp_failover_state_t
*peer
;
127 dhcp_failover_state_t
*dup
= (dhcp_failover_state_t
*)0;
130 status
= find_failover_peer (&dup
, peer
-> name
, MDL
);
131 if (status
== ISC_R_NOTFOUND
) {
132 if (failover_states
) {
133 dhcp_failover_state_reference (&peer
-> next
,
134 failover_states
, MDL
);
135 dhcp_failover_state_dereference (&failover_states
,
138 dhcp_failover_state_reference (&failover_states
, peer
, MDL
);
139 return ISC_R_SUCCESS
;
141 dhcp_failover_state_dereference (&dup
, MDL
);
142 if (status
== ISC_R_SUCCESS
)
147 isc_result_t
find_failover_peer (peer
, name
, file
, line
)
148 dhcp_failover_state_t
**peer
;
153 dhcp_failover_state_t
*p
;
155 for (p
= failover_states
; p
; p
= p
-> next
)
156 if (!strcmp (name
, p
-> name
))
159 return dhcp_failover_state_reference (peer
, p
, file
, line
);
160 return ISC_R_NOTFOUND
;
163 /* The failover protocol has three objects associated with it. For
164 each failover partner declaration in the dhcpd.conf file, primary
165 or secondary, there is a failover_state object. For any primary or
166 secondary state object that has a connection to its peer, there is
167 also a failover_link object, which has its own input state separate
168 from the failover protocol state for managing the actual bytes
169 coming in off the wire. Finally, there will be one listener object
170 for every distinct port number associated with a secondary
171 failover_state object. Normally all secondary failover_state
172 objects are expected to listen on the same port number, so there
173 need be only one listener object, but if different port numbers are
174 specified for each failover object, there could be as many as one
175 listener object for each secondary failover_state object. */
177 /* This, then, is the implemention of the failover link object. */
179 isc_result_t
dhcp_failover_link_initiate (omapi_object_t
*h
)
182 dhcp_failover_link_t
*obj
;
183 omapi_value_t
*value
= (omapi_value_t
*)0;
184 dhcp_failover_state_t
*state
;
187 struct data_string ds
;
188 omapi_addr_list_t
*addrs
= (omapi_addr_list_t
*)0;
189 omapi_addr_t local_addr
;
191 /* Find the failover state in the object chain. */
192 for (o
= h
; o
-> outer
; o
= o
-> outer
)
194 for (; o
; o
= o
-> inner
) {
195 if (o
-> type
== dhcp_type_failover_state
)
199 return ISC_R_INVALIDARG
;
200 state
= (dhcp_failover_state_t
*)o
;
202 obj
= (dhcp_failover_link_t
*)0;
203 status
= dhcp_failover_link_allocate (&obj
, MDL
);
204 if (status
!= ISC_R_SUCCESS
)
206 option_cache_reference (&obj
-> peer_address
,
207 state
-> partner
.address
, MDL
);
208 obj
-> peer_port
= state
-> partner
.port
;
209 dhcp_failover_state_reference (&obj
-> state_object
, state
, MDL
);
211 memset (&ds
, 0, sizeof ds
);
212 if (!evaluate_option_cache (&ds
, (struct packet
*)0, (struct lease
*)0,
213 (struct client_state
*)0,
214 (struct option_state
*)0,
215 (struct option_state
*)0,
216 &global_scope
, obj
-> peer_address
, MDL
)) {
217 dhcp_failover_link_dereference (&obj
, MDL
);
218 return ISC_R_UNEXPECTED
;
221 /* Make an omapi address list out of a buffer containing zero or more
223 status
= omapi_addr_list_new (&addrs
, ds
.len
/ 4, MDL
);
224 if (status
!= ISC_R_SUCCESS
) {
225 dhcp_failover_link_dereference (&obj
, MDL
);
229 for (i
= 0; i
< addrs
-> count
; i
++) {
230 addrs
-> addresses
[i
].addrtype
= AF_INET
;
231 addrs
-> addresses
[i
].addrlen
= sizeof (struct in_addr
);
232 memcpy (addrs
-> addresses
[i
].address
,
233 &ds
.data
[i
* 4], sizeof (struct in_addr
));
234 addrs
-> addresses
[i
].port
= obj
-> peer_port
;
236 data_string_forget (&ds
, MDL
);
238 /* Now figure out the local address that we're supposed to use. */
239 if (!state
-> me
.address
||
240 !evaluate_option_cache (&ds
, (struct packet
*)0,
242 (struct client_state
*)0,
243 (struct option_state
*)0,
244 (struct option_state
*)0,
245 &global_scope
, state
-> me
.address
,
247 memset (&local_addr
, 0, sizeof local_addr
);
248 local_addr
.addrtype
= AF_INET
;
249 local_addr
.addrlen
= sizeof (struct in_addr
);
250 if (!state
-> server_identifier
.len
) {
251 log_fatal ("failover peer %s: no local address.",
255 if (ds
.len
!= sizeof (struct in_addr
)) {
256 data_string_forget (&ds
, MDL
);
257 dhcp_failover_link_dereference (&obj
, MDL
);
258 omapi_addr_list_dereference (&addrs
, MDL
);
259 return ISC_R_INVALIDARG
;
261 local_addr
.addrtype
= AF_INET
;
262 local_addr
.addrlen
= ds
.len
;
263 memcpy (local_addr
.address
, ds
.data
, ds
.len
);
264 if (!state
-> server_identifier
.len
)
265 data_string_copy (&state
-> server_identifier
,
267 data_string_forget (&ds
, MDL
);
268 local_addr
.port
= 0; /* Let the O.S. choose. */
271 status
= omapi_connect_list ((omapi_object_t
*)obj
,
273 omapi_addr_list_dereference (&addrs
, MDL
);
275 dhcp_failover_link_dereference (&obj
, MDL
);
279 isc_result_t
dhcp_failover_link_signal (omapi_object_t
*h
,
280 const char *name
, va_list ap
)
283 dhcp_failover_link_t
*link
;
287 dhcp_failover_state_t
*s
, *state
= (dhcp_failover_state_t
*)0;
289 if (h
-> type
!= dhcp_type_failover_link
) {
290 /* XXX shouldn't happen. Put an assert here? */
291 return ISC_R_UNEXPECTED
;
293 link
= (dhcp_failover_link_t
*)h
;
295 if (!strcmp (name
, "connect")) {
296 if (link
-> state_object
-> i_am
== primary
) {
297 status
= dhcp_failover_send_connect (h
);
298 if (status
!= ISC_R_SUCCESS
) {
299 log_info ("dhcp_failover_send_connect: %s",
300 isc_result_totext (status
));
301 omapi_disconnect (h
-> outer
, 1);
304 status
= ISC_R_SUCCESS
;
305 /* Allow the peer fifteen seconds to send us a
307 #if defined (DEBUG_FAILOVER_TIMING)
308 log_info ("add_timeout +15 %s",
309 "dhcp_failover_link_startup_timeout");
311 add_timeout (cur_time
+ 15,
312 dhcp_failover_link_startup_timeout
,
314 (tvref_t
)dhcp_failover_link_reference
,
315 (tvunref_t
)dhcp_failover_link_dereference
);
319 if (!strcmp (name
, "disconnect")) {
320 if (link
-> state_object
) {
321 dhcp_failover_state_reference (&state
,
322 link
-> state_object
, MDL
);
323 link
-> state
= dhcp_flink_disconnected
;
325 /* Make the transition. */
326 if (state
-> link_to_peer
== link
) {
327 dhcp_failover_state_transition (link
-> state_object
,
330 /* Start trying to reconnect. */
331 #if defined (DEBUG_FAILOVER_TIMING)
332 log_info ("add_timeout +5 %s",
333 "dhcp_failover_reconnect");
335 add_timeout (cur_time
+ 5, dhcp_failover_reconnect
,
337 (tvref_t
)dhcp_failover_state_reference
,
338 (tvunref_t
)dhcp_failover_state_dereference
);
340 dhcp_failover_state_dereference (&state
, MDL
);
342 return ISC_R_SUCCESS
;
345 if (!strcmp (name
, "status")) {
346 if (link
-> state_object
) {
349 status
= va_arg(ap
, isc_result_t
);
351 if ((status
== ISC_R_HOSTUNREACH
) || (status
== ISC_R_TIMEDOUT
)) {
352 dhcp_failover_state_reference (&state
,
353 link
-> state_object
, MDL
);
354 link
-> state
= dhcp_flink_disconnected
;
356 /* Make the transition. */
357 dhcp_failover_state_transition (link
-> state_object
,
360 /* Start trying to reconnect. */
361 #if defined (DEBUG_FAILOVER_TIMING)
362 log_info ("add_timeout +5 %s",
363 "dhcp_failover_reconnect");
365 add_timeout (cur_time
+ 5, dhcp_failover_reconnect
,
367 (tvref_t
)dhcp_failover_state_reference
,
368 (tvunref_t
)dhcp_failover_state_dereference
);
370 dhcp_failover_state_dereference (&state
, MDL
);
372 return ISC_R_SUCCESS
;
375 /* Not a signal we recognize? */
376 if (strcmp (name
, "ready")) {
377 if (h
-> inner
&& h
-> inner
-> type
-> signal_handler
)
378 return (*(h
-> inner
-> type
-> signal_handler
))
379 (h
-> inner
, name
, ap
);
380 return ISC_R_NOTFOUND
;
383 if (!h
-> outer
|| h
-> outer
-> type
!= omapi_type_connection
)
384 return ISC_R_INVALIDARG
;
387 /* We get here because we requested that we be woken up after
388 some number of bytes were read, and that number of bytes
389 has in fact been read. */
390 switch (link
-> state
) {
391 case dhcp_flink_start
:
392 link
-> state
= dhcp_flink_message_length_wait
;
393 if ((omapi_connection_require (c
, 2)) != ISC_R_SUCCESS
)
395 case dhcp_flink_message_length_wait
:
397 link
-> state
= dhcp_flink_message_wait
;
398 link
-> imsg
= dmalloc (sizeof (failover_message_t
), MDL
);
400 status
= ISC_R_NOMEMORY
;
403 failover_message_dereference (&link
->imsg
,
406 link
-> state
= dhcp_flink_disconnected
;
407 log_info ("message length wait: %s",
408 isc_result_totext (status
));
409 omapi_disconnect (c
, 1);
410 /* XXX just blow away the protocol state now?
411 XXX or will disconnect blow it away? */
412 return ISC_R_UNEXPECTED
;
414 memset (link
-> imsg
, 0, sizeof (failover_message_t
));
415 link
-> imsg
-> refcnt
= 1;
416 /* Get the length: */
417 omapi_connection_get_uint16 (c
, &link
-> imsg_len
);
418 link
-> imsg_count
= 0; /* Bytes read. */
420 /* Ensure the message is of valid length. */
421 if (link
->imsg_len
< DHCP_FAILOVER_MIN_MESSAGE_SIZE
||
422 link
->imsg_len
> DHCP_FAILOVER_MAX_MESSAGE_SIZE
) {
423 status
= ISC_R_UNEXPECTED
;
424 goto dhcp_flink_fail
;
427 if ((omapi_connection_require (c
, link
-> imsg_len
- 2U)) !=
430 case dhcp_flink_message_wait
:
431 /* Read in the message. At this point we have the
432 entire message in the input buffer. For each
433 incoming value ID, set a bit in the bitmask
434 indicating that we've gotten it. Maybe flag an
435 error message if the bit is already set. Once
436 we're done reading, we can check the bitmask to
437 make sure that the required fields for each message
438 have been included. */
440 link
-> imsg_count
+= 2; /* Count the length as read. */
442 /* Get message type. */
443 omapi_connection_copyout (&link
-> imsg
-> type
, c
, 1);
444 link
-> imsg_count
++;
446 /* Get message payload offset. */
447 omapi_connection_copyout (&link
-> imsg_payoff
, c
, 1);
448 link
-> imsg_count
++;
450 /* Get message time. */
451 omapi_connection_get_uint32 (c
, &link
-> imsg
-> time
);
452 link
-> imsg_count
+= 4;
454 /* Get transaction ID. */
455 omapi_connection_get_uint32 (c
, &link
-> imsg
-> xid
);
456 link
-> imsg_count
+= 4;
458 #if defined (DEBUG_FAILOVER_MESSAGES)
459 log_info ("link: message %s payoff %d time %ld xid %ld",
460 dhcp_failover_message_name (link
-> imsg
-> type
),
462 (unsigned long)link
-> imsg
-> time
,
463 (unsigned long)link
-> imsg
-> xid
);
465 /* Skip over any portions of the message header that we
467 if (link
-> imsg_payoff
- link
-> imsg_count
) {
468 omapi_connection_copyout ((unsigned char *)0, c
,
469 (link
-> imsg_payoff
-
470 link
-> imsg_count
));
471 link
-> imsg_count
= link
-> imsg_payoff
;
474 /* Now start sucking options off the wire. */
475 while (link
-> imsg_count
< link
-> imsg_len
) {
476 status
= do_a_failover_option (c
, link
);
477 if (status
!= ISC_R_SUCCESS
)
478 goto dhcp_flink_fail
;
481 /* If it's a connect message, try to associate it with
483 /* XXX this should be authenticated! */
484 if (link
-> imsg
-> type
== FTM_CONNECT
) {
488 if (!(link
->imsg
->options_present
&
489 FTB_RELATIONSHIP_NAME
)) {
490 errmsg
= "missing relationship-name";
491 reason
= FTR_INVALID_PARTNER
;
495 /* See if we can find a failover_state object that
496 matches this connection. This message should only
497 be received by a secondary from a primary. */
498 for (s
= failover_states
; s
; s
= s
-> next
) {
499 if (dhcp_failover_state_match_by_name(s
,
500 &link
->imsg
->relationship_name
))
504 /* If we can't find a failover protocol state
505 for this remote host, drop the connection */
507 errmsg
= "unknown server";
508 reason
= FTR_INVALID_PARTNER
;
511 /* XXX Send a refusal message first?
512 XXX Look in protocol spec for guidance. */
513 log_error ("Failover CONNECT from %s: %s",
514 state
? state
->name
: "unknown", errmsg
);
515 dhcp_failover_send_connectack
516 ((omapi_object_t
*)link
, state
,
518 log_info ("failover: disconnect: %s", errmsg
);
519 omapi_disconnect (c
, 0);
520 link
-> state
= dhcp_flink_disconnected
;
521 return ISC_R_SUCCESS
;
524 if ((cur_time
> link
-> imsg
-> time
&&
525 cur_time
- link
-> imsg
-> time
> 60) ||
526 (cur_time
< link
-> imsg
-> time
&&
527 link
-> imsg
-> time
- cur_time
> 60)) {
528 errmsg
= "time offset too large";
529 reason
= FTR_TIMEMISMATCH
;
533 if (!(link
-> imsg
-> options_present
& FTB_HBA
) ||
534 link
-> imsg
-> hba
.count
!= 32) {
535 errmsg
= "invalid HBA";
536 reason
= FTR_HBA_CONFLICT
; /* XXX */
540 dfree (state
-> hba
, MDL
);
541 state
-> hba
= dmalloc (32, MDL
);
543 errmsg
= "no memory";
544 reason
= FTR_MISC_REJECT
;
547 memcpy (state
-> hba
, link
-> imsg
-> hba
.data
, 32);
549 if (!link
-> state_object
)
550 dhcp_failover_state_reference
551 (&link
-> state_object
, state
, MDL
);
552 if (!link
-> peer_address
)
553 option_cache_reference
554 (&link
-> peer_address
,
555 state
-> partner
.address
, MDL
);
558 /* If we don't have a state object at this point, it's
559 some kind of bogus situation, so just drop the
561 if (!link
-> state_object
) {
562 log_info ("failover: connect: no matching state.");
563 omapi_disconnect (c
, 1);
564 link
-> state
= dhcp_flink_disconnected
;
565 return ISC_R_INVALIDARG
;
568 /* Once we have the entire message, and we've validated
569 it as best we can here, pass it to the parent. */
570 omapi_signal ((omapi_object_t
*)link
-> state_object
,
572 link
-> state
= dhcp_flink_message_length_wait
;
573 failover_message_dereference (&link
-> imsg
, MDL
);
574 /* XXX This is dangerous because we could get into a tight
575 XXX loop reading input without servicing any other stuff.
576 XXX There needs to be a way to relinquish control but
577 XXX get it back immediately if there's no other work to
579 if ((omapi_connection_require (c
, 2)) == ISC_R_SUCCESS
)
584 /* XXX should never get here. Assertion? */
587 return ISC_R_SUCCESS
;
590 static isc_result_t
do_a_failover_option (c
, link
)
592 dhcp_failover_link_t
*link
;
594 u_int16_t option_code
;
595 u_int16_t option_len
;
602 if (link
-> imsg_count
+ 2 > link
-> imsg_len
) {
603 log_error ("FAILOVER: message overflow at option code.");
604 return ISC_R_PROTOCOLERROR
;
607 /* Get option code. */
608 omapi_connection_get_uint16 (c
, &option_code
);
609 link
-> imsg_count
+= 2;
611 if (link
-> imsg_count
+ 2 > link
-> imsg_len
) {
612 log_error ("FAILOVER: message overflow at length.");
613 return ISC_R_PROTOCOLERROR
;
616 /* Get option length. */
617 omapi_connection_get_uint16 (c
, &option_len
);
618 link
-> imsg_count
+= 2;
620 if (link
-> imsg_count
+ option_len
> link
-> imsg_len
) {
621 log_error ("FAILOVER: message overflow at data.");
622 return ISC_R_PROTOCOLERROR
;
625 /* If it's an unknown code, skip over it. */
626 if (option_code
> FTO_MAX
) {
627 #if defined (DEBUG_FAILOVER_MESSAGES)
628 log_debug (" option code %d (%s) len %d (not recognized)",
630 dhcp_failover_option_name (option_code
),
633 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
634 link
-> imsg_count
+= option_len
;
635 return ISC_R_SUCCESS
;
638 /* If it's the digest, do it now. */
639 if (ft_options
[option_code
].type
== FT_DIGEST
) {
640 link
-> imsg_count
+= option_len
;
641 if (link
-> imsg_count
!= link
-> imsg_len
) {
642 log_error ("FAILOVER: digest not at end of message");
643 return ISC_R_PROTOCOLERROR
;
645 #if defined (DEBUG_FAILOVER_MESSAGES)
646 log_debug (" option %s len %d",
647 ft_options
[option_code
].name
, option_len
);
649 /* For now, just dump it. */
650 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
651 return ISC_R_SUCCESS
;
654 /* Only accept an option once. */
655 if (link
-> imsg
-> options_present
& ft_options
[option_code
].bit
) {
656 log_error ("FAILOVER: duplicate option %s",
657 ft_options
[option_code
].name
);
658 return ISC_R_PROTOCOLERROR
;
661 /* Make sure the option is appropriate for this type of message.
662 Really, any option is generally allowed for any message, and the
663 cases where this is not true are too complicated to represent in
664 this way - what this code is doing is to just avoid saving the
665 value of an option we don't have any way to use, which allows
666 us to make the failover_message structure smaller. */
667 if (ft_options
[option_code
].bit
&&
668 !(fto_allowed
[link
-> imsg
-> type
] &
669 ft_options
[option_code
].bit
)) {
670 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
671 link
-> imsg_count
+= option_len
;
672 return ISC_R_SUCCESS
;
675 /* Figure out how many elements, how big they are, and where
677 if (ft_options
[option_code
].num_present
) {
678 /* If this option takes a fixed number of elements,
679 we expect the space for them to be preallocated,
680 and we can just read the data in. */
682 op
= ((unsigned char *)link
-> imsg
) +
683 ft_options
[option_code
].offset
;
684 op_size
= ft_sizes
[ft_options
[option_code
].type
];
685 op_count
= ft_options
[option_code
].num_present
;
687 if (option_len
!= op_size
* op_count
) {
688 log_error ("FAILOVER: option size (%d:%d), option %s",
690 (ft_sizes
[ft_options
[option_code
].type
] *
691 ft_options
[option_code
].num_present
),
692 ft_options
[option_code
].name
);
693 return ISC_R_PROTOCOLERROR
;
696 failover_option_t
*fo
;
698 /* FT_DDNS* are special - one or two bytes of status
699 followed by the client FQDN. */
700 if (ft_options
[option_code
].type
== FT_DDNS1
||
701 ft_options
[option_code
].type
== FT_DDNS1
) {
704 (((char *)link
-> imsg
) +
705 ft_options
[option_code
].offset
));
707 op_count
= (ft_options
[option_code
].type
== FT_DDNS1
710 omapi_connection_copyout (&ddns
-> codes
[0],
712 link
-> imsg_count
+= op_count
;
714 ddns
-> codes
[1] = 0;
716 op_count
= option_len
- op_count
;
718 ddns
-> length
= op_count
;
719 ddns
-> data
= dmalloc (op_count
, MDL
);
721 log_error ("FAILOVER: no memory getting%s(%d)",
722 " DNS data ", op_count
);
724 /* Actually, NO_MEMORY, but if we lose here
725 we have to drop the connection. */
726 return ISC_R_PROTOCOLERROR
;
728 omapi_connection_copyout (ddns
-> data
, c
, op_count
);
732 /* A zero for num_present means that any number of
733 elements can appear, so we have to figure out how
734 many we got from the length of the option, and then
735 fill out a failover_option structure describing the
737 op_size
= ft_sizes
[ft_options
[option_code
].type
];
739 /* Make sure that option data length is a multiple of the
740 size of the data type being sent. */
741 if (op_size
> 1 && option_len
% op_size
) {
742 log_error ("FAILOVER: option_len %d not %s%d",
743 option_len
, "multiple of ", op_size
);
744 return ISC_R_PROTOCOLERROR
;
747 op_count
= option_len
/ op_size
;
749 fo
= ((failover_option_t
*)
750 (((char *)link
-> imsg
) +
751 ft_options
[option_code
].offset
));
753 fo
-> count
= op_count
;
754 fo
-> data
= dmalloc (option_len
, MDL
);
756 log_error ("FAILOVER: no memory getting %s (%d)",
757 "option data", op_count
);
759 return ISC_R_PROTOCOLERROR
;
764 /* For single-byte message values and multi-byte values that
765 don't need swapping, just read them in all at once. */
766 if (op_size
== 1 || ft_options
[option_code
].type
== FT_IPADDR
) {
767 omapi_connection_copyout ((unsigned char *)op
, c
, option_len
);
768 link
-> imsg_count
+= option_len
;
772 /* For values that require swapping, read them in one at a time
773 using routines that swap bytes. */
774 for (i
= 0; i
< op_count
; i
++) {
775 switch (ft_options
[option_code
].type
) {
777 omapi_connection_get_uint32 (c
, (u_int32_t
*)op
);
779 link
-> imsg_count
+= 4;
783 omapi_connection_get_uint16 (c
, (u_int16_t
*)op
);
785 link
-> imsg_count
+= 2;
789 /* Everything else should have been handled
791 log_error ("FAILOVER: option %s: bad type %d",
792 ft_options
[option_code
].name
,
793 ft_options
[option_code
].type
);
794 return ISC_R_PROTOCOLERROR
;
798 /* Remember that we got this option. */
799 link
-> imsg
-> options_present
|= ft_options
[option_code
].bit
;
800 return ISC_R_SUCCESS
;
803 isc_result_t
dhcp_failover_link_set_value (omapi_object_t
*h
,
805 omapi_data_string_t
*name
,
806 omapi_typed_data_t
*value
)
808 if (h
-> type
!= omapi_type_protocol
)
809 return ISC_R_INVALIDARG
;
811 /* Never valid to set these. */
812 if (!omapi_ds_strcmp (name
, "link-port") ||
813 !omapi_ds_strcmp (name
, "link-name") ||
814 !omapi_ds_strcmp (name
, "link-state"))
817 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
818 return (*(h
-> inner
-> type
-> set_value
))
819 (h
-> inner
, id
, name
, value
);
820 return ISC_R_NOTFOUND
;
823 isc_result_t
dhcp_failover_link_get_value (omapi_object_t
*h
,
825 omapi_data_string_t
*name
,
826 omapi_value_t
**value
)
828 dhcp_failover_link_t
*link
;
830 if (h
-> type
!= omapi_type_protocol
)
831 return ISC_R_INVALIDARG
;
832 link
= (dhcp_failover_link_t
*)h
;
834 if (!omapi_ds_strcmp (name
, "link-port")) {
835 return omapi_make_int_value (value
, name
,
836 (int)link
-> peer_port
, MDL
);
837 } else if (!omapi_ds_strcmp (name
, "link-state")) {
838 if (link
-> state
< 0 ||
839 link
-> state
>= dhcp_flink_state_max
)
840 return omapi_make_string_value (value
, name
,
841 "invalid link state",
843 return omapi_make_string_value
845 dhcp_flink_state_names
[link
-> state
], MDL
);
848 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
849 return (*(h
-> inner
-> type
-> get_value
))
850 (h
-> inner
, id
, name
, value
);
851 return ISC_R_NOTFOUND
;
854 isc_result_t
dhcp_failover_link_destroy (omapi_object_t
*h
,
855 const char *file
, int line
)
857 dhcp_failover_link_t
*link
;
858 if (h
-> type
!= dhcp_type_failover_link
)
859 return ISC_R_INVALIDARG
;
860 link
= (dhcp_failover_link_t
*)h
;
862 if (link
-> peer_address
)
863 option_cache_dereference (&link
-> peer_address
, file
, line
);
865 failover_message_dereference (&link
-> imsg
, file
, line
);
866 if (link
-> state_object
)
867 dhcp_failover_state_dereference (&link
-> state_object
,
869 return ISC_R_SUCCESS
;
872 /* Write all the published values associated with the object through the
873 specified connection. */
875 isc_result_t
dhcp_failover_link_stuff_values (omapi_object_t
*c
,
879 dhcp_failover_link_t
*link
;
882 if (l
-> type
!= dhcp_type_failover_link
)
883 return ISC_R_INVALIDARG
;
884 link
= (dhcp_failover_link_t
*)l
;
886 status
= omapi_connection_put_name (c
, "link-port");
887 if (status
!= ISC_R_SUCCESS
)
889 status
= omapi_connection_put_uint32 (c
, sizeof (int));
890 if (status
!= ISC_R_SUCCESS
)
892 status
= omapi_connection_put_uint32 (c
, link
-> peer_port
);
893 if (status
!= ISC_R_SUCCESS
)
896 status
= omapi_connection_put_name (c
, "link-state");
897 if (status
!= ISC_R_SUCCESS
)
899 if (link
-> state
< 0 ||
900 link
-> state
>= dhcp_flink_state_max
)
901 status
= omapi_connection_put_string (c
, "invalid link state");
903 status
= (omapi_connection_put_string
904 (c
, dhcp_flink_state_names
[link
-> state
]));
905 if (status
!= ISC_R_SUCCESS
)
908 if (link
-> inner
&& link
-> inner
-> type
-> stuff_values
)
909 return (*(link
-> inner
-> type
-> stuff_values
)) (c
, id
,
911 return ISC_R_SUCCESS
;
914 /* Set up a listener for the omapi protocol. The handle stored points to
915 a listener object, not a protocol object. */
917 isc_result_t
dhcp_failover_listen (omapi_object_t
*h
)
920 dhcp_failover_listener_t
*obj
, *l
;
921 omapi_value_t
*value
= (omapi_value_t
*)0;
922 omapi_addr_t local_addr
;
925 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
926 "local-port", &value
);
927 if (status
!= ISC_R_SUCCESS
)
929 if (!value
-> value
) {
930 omapi_value_dereference (&value
, MDL
);
931 return ISC_R_INVALIDARG
;
934 status
= omapi_get_int_value (&port
, value
-> value
);
935 omapi_value_dereference (&value
, MDL
);
936 if (status
!= ISC_R_SUCCESS
)
938 local_addr
.port
= port
;
940 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
941 "local-address", &value
);
942 if (status
!= ISC_R_SUCCESS
)
944 if (!value
-> value
) {
946 omapi_value_dereference (&value
, MDL
);
947 return ISC_R_INVALIDARG
;
950 if (value
-> value
-> type
!= omapi_datatype_data
||
951 value
-> value
-> u
.buffer
.len
!= sizeof (struct in_addr
))
954 memcpy (local_addr
.address
, value
-> value
-> u
.buffer
.value
,
955 value
-> value
-> u
.buffer
.len
);
956 local_addr
.addrlen
= value
-> value
-> u
.buffer
.len
;
957 local_addr
.addrtype
= AF_INET
;
959 omapi_value_dereference (&value
, MDL
);
961 /* Are we already listening on this port and address? */
962 for (l
= failover_listeners
; l
; l
= l
-> next
) {
963 if (l
-> address
.port
== local_addr
.port
&&
964 l
-> address
.addrtype
== local_addr
.addrtype
&&
965 l
-> address
.addrlen
== local_addr
.addrlen
&&
966 !memcmp (l
-> address
.address
, local_addr
.address
,
970 /* Already listening. */
972 return ISC_R_SUCCESS
;
974 obj
= (dhcp_failover_listener_t
*)0;
975 status
= dhcp_failover_listener_allocate (&obj
, MDL
);
976 if (status
!= ISC_R_SUCCESS
)
978 obj
-> address
= local_addr
;
980 status
= omapi_listen_addr ((omapi_object_t
*)obj
, &obj
-> address
, 1);
981 if (status
!= ISC_R_SUCCESS
)
984 status
= omapi_object_reference (&h
-> outer
,
985 (omapi_object_t
*)obj
, MDL
);
986 if (status
!= ISC_R_SUCCESS
) {
987 dhcp_failover_listener_dereference (&obj
, MDL
);
990 status
= omapi_object_reference (&obj
-> inner
, h
, MDL
);
991 if (status
!= ISC_R_SUCCESS
) {
992 dhcp_failover_listener_dereference (&obj
, MDL
);
996 /* Put this listener on the list. */
997 if (failover_listeners
) {
998 dhcp_failover_listener_reference (&obj
-> next
,
999 failover_listeners
, MDL
);
1000 dhcp_failover_listener_dereference (&failover_listeners
, MDL
);
1002 dhcp_failover_listener_reference (&failover_listeners
, obj
, MDL
);
1004 return dhcp_failover_listener_dereference (&obj
, MDL
);
1007 /* Signal handler for protocol listener - if we get a connect signal,
1008 create a new protocol connection, otherwise pass the signal down. */
1010 isc_result_t
dhcp_failover_listener_signal (omapi_object_t
*o
,
1011 const char *name
, va_list ap
)
1013 isc_result_t status
;
1014 omapi_connection_object_t
*c
;
1015 dhcp_failover_link_t
*obj
;
1016 dhcp_failover_listener_t
*p
;
1017 dhcp_failover_state_t
*s
, *state
= (dhcp_failover_state_t
*)0;
1019 if (!o
|| o
-> type
!= dhcp_type_failover_listener
)
1020 return ISC_R_INVALIDARG
;
1021 p
= (dhcp_failover_listener_t
*)o
;
1023 /* Not a signal we recognize? */
1024 if (strcmp (name
, "connect")) {
1025 if (p
-> inner
&& p
-> inner
-> type
-> signal_handler
)
1026 return (*(p
-> inner
-> type
-> signal_handler
))
1027 (p
-> inner
, name
, ap
);
1028 return ISC_R_NOTFOUND
;
1031 c
= va_arg (ap
, omapi_connection_object_t
*);
1032 if (!c
|| c
-> type
!= omapi_type_connection
)
1033 return ISC_R_INVALIDARG
;
1035 /* See if we can find a failover_state object that
1036 matches this connection. */
1037 for (s
= failover_states
; s
; s
= s
-> next
) {
1038 if (dhcp_failover_state_match
1039 (s
, (u_int8_t
*)&c
-> remote_addr
.sin_addr
,
1040 sizeof c
-> remote_addr
.sin_addr
)) {
1046 log_info ("failover: listener: no matching state");
1047 return omapi_disconnect ((omapi_object_t
*)c
, 1);
1050 obj
= (dhcp_failover_link_t
*)0;
1051 status
= dhcp_failover_link_allocate (&obj
, MDL
);
1052 if (status
!= ISC_R_SUCCESS
)
1054 obj
-> peer_port
= ntohs (c
-> remote_addr
.sin_port
);
1056 status
= omapi_object_reference (&obj
-> outer
,
1057 (omapi_object_t
*)c
, MDL
);
1058 if (status
!= ISC_R_SUCCESS
) {
1060 dhcp_failover_link_dereference (&obj
, MDL
);
1061 log_info ("failover: listener: picayune failure.");
1062 omapi_disconnect ((omapi_object_t
*)c
, 1);
1066 status
= omapi_object_reference (&c
-> inner
,
1067 (omapi_object_t
*)obj
, MDL
);
1068 if (status
!= ISC_R_SUCCESS
)
1071 status
= dhcp_failover_state_reference (&obj
-> state_object
,
1073 if (status
!= ISC_R_SUCCESS
)
1076 omapi_signal_in ((omapi_object_t
*)obj
, "connect");
1078 return dhcp_failover_link_dereference (&obj
, MDL
);
1081 isc_result_t
dhcp_failover_listener_set_value (omapi_object_t
*h
,
1083 omapi_data_string_t
*name
,
1084 omapi_typed_data_t
*value
)
1086 if (h
-> type
!= dhcp_type_failover_listener
)
1087 return ISC_R_INVALIDARG
;
1089 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
1090 return (*(h
-> inner
-> type
-> set_value
))
1091 (h
-> inner
, id
, name
, value
);
1092 return ISC_R_NOTFOUND
;
1095 isc_result_t
dhcp_failover_listener_get_value (omapi_object_t
*h
,
1097 omapi_data_string_t
*name
,
1098 omapi_value_t
**value
)
1100 if (h
-> type
!= dhcp_type_failover_listener
)
1101 return ISC_R_INVALIDARG
;
1103 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
1104 return (*(h
-> inner
-> type
-> get_value
))
1105 (h
-> inner
, id
, name
, value
);
1106 return ISC_R_NOTFOUND
;
1109 isc_result_t
dhcp_failover_listener_destroy (omapi_object_t
*h
,
1110 const char *file
, int line
)
1112 dhcp_failover_listener_t
*l
;
1114 if (h
-> type
!= dhcp_type_failover_listener
)
1115 return ISC_R_INVALIDARG
;
1116 l
= (dhcp_failover_listener_t
*)h
;
1118 dhcp_failover_listener_dereference (&l
-> next
, file
, line
);
1120 return ISC_R_SUCCESS
;
1123 /* Write all the published values associated with the object through the
1124 specified connection. */
1126 isc_result_t
dhcp_failover_listener_stuff (omapi_object_t
*c
,
1132 if (p
-> type
!= dhcp_type_failover_listener
)
1133 return ISC_R_INVALIDARG
;
1135 if (p
-> inner
&& p
-> inner
-> type
-> stuff_values
)
1136 return (*(p
-> inner
-> type
-> stuff_values
)) (c
, id
,
1138 return ISC_R_SUCCESS
;
1141 /* Set up master state machine for the failover protocol. */
1143 isc_result_t
dhcp_failover_register (omapi_object_t
*h
)
1145 isc_result_t status
;
1146 dhcp_failover_state_t
*obj
;
1148 omapi_value_t
*value
= (omapi_value_t
*)0;
1150 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
1151 "local-port", &value
);
1152 if (status
!= ISC_R_SUCCESS
)
1154 if (!value
-> value
) {
1155 omapi_value_dereference (&value
, MDL
);
1156 return ISC_R_INVALIDARG
;
1159 status
= omapi_get_int_value (&port
, value
-> value
);
1160 omapi_value_dereference (&value
, MDL
);
1161 if (status
!= ISC_R_SUCCESS
)
1164 obj
= (dhcp_failover_state_t
*)0;
1165 dhcp_failover_state_allocate (&obj
, MDL
);
1166 obj
-> me
.port
= port
;
1168 status
= omapi_listen ((omapi_object_t
*)obj
, port
, 1);
1169 if (status
!= ISC_R_SUCCESS
) {
1170 dhcp_failover_state_dereference (&obj
, MDL
);
1174 status
= omapi_object_reference (&h
-> outer
, (omapi_object_t
*)obj
,
1176 if (status
!= ISC_R_SUCCESS
) {
1177 dhcp_failover_state_dereference (&obj
, MDL
);
1180 status
= omapi_object_reference (&obj
-> inner
, h
, MDL
);
1181 dhcp_failover_state_dereference (&obj
, MDL
);
1185 /* Signal handler for protocol state machine. */
1187 isc_result_t
dhcp_failover_state_signal (omapi_object_t
*o
,
1188 const char *name
, va_list ap
)
1190 isc_result_t status
;
1191 omapi_connection_object_t
*c
;
1192 omapi_protocol_object_t
*obj
;
1193 dhcp_failover_state_t
*state
;
1194 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
) {
1280 cancel_timeout (dhcp_failover_link_startup_timeout
,
1283 if (!(link
->imsg
->options_present
&
1284 FTB_RELATIONSHIP_NAME
)) {
1285 errmsg
= "missing relationship-name";
1286 reason
= FTR_INVALID_PARTNER
;
1290 if (link
->imsg
->options_present
& FTB_REJECT_REASON
) {
1291 /* XXX: add message option to text output. */
1292 log_error ("Failover CONNECT to %s rejected: %s",
1293 state
? state
->name
: "unknown",
1294 (dhcp_failover_reject_reason_print
1295 (link
-> imsg
-> reject_reason
)));
1296 /* XXX print message from peer if peer sent message. */
1297 omapi_disconnect (link
-> outer
, 1);
1298 return ISC_R_SUCCESS
;
1301 if (!dhcp_failover_state_match_by_name(state
,
1302 &link
->imsg
->relationship_name
)) {
1303 errmsg
= "unknown server";
1304 reason
= FTR_INVALID_PARTNER
;
1306 log_error ("Failover CONNECTACK from %s: %s",
1307 state
? state
->name
: "unknown", errmsg
);
1308 dhcp_failover_send_disconnect ((omapi_object_t
*)link
,
1310 omapi_disconnect (link
-> outer
, 0);
1311 return ISC_R_SUCCESS
;
1314 if (state
-> link_to_peer
) {
1315 errmsg
= "already connected";
1316 reason
= FTR_DUP_CONNECTION
;
1320 if ((cur_time
> link
-> imsg
-> time
&&
1321 cur_time
- link
-> imsg
-> time
> 60) ||
1322 (cur_time
< link
-> imsg
-> time
&&
1323 link
-> imsg
-> time
- cur_time
> 60)) {
1324 errmsg
= "time offset too large";
1325 reason
= FTR_TIMEMISMATCH
;
1329 dhcp_failover_link_reference (&state
-> link_to_peer
,
1332 /* XXX This is probably the right thing to do, but
1333 XXX for release three, to make the smallest possible
1334 XXX change, we are doing this when the peer state
1335 XXX changes instead. */
1336 if (state
-> me
.state
== startup
)
1337 dhcp_failover_set_state (state
,
1338 state
-> saved_state
);
1341 dhcp_failover_send_state (state
);
1343 if (link
-> imsg
-> options_present
& FTB_MAX_UNACKED
)
1344 state
-> partner
.max_flying_updates
=
1345 link
-> imsg
-> max_unacked
;
1346 if (link
-> imsg
-> options_present
& FTB_RECEIVE_TIMER
)
1347 state
-> partner
.max_response_delay
=
1348 link
-> imsg
-> receive_timer
;
1349 #if defined (DEBUG_FAILOVER_TIMING)
1350 log_info ("add_timeout +%d %s",
1351 (int)state
-> partner
.max_response_delay
/ 3,
1352 "dhcp_failover_send_contact");
1354 add_timeout (cur_time
+
1355 (int)state
-> partner
.max_response_delay
/ 3,
1356 dhcp_failover_send_contact
, state
,
1357 (tvref_t
)dhcp_failover_state_reference
,
1358 (tvunref_t
)dhcp_failover_state_dereference
);
1359 #if defined (DEBUG_FAILOVER_TIMING)
1360 log_info ("add_timeout +%d %s",
1361 (int)state
-> me
.max_response_delay
,
1362 "dhcp_failover_timeout");
1364 add_timeout (cur_time
+
1365 (int)state
-> me
.max_response_delay
,
1366 dhcp_failover_timeout
, state
,
1367 (tvref_t
)dhcp_failover_state_reference
,
1368 (tvunref_t
)dhcp_failover_state_dereference
);
1369 } else if (link
-> imsg
-> type
== FTM_DISCONNECT
) {
1370 if (link
-> imsg
-> reject_reason
) {
1371 log_error ("Failover DISCONNECT from %s: %s",
1372 state
? state
->name
: "unknown",
1373 (dhcp_failover_reject_reason_print
1374 (link
-> imsg
-> reject_reason
)));
1376 omapi_disconnect (link
-> outer
, 1);
1377 } else if (link
-> imsg
-> type
== FTM_BNDUPD
) {
1378 dhcp_failover_process_bind_update (state
,
1380 } else if (link
-> imsg
-> type
== FTM_BNDACK
) {
1381 dhcp_failover_process_bind_ack (state
, link
-> imsg
);
1382 } else if (link
-> imsg
-> type
== FTM_UPDREQ
) {
1383 dhcp_failover_process_update_request (state
,
1385 } else if (link
-> imsg
-> type
== FTM_UPDREQALL
) {
1386 dhcp_failover_process_update_request_all
1387 (state
, link
-> imsg
);
1388 } else if (link
-> imsg
-> type
== FTM_UPDDONE
) {
1389 dhcp_failover_process_update_done (state
,
1391 } else if (link
-> imsg
-> type
== FTM_POOLREQ
) {
1392 dhcp_failover_pool_reqbalance(state
);
1393 } else if (link
-> imsg
-> type
== FTM_POOLRESP
) {
1394 log_info ("pool response: %ld leases",
1396 link
-> imsg
-> addresses_transferred
);
1397 } else if (link
-> imsg
-> type
== FTM_STATE
) {
1398 dhcp_failover_peer_state_changed (state
,
1402 /* Add a timeout so that if the partner doesn't send
1403 another message for the maximum transmit idle time
1404 plus a grace of one second, we close the
1406 if (state
-> link_to_peer
&&
1407 state
-> link_to_peer
== link
&&
1408 state
-> link_to_peer
-> state
!= dhcp_flink_disconnected
)
1410 #if defined (DEBUG_FAILOVER_TIMING)
1411 log_info ("add_timeout +%d %s",
1412 (int)state
-> me
.max_response_delay
,
1413 "dhcp_failover_timeout");
1415 add_timeout (cur_time
+
1416 (int)state
-> me
.max_response_delay
,
1417 dhcp_failover_timeout
, state
,
1418 (tvref_t
)dhcp_failover_state_reference
,
1419 (tvunref_t
)dhcp_failover_state_dereference
);
1424 /* Handle all the events we care about... */
1425 return ISC_R_SUCCESS
;
1428 isc_result_t
dhcp_failover_state_transition (dhcp_failover_state_t
*state
,
1431 isc_result_t status
;
1433 /* XXX Check these state transitions against the spec! */
1434 if (!strcmp (name
, "disconnect")) {
1435 if (state
-> link_to_peer
) {
1436 log_info ("peer %s: disconnected", state
-> name
);
1437 if (state
-> link_to_peer
-> state_object
)
1438 dhcp_failover_state_dereference
1439 (&state
-> link_to_peer
-> state_object
, MDL
);
1440 dhcp_failover_link_dereference (&state
-> link_to_peer
,
1443 cancel_timeout (dhcp_failover_send_contact
, state
);
1444 cancel_timeout (dhcp_failover_timeout
, state
);
1445 cancel_timeout (dhcp_failover_startup_timeout
, state
);
1447 switch (state
-> me
.state
== startup
?
1448 state
-> saved_state
: state
-> me
.state
) {
1449 /* In these situations, we remain in the current
1450 * state, or if in startup enter those states.
1452 case communications_interrupted
:
1459 case resolution_interrupted
:
1461 /* Already in the right state? */
1462 if (state
-> me
.state
== startup
)
1463 return (dhcp_failover_set_state
1464 (state
, state
-> saved_state
));
1465 return ISC_R_SUCCESS
;
1467 case potential_conflict
:
1468 return dhcp_failover_set_state
1469 (state
, resolution_interrupted
);
1472 return dhcp_failover_set_state
1473 (state
, communications_interrupted
);
1476 return dhcp_failover_set_state
1477 (state
, resolution_interrupted
);
1480 log_fatal("Impossible case at %s:%d.", MDL
);
1481 break; /* can't happen. */
1483 } else if (!strcmp (name
, "connect")) {
1484 switch (state
-> me
.state
) {
1485 case communications_interrupted
:
1486 status
= dhcp_failover_set_state (state
, normal
);
1487 dhcp_failover_send_updates (state
);
1490 case resolution_interrupted
:
1491 return dhcp_failover_set_state (state
,
1492 potential_conflict
);
1496 case potential_conflict
:
1505 return dhcp_failover_send_state (state
);
1508 log_fatal("Impossible case at %s:%d.", MDL
);
1511 } else if (!strcmp (name
, "startup")) {
1512 dhcp_failover_set_state (state
, startup
);
1513 return ISC_R_SUCCESS
;
1514 } else if (!strcmp (name
, "connect-timeout")) {
1515 switch (state
-> me
.state
) {
1516 case communications_interrupted
:
1518 case resolution_interrupted
:
1523 return ISC_R_SUCCESS
;
1530 return dhcp_failover_set_state
1531 (state
, communications_interrupted
);
1533 case potential_conflict
:
1534 return dhcp_failover_set_state
1535 (state
, resolution_interrupted
);
1538 log_fatal("Impossible case at %s:%d.", MDL
);
1542 return ISC_R_INVALIDARG
;
1545 isc_result_t
dhcp_failover_set_service_state (dhcp_failover_state_t
*state
)
1547 switch (state
-> me
.state
) {
1549 state
-> service_state
= not_responding
;
1550 state
-> nrr
= " (my state unknown)";
1554 state
-> service_state
= service_partner_down
;
1559 state
-> service_state
= cooperating
;
1563 case communications_interrupted
:
1564 state
-> service_state
= not_cooperating
;
1568 case resolution_interrupted
:
1569 case potential_conflict
:
1571 state
-> service_state
= not_responding
;
1572 state
-> nrr
= " (resolving conflicts)";
1576 state
-> service_state
= not_responding
;
1577 state
-> nrr
= " (recovering)";
1581 state
-> service_state
= not_responding
;
1582 state
-> nrr
= " (shut down)";
1586 state
-> service_state
= not_responding
;
1587 state
-> nrr
= " (paused)";
1591 state
-> service_state
= not_responding
;
1592 state
-> nrr
= " (recover wait)";
1596 state
-> service_state
= not_responding
;
1597 state
-> nrr
= " (recover done)";
1601 state
-> service_state
= service_startup
;
1602 state
-> nrr
= " (startup)";
1606 log_fatal("Impossible case at %s:%d.\n", MDL
);
1610 /* Some peer states can require us not to respond, even if our
1612 /* XXX hm. I suspect this isn't true anymore. */
1613 if (state
-> service_state
!= not_responding
) {
1614 switch (state
-> partner
.state
) {
1616 state
-> service_state
= not_responding
;
1617 state
-> nrr
= " (peer demands: recovering)";
1620 case potential_conflict
:
1622 case resolution_interrupted
:
1623 state
-> service_state
= not_responding
;
1624 state
-> nrr
= " (peer demands: resolving conflicts)";
1627 /* Other peer states don't affect our behaviour. */
1633 return ISC_R_SUCCESS
;
1636 isc_result_t
dhcp_failover_set_state (dhcp_failover_state_t
*state
,
1637 enum failover_state new_state
)
1639 enum failover_state saved_state
;
1642 struct shared_network
*s
;
1645 /* If we're in certain states where we're sending updates, and the peer
1646 * state changes, we need to re-schedule any pending updates just to
1647 * be on the safe side. This results in retransmission.
1649 switch (state
-> me
.state
) {
1651 case potential_conflict
:
1653 if (state
-> ack_queue_tail
) {
1656 /* Zap the flags. */
1657 for (lp
= state
-> ack_queue_head
; lp
; lp
= lp
-> next_pending
)
1658 lp
-> flags
= ((lp
-> flags
& ~ON_ACK_QUEUE
) |
1661 /* Now hook the ack queue to the beginning of the update
1663 if (state
-> update_queue_head
) {
1664 lease_reference (&state
-> ack_queue_tail
-> next_pending
,
1665 state
-> update_queue_head
, MDL
);
1666 lease_dereference (&state
-> update_queue_head
, MDL
);
1668 lease_reference (&state
-> update_queue_head
,
1669 state
-> ack_queue_head
, MDL
);
1670 if (!state
-> update_queue_tail
) {
1671 #if defined (POINTER_DEBUG)
1672 if (state
-> ack_queue_tail
-> next_pending
) {
1673 log_error ("next pending on ack queue tail.");
1677 lease_reference (&state
-> update_queue_tail
,
1678 state
-> ack_queue_tail
, MDL
);
1680 lease_dereference (&state
-> ack_queue_tail
, MDL
);
1681 lease_dereference (&state
-> ack_queue_head
, MDL
);
1682 state
-> cur_unacked_updates
= 0;
1684 /* We will re-queue a timeout later, if applicable. */
1685 cancel_timeout (dhcp_failover_keepalive
, state
);
1692 /* Tentatively make the transition. */
1693 saved_state
= state
-> me
.state
;
1694 saved_stos
= state
-> me
.stos
;
1696 /* Keep the old stos if we're going into recover_wait or if we're
1697 coming into or out of startup. */
1698 if (new_state
!= recover_wait
&& new_state
!= startup
&&
1699 saved_state
!= startup
)
1700 state
-> me
.stos
= cur_time
;
1702 /* If we're in shutdown, peer is in partner_down, and we're moving
1703 to recover, we can skip waiting for MCLT to expire. This happens
1704 when a server is moved administratively into shutdown prior to
1705 actually shutting down. Of course, if there are any updates
1706 pending we can't actually do this. */
1707 if (new_state
== recover
&& saved_state
== shut_down
&&
1708 state
-> partner
.state
== partner_down
&&
1709 !state
-> update_queue_head
&& !state
-> ack_queue_head
)
1710 state
-> me
.stos
= cur_time
- state
-> mclt
;
1712 state
-> me
.state
= new_state
;
1713 if (new_state
== startup
&& saved_state
!= startup
)
1714 state
-> saved_state
= saved_state
;
1716 /* If we can't record the new state, we can't make a state transition. */
1717 if (!write_failover_state (state
) || !commit_leases ()) {
1718 log_error ("Unable to record current failover state for %s",
1720 state
-> me
.state
= saved_state
;
1721 state
-> me
.stos
= saved_stos
;
1722 return ISC_R_IOERROR
;
1725 log_info ("failover peer %s: I move from %s to %s",
1726 state
-> name
, dhcp_failover_state_name_print (saved_state
),
1727 dhcp_failover_state_name_print (state
-> me
.state
));
1729 /* If we were in startup and we just left it, cancel the timeout. */
1730 if (new_state
!= startup
&& saved_state
== startup
)
1731 cancel_timeout (dhcp_failover_startup_timeout
, state
);
1733 /* Set our service state. */
1734 dhcp_failover_set_service_state (state
);
1736 /* Tell the peer about it. */
1737 if (state
-> link_to_peer
)
1738 dhcp_failover_send_state (state
);
1740 switch (new_state
) {
1742 /* Upon entering normal state, the server is expected to retransmit
1743 * all pending binding updates. This is a good opportunity to
1744 * rebalance the pool (potentially making new pending updates),
1745 * which also schedules the next pool rebalance.
1747 dhcp_failover_pool_balance(state
);
1748 dhcp_failover_generate_update_queue(state
, 0);
1750 if (state
->update_queue_tail
!= NULL
) {
1751 dhcp_failover_send_updates(state
);
1752 log_info("Sending updates to %s.", state
->name
);
1757 case potential_conflict
:
1758 if (state
-> i_am
== primary
)
1759 dhcp_failover_send_update_request (state
);
1763 #if defined (DEBUG_FAILOVER_TIMING)
1764 log_info ("add_timeout +15 %s",
1765 "dhcp_failover_startup_timeout");
1767 add_timeout (cur_time
+ 15,
1768 dhcp_failover_startup_timeout
,
1770 (tvref_t
)omapi_object_reference
,
1772 omapi_object_dereference
);
1775 /* If we come back in recover_wait and there's still waiting
1776 to do, set a timeout. */
1778 if (state
-> me
.stos
+ state
-> mclt
> cur_time
) {
1779 #if defined (DEBUG_FAILOVER_TIMING)
1780 log_info ("add_timeout +%d %s",
1782 state
-> me
.stos
+ state
-> mclt
),
1783 "dhcp_failover_startup_timeout");
1785 add_timeout ((int)(state
-> me
.stos
+ state
-> mclt
),
1786 dhcp_failover_recover_done
,
1788 (tvref_t
)omapi_object_reference
,
1790 omapi_object_dereference
);
1792 dhcp_failover_recover_done (state
);
1796 /* XXX: We're supposed to calculate if updreq or updreqall is
1797 * needed. In theory, we should only have to updreqall if we
1798 * are positive we lost our stable storage.
1800 if (state
-> link_to_peer
)
1801 dhcp_failover_send_update_request_all (state
);
1805 /* For every expired lease, set a timeout for it to become free. */
1806 for (s
= shared_networks
; s
; s
= s
-> next
) {
1807 for (p
= s
-> pools
; p
; p
= p
-> next
) {
1808 if (p
-> failover_peer
== state
) {
1809 for (l
= p
->expired
; l
; l
= l
->next
) {
1810 l
->tsfp
= state
->me
.stos
+ state
->mclt
;
1811 l
->sort_time
= (l
->tsfp
> l
->ends
) ?
1815 (p
->expired
->sort_time
< p
->next_event_time
)) {
1817 p
->next_event_time
= p
->expired
->sort_time
;
1818 #if defined (DEBUG_FAILOVER_TIMING)
1819 log_info ("add_timeout +%d %s",
1820 (int)(cur_time
- p
->next_event_time
),
1823 add_timeout(p
->next_event_time
, pool_timer
, p
,
1824 (tvref_t
)pool_reference
,
1825 (tvunref_t
)pool_dereference
);
1837 return ISC_R_SUCCESS
;
1840 isc_result_t
dhcp_failover_peer_state_changed (dhcp_failover_state_t
*state
,
1841 failover_message_t
*msg
)
1843 enum failover_state previous_state
= state
-> partner
.state
;
1844 enum failover_state new_state
;
1846 isc_result_t status
;
1848 new_state
= msg
-> server_state
;
1849 startupp
= (msg
-> server_flags
& FTF_SERVER_STARTUP
) ? 1 : 0;
1851 if (state
-> partner
.state
== new_state
&& state
-> me
.state
) {
1852 switch (state
-> me
.state
) {
1854 dhcp_failover_set_state (state
, state
-> saved_state
);
1855 return ISC_R_SUCCESS
;
1859 case potential_conflict
:
1864 return ISC_R_SUCCESS
;
1866 /* If we get a peer state change when we're
1867 disconnected, we always process it. */
1869 case communications_interrupted
:
1870 case resolution_interrupted
:
1876 log_fatal("Impossible case at %s:%d.", MDL
);
1881 state
-> partner
.state
= new_state
;
1883 log_info ("failover peer %s: peer moves from %s to %s",
1885 dhcp_failover_state_name_print (previous_state
),
1886 dhcp_failover_state_name_print (state
-> partner
.state
));
1888 if (!write_failover_state (state
) || !commit_leases ()) {
1889 /* This is bad, but it's not fatal. Of course, if we
1890 can't write to the lease database, we're not going to
1891 get much done anyway. */
1892 log_error ("Unable to record current failover state for %s",
1896 /* Quickly validate the new state as being one of the 13 known
1899 switch (new_state
) {
1903 case communications_interrupted
:
1905 case potential_conflict
:
1910 case resolution_interrupted
:
1916 log_error("failover peer %s: Invalid state: %d", state
->name
,
1918 dhcp_failover_set_state(state
, shut_down
);
1919 return ISC_R_SUCCESS
;
1922 /* Do any state transitions that are required as a result of the
1923 peer's state transition. */
1925 switch (state
-> me
.state
== startup
?
1926 state
-> saved_state
: state
-> me
.state
) {
1928 switch (new_state
) {
1930 dhcp_failover_state_pool_check (state
);
1934 if (state
-> me
.state
== startup
)
1935 dhcp_failover_set_state (state
, recover
);
1937 dhcp_failover_set_state (state
,
1938 potential_conflict
);
1941 case potential_conflict
:
1942 case resolution_interrupted
:
1944 /* None of these transitions should ever occur. */
1945 log_error("Peer %s: Invalid state transition %s "
1946 "to %s.", state
->name
,
1947 dhcp_failover_state_name_print(previous_state
),
1948 dhcp_failover_state_name_print(new_state
));
1949 dhcp_failover_set_state (state
, shut_down
);
1954 dhcp_failover_set_state (state
, partner_down
);
1958 dhcp_failover_set_state (state
,
1959 communications_interrupted
);
1963 /* recover_wait, recover_done, unknown_state, startup,
1964 * communications_interrupted
1971 switch (new_state
) {
1973 log_info ("failover peer %s: requesting %s",
1974 state
-> name
, "full update from peer");
1975 /* Don't send updreqall if we're really in the
1976 startup state, because that will result in two
1978 if (state
-> me
.state
== recover
)
1979 dhcp_failover_send_update_request_all (state
);
1982 case potential_conflict
:
1983 case resolution_interrupted
:
1986 dhcp_failover_set_state (state
, potential_conflict
);
1990 case communications_interrupted
:
1991 /* We're supposed to send an update request at this
1993 /* XXX we don't currently have code here to do any
1994 XXX clever detection of when we should send an
1995 XXX UPDREQALL message rather than an UPDREQ
1996 XXX message. What to do, what to do? */
1997 /* Currently when we enter recover state, no matter
1998 * the reason, we send an UPDREQALL. So, it makes
1999 * the most sense to stick to that until something
2001 * Furthermore, we only want to send the update
2002 * request if we are not in startup state.
2004 if (state
-> me
.state
== recover
)
2005 dhcp_failover_send_update_request_all (state
);
2009 /* XXX We're not explicitly told what to do in this
2010 XXX case, but this transition is consistent with
2011 XXX what is elsewhere in the draft. */
2012 dhcp_failover_set_state (state
, partner_down
);
2015 /* We can't really do anything in this case. */
2017 /* paused, recover_done, recover_wait, unknown_state,
2024 case potential_conflict
:
2025 switch (new_state
) {
2027 /* This is an illegal transition. */
2028 log_error("Peer %s moves to normal during conflict "
2029 "resolution - panic, shutting down.",
2031 dhcp_failover_set_state(state
, shut_down
);
2035 if (previous_state
== potential_conflict
)
2036 dhcp_failover_send_update_request (state
);
2038 log_error("Peer %s: Unexpected move to "
2039 "conflict-done.", state
->name
);
2044 case potential_conflict
:
2046 case communications_interrupted
:
2047 case resolution_interrupted
:
2052 dhcp_failover_set_state (state
, recover
);
2056 dhcp_failover_set_state (state
, partner_down
);
2060 /* unknown_state, startup */
2066 switch (new_state
) {
2069 dhcp_failover_set_state(state
, new_state
);
2073 log_fatal("Peer %s: Invalid attempt to move from %s "
2074 "to %s while local state is conflict-done.",
2076 dhcp_failover_state_name_print(previous_state
),
2077 dhcp_failover_state_name_print(new_state
));
2082 /* Take no action if other server is starting up. */
2086 switch (new_state
) {
2087 /* This is where we should be. */
2093 dhcp_failover_set_state (state
, normal
);
2097 case potential_conflict
:
2099 case communications_interrupted
:
2100 case resolution_interrupted
:
2102 dhcp_failover_set_state (state
, potential_conflict
);
2106 /* shut_down, paused, unknown_state, startup */
2111 case communications_interrupted
:
2112 switch (new_state
) {
2114 /* Stick with the status quo. */
2117 /* If we're in communications-interrupted and an
2118 amnesiac peer connects, go to the partner_down
2119 state immediately. */
2121 dhcp_failover_set_state (state
, partner_down
);
2125 case communications_interrupted
:
2128 /* XXX so we don't need to do this specially in
2129 XXX the CONNECT and CONNECTACK handlers. */
2130 dhcp_failover_send_updates (state
);
2131 dhcp_failover_set_state (state
, normal
);
2134 case potential_conflict
:
2136 case resolution_interrupted
:
2138 dhcp_failover_set_state (state
, potential_conflict
);
2142 dhcp_failover_set_state (state
, partner_down
);
2146 /* unknown_state, startup */
2151 case resolution_interrupted
:
2152 switch (new_state
) {
2155 case potential_conflict
:
2157 case communications_interrupted
:
2158 case resolution_interrupted
:
2162 dhcp_failover_set_state (state
, potential_conflict
);
2166 dhcp_failover_set_state (state
, partner_down
);
2170 /* paused, unknown_state, startup */
2175 /* Make no transitions while in recover_wait...just wait. */
2180 switch (new_state
) {
2182 log_error("Both servers have entered recover-done!");
2184 dhcp_failover_set_state (state
, normal
);
2188 dhcp_failover_set_state (state
, partner_down
);
2192 /* potential_conflict, partner_down,
2193 * communications_interrupted, resolution_interrupted,
2194 * paused, recover, recover_wait, unknown_state,
2201 /* We are essentially dead in the water when we're in
2202 either shut_down or paused states, and do not do any
2203 automatic state transitions. */
2208 /* XXX: Shouldn't this be a fatal condition? */
2213 log_fatal("Impossible condition at %s:%d.", MDL
);
2218 /* If we didn't make a transition out of startup as a result of
2219 the peer's state change, do it now as a result of the fact that
2220 we got a state change from the peer. */
2221 if (state
-> me
.state
== startup
&& state
-> saved_state
!= startup
)
2222 dhcp_failover_set_state (state
, state
-> saved_state
);
2224 /* For now, just set the service state based on the peer's state
2226 dhcp_failover_set_service_state (state
);
2228 return ISC_R_SUCCESS
;
2231 /* Balance operation manual entry. */
2233 dhcp_failover_pool_balance(dhcp_failover_state_t
*state
)
2235 /* Cancel pending event. */
2236 cancel_timeout(dhcp_failover_pool_rebalance
, state
);
2237 state
->sched_balance
= 0;
2239 dhcp_failover_pool_dobalance(state
);
2242 /* Balance operation entry from timer event. */
2244 dhcp_failover_pool_rebalance(void *failover_state
)
2246 dhcp_failover_state_t
*state
;
2248 state
= (dhcp_failover_state_t
*)failover_state
;
2250 /* Clear scheduled event indicator. */
2251 state
->sched_balance
= 0;
2253 if (dhcp_failover_pool_dobalance(state
))
2254 dhcp_failover_send_updates(state
);
2257 /* Balance operation entry from POOLREQ protocol message. */
2259 dhcp_failover_pool_reqbalance(dhcp_failover_state_t
*state
)
2263 /* Cancel pending event. */
2264 cancel_timeout(dhcp_failover_pool_rebalance
, state
);
2265 state
->sched_balance
= 0;
2267 queued
= dhcp_failover_pool_dobalance(state
);
2269 dhcp_failover_send_poolresp(state
, queued
);
2272 dhcp_failover_send_updates(state
);
2274 log_info("peer %s: Got POOLREQ, answering negatively! "
2275 "Peer may be out of leases or database inconsistent.",
2279 /* Do the meat of the work common to all forms of pool rebalance. */
2280 static int dhcp_failover_pool_dobalance(dhcp_failover_state_t
*state
)
2282 int lts
, total
, thresh
, hold
, pass
;
2283 int leases_queued
= 0;
2285 struct lease
*lp
= (struct lease
*)0;
2286 struct lease
*next
= (struct lease
*)0;
2287 struct shared_network
*s
;
2290 binding_state_t peer_lease_state
;
2291 binding_state_t my_lease_state
;
2293 int (*log_func
)(const char *, ...);
2296 if (state
-> me
.state
!= normal
)
2299 state
->last_balance
= cur_time
;
2301 for (s
= shared_networks
; s
; s
= s
->next
) {
2302 for (p
= s
->pools
; p
; p
= p
->next
) {
2303 if (p
->failover_peer
!= state
)
2306 /* Right now we're giving the peer half of the free leases.
2307 If we have more leases than the peer (i.e., more than
2308 half), then the number of leases we have, less the number
2309 of leases the peer has, will be how many more leases we
2310 have than the peer has. So if we send half that number
2311 to the peer, we should be even. */
2312 if (p
->failover_peer
->i_am
== primary
) {
2313 lts
= (p
->free_leases
- p
->backup_leases
) / 2;
2314 peer_lease_state
= FTS_BACKUP
;
2315 my_lease_state
= FTS_FREE
;
2318 lts
= (p
->backup_leases
- p
->free_leases
) / 2;
2319 peer_lease_state
= FTS_FREE
;
2320 my_lease_state
= FTS_BACKUP
;
2324 total
= p
->backup_leases
+ p
->free_leases
;
2326 thresh
= ((total
* state
->max_lease_misbalance
) + 50) / 100;
2327 hold
= ((total
* state
->max_lease_ownership
) + 50) / 100;
2329 log_info("balancing pool %lx %s total %d free %d "
2330 "backup %d lts %d max-own (+/-)%d",
2332 (p
->shared_network
?
2333 p
->shared_network
->name
: ""), p
->lease_count
,
2334 p
->free_leases
, p
->backup_leases
, lts
, hold
);
2336 /* If lts is in the negatives (we need leases) more than
2337 * negative double the thresh%, panic and send poolreq to
2338 * hopefully wake up the peer.
2340 if (!reqsent
&& (lts
< (thresh
* -2))) {
2341 dhcp_failover_send_poolreq(state
);
2345 /* In the first pass, try to allocate leases to the
2346 * peer which it would normally be responsible for (if
2347 * the lease has a hardware address or client-identifier,
2348 * and the load-balance-algorithm chooses the peer to
2349 * answer that address), up to a hold% excess in the peer's
2350 * favor. In the second pass, just send the oldest (first
2351 * on the list) leases up to a hold% excess in our favor.
2353 * This could make for additional pool rebalance
2354 * events, but preserving MAC possession should be
2358 lease_reference(&lp
, *lq
, MDL
);
2360 /* In the case where there are 2 leases, hold is zero, and
2361 * lts is 1 if both leases are on the local server. If
2362 * there is only 1 lease, both lts and hold are zero. Let's
2363 * not play ping pong.
2365 while (lp
&& (lts
> (pass
? hold
: -hold
))) {
2367 lease_dereference(&next
, MDL
);
2369 lease_reference(&next
, lp
->next
, MDL
);
2371 if (pass
|| peer_wants_lease(lp
)) {
2374 lp
->next_binding_state
= peer_lease_state
;
2375 lp
->tstp
= cur_time
;
2376 lp
->starts
= cur_time
;
2378 if (!supersede_lease(lp
, NULL
, 0, 1, 0) ||
2380 log_error("can't commit lease %s on "
2381 "giveaway", piaddr(lp
->ip_addr
));
2384 lease_dereference(&lp
, MDL
);
2386 lease_reference(&lp
, next
, MDL
);
2389 lease_reference(&lp
, *lq
, MDL
);
2394 lease_dereference(&next
, MDL
);
2396 lease_dereference(&lp
, MDL
);
2399 result
= "IMBALANCED";
2400 log_func
= log_error
;
2402 result
= "balanced";
2403 log_func
= log_info
;
2406 log_func("%s pool %lx %s total %d free %d backup %d "
2407 "lts %d max-misbal %d", result
, (unsigned long)p
,
2408 (p
->shared_network
?
2409 p
->shared_network
->name
: ""), p
->lease_count
,
2410 p
->free_leases
, p
->backup_leases
, lts
, thresh
);
2412 /* Recalculate next rebalance event timer. */
2413 dhcp_failover_pool_check(p
);
2420 return leases_queued
;
2423 /* dhcp_failover_pool_check: Called whenever FREE or BACKUP leases change
2424 * states, on both servers. Check the scheduled time to rebalance the pool
2425 * and lower it if applicable.
2428 dhcp_failover_pool_check(struct pool
*pool
)
2430 dhcp_failover_state_t
*peer
;
2433 peer
= pool
->failover_peer
;
2435 if(!peer
|| peer
->me
.state
!= normal
)
2438 /* Estimate the time left until lease exhaustion.
2439 * The first lease on the backup or free lists is also the oldest
2440 * lease. It is reasonable to guess that it will take at least
2441 * as much time for a pool to run out of leases, as the present
2442 * age of the oldest lease (seconds since it expired).
2444 * Note that this isn't so sane of an assumption if the oldest
2445 * lease is a virgin (ends = 0), we wind up sending this against
2446 * the max_balance bounds check.
2448 if(pool
->free
&& pool
->free
->ends
< cur_time
)
2449 est1
= cur_time
- pool
->free
->ends
;
2453 if(pool
->backup
&& pool
->backup
->ends
< cur_time
)
2454 est2
= cur_time
- pool
->backup
->ends
;
2458 /* We don't want to schedule rebalance for when we think we'll run
2459 * out of leases, we want to schedule the rebalance for when we think
2460 * the disparity will be 'large enough' to warrant action.
2462 est1
= ((est1
* peer
->max_lease_misbalance
) + 50) / 100;
2463 est2
= ((est2
* peer
->max_lease_misbalance
) + 50) / 100;
2465 /* Guess when the local system will begin issuing POOLREQ panic
2466 * attacks because "max_lease_misbalance*2" has been exceeded.
2468 if(peer
->i_am
== primary
)
2473 /* Select the smallest time. */
2477 /* Bounded by the maximum configured value. */
2478 if(est1
> peer
->max_balance
)
2479 est1
= peer
->max_balance
;
2481 /* Project this time into the future. */
2484 /* Do not move the time down under the minimum. */
2485 est2
= peer
->last_balance
+ peer
->min_balance
;
2486 if(peer
->last_balance
&& (est1
< est2
))
2489 /* Introduce a random delay. */
2490 est1
+= random() % 5;
2492 /* Do not move the time forward, or reset to the same time. */
2493 if(peer
->sched_balance
) {
2494 if (est1
>= peer
->sched_balance
)
2497 /* We are about to schedule the time down, cancel the
2500 cancel_timeout(dhcp_failover_pool_rebalance
, peer
);
2503 /* The time is different, and lower, use it. */
2504 peer
->sched_balance
= est1
;
2506 #if defined(DEBUG_FAILOVER_TIMING)
2507 log_info("add_timeout +%d dhcp_failover_pool_rebalance",
2510 add_timeout(est1
, dhcp_failover_pool_rebalance
, peer
,
2511 (tvref_t
)dhcp_failover_state_reference
,
2512 (tvunref_t
)dhcp_failover_state_dereference
);
2515 int dhcp_failover_state_pool_check (dhcp_failover_state_t
*state
)
2518 struct shared_network
*s
;
2521 for (s
= shared_networks
; s
; s
= s
-> next
) {
2522 for (p
= s
-> pools
; p
; p
= p
-> next
) {
2523 if (p
-> failover_peer
!= state
)
2525 dhcp_failover_pool_check (p
);
2531 isc_result_t
dhcp_failover_send_updates (dhcp_failover_state_t
*state
)
2533 struct lease
*lp
= (struct lease
*)0;
2534 isc_result_t status
;
2536 /* Can't update peer if we're not talking to it! */
2537 if (!state
-> link_to_peer
)
2538 return ISC_R_SUCCESS
;
2540 /* If there are acks pending, transmit them prior to potentialy
2541 * sending new updates for the same lease.
2543 if (state
->toack_queue_head
!= NULL
)
2544 dhcp_failover_send_acks(state
);
2546 while ((state
-> partner
.max_flying_updates
>
2547 state
-> cur_unacked_updates
) && state
-> update_queue_head
) {
2548 /* Grab the head of the update queue. */
2549 lease_reference (&lp
, state
-> update_queue_head
, MDL
);
2551 /* Send the update to the peer. */
2552 status
= dhcp_failover_send_bind_update (state
, lp
);
2553 if (status
!= ISC_R_SUCCESS
) {
2554 lease_dereference (&lp
, MDL
);
2557 lp
-> flags
&= ~ON_UPDATE_QUEUE
;
2559 /* Take it off the head of the update queue and put the next
2560 item in the update queue at the head. */
2561 lease_dereference (&state
-> update_queue_head
, MDL
);
2562 if (lp
-> next_pending
) {
2563 lease_reference (&state
-> update_queue_head
,
2564 lp
-> next_pending
, MDL
);
2565 lease_dereference (&lp
-> next_pending
, MDL
);
2567 lease_dereference (&state
-> update_queue_tail
, MDL
);
2570 if (state
-> ack_queue_head
) {
2572 (&state
-> ack_queue_tail
-> next_pending
,
2574 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2576 lease_reference (&state
-> ack_queue_head
, lp
, MDL
);
2578 #if defined (POINTER_DEBUG)
2579 if (lp
-> next_pending
) {
2580 log_error ("ack_queue_tail: lp -> next_pending");
2584 lease_reference (&state
-> ack_queue_tail
, lp
, MDL
);
2585 lp
-> flags
|= ON_ACK_QUEUE
;
2586 lease_dereference (&lp
, MDL
);
2588 /* Count the object as an unacked update. */
2589 state
-> cur_unacked_updates
++;
2591 return ISC_R_SUCCESS
;
2594 /* Queue an update for a lease. Always returns 1 at this point - it's
2595 not an error for this to be called on a lease for which there's no
2598 int dhcp_failover_queue_update (struct lease
*lease
, int immediate
)
2600 dhcp_failover_state_t
*state
;
2602 if (!lease
-> pool
||
2603 !lease
-> pool
-> failover_peer
)
2606 /* If it's already on the update queue, leave it there. */
2607 if (lease
-> flags
& ON_UPDATE_QUEUE
)
2610 /* Get the failover state structure for this lease. */
2611 state
= lease
-> pool
-> failover_peer
;
2613 /* If it's on the ack queue, take it off. */
2614 if (lease
-> flags
& ON_ACK_QUEUE
)
2615 dhcp_failover_ack_queue_remove (state
, lease
);
2617 if (state
-> update_queue_head
) {
2618 lease_reference (&state
-> update_queue_tail
-> next_pending
,
2620 lease_dereference (&state
-> update_queue_tail
, MDL
);
2622 lease_reference (&state
-> update_queue_head
, lease
, MDL
);
2624 #if defined (POINTER_DEBUG)
2625 if (lease
-> next_pending
) {
2626 log_error ("next pending on update queue lease.");
2627 #if defined (DEBUG_RC_HISTORY)
2628 dump_rc_history (lease
);
2633 lease_reference (&state
-> update_queue_tail
, lease
, MDL
);
2634 lease
-> flags
|= ON_UPDATE_QUEUE
;
2636 dhcp_failover_send_updates (state
);
2640 int dhcp_failover_send_acks (dhcp_failover_state_t
*state
)
2642 failover_message_t
*msg
= (failover_message_t
*)0;
2644 /* Must commit all leases prior to acking them. */
2645 if (!commit_leases ())
2648 while (state
-> toack_queue_head
) {
2649 failover_message_reference
2650 (&msg
, state
-> toack_queue_head
, MDL
);
2651 failover_message_dereference
2652 (&state
-> toack_queue_head
, MDL
);
2654 failover_message_reference
2655 (&state
-> toack_queue_head
, msg
-> next
, MDL
);
2658 dhcp_failover_send_bind_ack (state
, msg
, 0, (const char *)0);
2660 failover_message_dereference (&msg
, MDL
);
2663 if (state
-> toack_queue_tail
)
2664 failover_message_dereference (&state
-> toack_queue_tail
, MDL
);
2665 state
-> pending_acks
= 0;
2670 void dhcp_failover_toack_queue_timeout (void *vs
)
2672 dhcp_failover_state_t
*state
= vs
;
2674 #if defined (DEBUG_FAILOVER_TIMING)
2675 log_info ("dhcp_failover_toack_queue_timeout");
2678 dhcp_failover_send_acks (state
);
2681 /* Queue an ack for a message. There is currently no way to queue a
2682 negative ack -- these need to be sent directly. */
2684 int dhcp_failover_queue_ack (dhcp_failover_state_t
*state
,
2685 failover_message_t
*msg
)
2687 if (state
-> toack_queue_head
) {
2688 failover_message_reference
2689 (&state
-> toack_queue_tail
-> next
, msg
, MDL
);
2690 failover_message_dereference (&state
-> toack_queue_tail
, MDL
);
2692 failover_message_reference (&state
-> toack_queue_head
,
2695 failover_message_reference (&state
-> toack_queue_tail
, msg
, MDL
);
2697 state
-> pending_acks
++;
2699 /* Flush the toack queue whenever we exceed half the number of
2700 allowed unacked updates. */
2701 if (state
-> pending_acks
>= state
-> partner
.max_flying_updates
/ 2) {
2702 dhcp_failover_send_acks (state
);
2705 /* Schedule a timeout to flush the ack queue. */
2706 if (state
-> pending_acks
> 0) {
2707 #if defined (DEBUG_FAILOVER_TIMING)
2708 log_info ("add_timeout +2 %s",
2709 "dhcp_failover_toack_queue_timeout");
2711 add_timeout (cur_time
+ 2,
2712 dhcp_failover_toack_queue_timeout
, state
,
2713 (tvref_t
)dhcp_failover_state_reference
,
2714 (tvunref_t
)dhcp_failover_state_dereference
);
2720 void dhcp_failover_ack_queue_remove (dhcp_failover_state_t
*state
,
2721 struct lease
*lease
)
2725 if (!(lease
-> flags
& ON_ACK_QUEUE
))
2728 if (state
-> ack_queue_head
== lease
) {
2729 lease_dereference (&state
-> ack_queue_head
, MDL
);
2730 if (lease
-> next_pending
) {
2731 lease_reference (&state
-> ack_queue_head
,
2732 lease
-> next_pending
, MDL
);
2733 lease_dereference (&lease
-> next_pending
, MDL
);
2735 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2738 for (lp
= state
-> ack_queue_head
;
2739 lp
&& lp
-> next_pending
!= lease
;
2740 lp
= lp
-> next_pending
)
2746 lease_dereference (&lp
-> next_pending
, MDL
);
2747 if (lease
-> next_pending
) {
2748 lease_reference (&lp
-> next_pending
,
2749 lease
-> next_pending
, MDL
);
2750 lease_dereference (&lease
-> next_pending
, MDL
);
2752 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2753 if (lp
-> next_pending
) {
2754 log_error ("state -> ack_queue_tail");
2757 lease_reference (&state
-> ack_queue_tail
, lp
, MDL
);
2761 lease
-> flags
&= ~ON_ACK_QUEUE
;
2762 /* Multiple acks on one XID is an error and may cause badness. */
2763 lease
->last_xid
= 0;
2764 /* XXX: this violates draft-failover. We can't send another
2765 * update just because we forgot about an old one that hasn't
2768 state
-> cur_unacked_updates
--;
2771 * When updating leases as a result of an ack, we defer the commit
2772 * for performance reasons. When there are no more acks pending,
2775 if (state
-> cur_unacked_updates
== 0) {
2780 isc_result_t
dhcp_failover_state_set_value (omapi_object_t
*h
,
2782 omapi_data_string_t
*name
,
2783 omapi_typed_data_t
*value
)
2785 isc_result_t status
;
2787 if (h
-> type
!= dhcp_type_failover_state
)
2788 return ISC_R_INVALIDARG
;
2790 /* This list of successful returns is completely wrong, but the
2791 fastest way to make dhcpctl do something vaguely sane when
2792 you try to change the local state. */
2794 if (!omapi_ds_strcmp (name
, "name")) {
2795 return ISC_R_SUCCESS
;
2796 } else if (!omapi_ds_strcmp (name
, "partner-address")) {
2797 return ISC_R_SUCCESS
;
2798 } else if (!omapi_ds_strcmp (name
, "local-address")) {
2799 return ISC_R_SUCCESS
;
2800 } else if (!omapi_ds_strcmp (name
, "partner-port")) {
2801 return ISC_R_SUCCESS
;
2802 } else if (!omapi_ds_strcmp (name
, "local-port")) {
2803 return ISC_R_SUCCESS
;
2804 } else if (!omapi_ds_strcmp (name
, "max-outstanding-updates")) {
2805 return ISC_R_SUCCESS
;
2806 } else if (!omapi_ds_strcmp (name
, "mclt")) {
2807 return ISC_R_SUCCESS
;
2808 } else if (!omapi_ds_strcmp (name
, "load-balance-max-secs")) {
2809 return ISC_R_SUCCESS
;
2810 } else if (!omapi_ds_strcmp (name
, "load-balance-hba")) {
2811 return ISC_R_SUCCESS
;
2812 } else if (!omapi_ds_strcmp (name
, "partner-state")) {
2813 return ISC_R_SUCCESS
;
2814 } else if (!omapi_ds_strcmp (name
, "local-state")) {
2816 status
= omapi_get_int_value (&l
, value
);
2817 if (status
!= ISC_R_SUCCESS
)
2819 return dhcp_failover_set_state ((dhcp_failover_state_t
*)h
, l
);
2820 } else if (!omapi_ds_strcmp (name
, "partner-stos")) {
2821 return ISC_R_SUCCESS
;
2822 } else if (!omapi_ds_strcmp (name
, "local-stos")) {
2823 return ISC_R_SUCCESS
;
2824 } else if (!omapi_ds_strcmp (name
, "hierarchy")) {
2825 return ISC_R_SUCCESS
;
2826 } else if (!omapi_ds_strcmp (name
, "last-packet-sent")) {
2827 return ISC_R_SUCCESS
;
2828 } else if (!omapi_ds_strcmp (name
, "last-timestamp-received")) {
2829 return ISC_R_SUCCESS
;
2830 } else if (!omapi_ds_strcmp (name
, "skew")) {
2831 return ISC_R_SUCCESS
;
2832 } else if (!omapi_ds_strcmp (name
, "max-response-delay")) {
2833 return ISC_R_SUCCESS
;
2834 } else if (!omapi_ds_strcmp (name
, "cur-unacked-updates")) {
2835 return ISC_R_SUCCESS
;
2838 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
2839 return (*(h
-> inner
-> type
-> set_value
))
2840 (h
-> inner
, id
, name
, value
);
2841 return ISC_R_NOTFOUND
;
2844 void dhcp_failover_keepalive (void *vs
)
2846 dhcp_failover_state_t
*state
= vs
;
2849 void dhcp_failover_reconnect (void *vs
)
2851 dhcp_failover_state_t
*state
= vs
;
2852 isc_result_t status
;
2854 #if defined (DEBUG_FAILOVER_TIMING)
2855 log_info ("dhcp_failover_reconnect");
2857 /* If we already connected the other way, let the connection
2858 recovery code initiate any retry that may be required. */
2859 if (state
-> link_to_peer
)
2862 status
= dhcp_failover_link_initiate ((omapi_object_t
*)state
);
2863 if (status
!= ISC_R_SUCCESS
&& status
!= ISC_R_INCOMPLETE
) {
2864 log_info ("failover peer %s: %s", state
-> name
,
2865 isc_result_totext (status
));
2866 #if defined (DEBUG_FAILOVER_TIMING)
2867 log_info ("add_timeout +90 %s",
2868 "dhcp_failover_listener_restart");
2870 add_timeout (cur_time
+ 90,
2871 dhcp_failover_listener_restart
, state
,
2872 (tvref_t
)dhcp_failover_state_reference
,
2873 (tvunref_t
)dhcp_failover_state_dereference
);
2877 void dhcp_failover_startup_timeout (void *vs
)
2879 dhcp_failover_state_t
*state
= vs
;
2880 isc_result_t status
;
2882 #if defined (DEBUG_FAILOVER_TIMING)
2883 log_info ("dhcp_failover_startup_timeout");
2886 dhcp_failover_state_transition (state
, "disconnect");
2889 void dhcp_failover_link_startup_timeout (void *vl
)
2891 dhcp_failover_link_t
*link
= vl
;
2892 isc_result_t status
;
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 option_cache
*oc
;
3372 struct data_string ds
;
3375 memset (&ds
, 0, sizeof ds
);
3376 if (evaluate_option_cache (&ds
, (struct packet
*)0,
3378 (struct client_state
*)0,
3379 (struct option_state
*)0,
3380 (struct option_state
*)0,
3382 state
-> partner
.address
, MDL
)) {
3383 for (i
= 0; i
+ addrlen
- 1 < ds
.len
; i
+= addrlen
) {
3384 if (!memcmp (&ds
.data
[i
],
3386 data_string_forget (&ds
, MDL
);
3390 data_string_forget (&ds
, MDL
);
3396 dhcp_failover_state_match_by_name(state
, name
)
3397 dhcp_failover_state_t
*state
;
3398 failover_option_t
*name
;
3400 if ((strlen(state
->name
) == name
->count
) &&
3401 (memcmp(state
->name
, name
->data
, name
->count
) == 0))
3407 const char *dhcp_failover_reject_reason_print (int reason
)
3409 static char resbuf
[sizeof("Undefined-255: This reason code is not defined "
3410 "in the protocol standard.")];
3412 if ((reason
> 0xff) || (reason
< 0))
3413 return "Reason code out of range.";
3416 case FTR_ILLEGAL_IP_ADDR
:
3417 return "Illegal IP address (not part of any address pool).";
3419 case FTR_FATAL_CONFLICT
:
3420 return "Fatal conflict exists: address in use by other client.";
3422 case FTR_MISSING_BINDINFO
:
3423 return "Missing binding information.";
3425 case FTR_TIMEMISMATCH
:
3426 return "Connection rejected, time mismatch too great.";
3428 case FTR_INVALID_MCLT
:
3429 return "Connection rejected, invalid MCLT.";
3431 case FTR_MISC_REJECT
:
3432 return "Connection rejected, unknown reason.";
3434 case FTR_DUP_CONNECTION
:
3435 return "Connection rejected, duplicate connection.";
3437 case FTR_INVALID_PARTNER
:
3438 return "Connection rejected, invalid failover partner.";
3440 case FTR_TLS_UNSUPPORTED
:
3441 return "TLS not supported.";
3443 case FTR_TLS_UNCONFIGURED
:
3444 return "TLS supported but not configured.";
3446 case FTR_TLS_REQUIRED
:
3447 return "TLS required but not supported by partner.";
3449 case FTR_DIGEST_UNSUPPORTED
:
3450 return "Message digest not supported.";
3452 case FTR_DIGEST_UNCONFIGURED
:
3453 return "Message digest not configured.";
3455 case FTR_VERSION_MISMATCH
:
3456 return "Protocol version mismatch.";
3458 case FTR_OUTDATED_BIND_INFO
:
3459 return "Outdated binding information.";
3461 case FTR_LESS_CRIT_BIND_INFO
:
3462 return "Less critical binding information.";
3464 case FTR_NO_TRAFFIC
:
3465 return "No traffic within sufficient time.";
3467 case FTR_HBA_CONFLICT
:
3468 return "Hash bucket assignment conflict.";
3470 case FTR_IP_NOT_RESERVED
:
3471 return "IP not reserved on this server.";
3473 case FTR_IP_DIGEST_FAILURE
:
3474 return "Message digest failed to compare.";
3476 case FTR_IP_MISSING_DIGEST
:
3477 return "Missing message digest.";
3480 return "Unknown Error.";
3483 sprintf(resbuf
, "Undefined-%d: This reason code is not defined in the "
3484 "protocol standard.", reason
);
3489 const char *dhcp_failover_state_name_print (enum failover_state state
)
3494 return "unknown-state";
3497 return "partner-down";
3502 case communications_interrupted
:
3503 return "communications-interrupted";
3505 case resolution_interrupted
:
3506 return "resolution-interrupted";
3508 case potential_conflict
:
3509 return "potential-conflict";
3515 return "recover-done";
3518 return "recover-wait";
3531 const char *dhcp_failover_message_name (unsigned type
)
3533 static char messbuf
[sizeof("unknown-message-255")];
3536 return "invalid-message";
3540 return "pool-request";
3543 return "pool-response";
3546 return "bind-update";
3554 case FTM_CONNECTACK
:
3555 return "connect-ack";
3558 return "update-request";
3561 return "update-done";
3564 return "update-request-all";
3572 case FTM_DISCONNECT
:
3573 return "disconnect";
3576 sprintf(messbuf
, "unknown-message-%u", type
);
3581 const char *dhcp_failover_option_name (unsigned type
)
3583 static char optbuf
[sizeof("unknown-option-65535")];
3586 return "invalid-option";
3589 case FTO_ADDRESSES_TRANSFERRED
:
3590 return "addresses-transferred";
3592 case FTO_ASSIGNED_IP_ADDRESS
:
3593 return "assigned-ip-address";
3595 case FTO_BINDING_STATUS
:
3596 return "binding-status";
3598 case FTO_CLIENT_IDENTIFIER
:
3599 return "client-identifier";
3610 case FTO_DELAYED_SERVICE
:
3611 return "delayed-service";
3619 case FTO_LEASE_EXPIRY
:
3620 return "lease-expiry";
3622 case FTO_MAX_UNACKED
:
3623 return "max-unacked";
3631 case FTO_MESSAGE_DIGEST
:
3632 return "message-digest";
3634 case FTO_POTENTIAL_EXPIRY
:
3635 return "potential-expiry";
3637 case FTO_PROTOCOL_VERSION
:
3638 return "protocol-version";
3640 case FTO_RECEIVE_TIMER
:
3641 return "receive-timer";
3643 case FTO_REJECT_REASON
:
3644 return "reject-reason";
3646 case FTO_RELATIONSHIP_NAME
:
3647 return "relationship-name";
3649 case FTO_REPLY_OPTIONS
:
3650 return "reply-options";
3652 case FTO_REQUEST_OPTIONS
:
3653 return "request-options";
3655 case FTO_SERVER_FLAGS
:
3656 return "server-flags";
3658 case FTO_SERVER_STATE
:
3659 return "server-state";
3667 case FTO_TLS_REQUEST
:
3668 return "tls-request";
3670 case FTO_VENDOR_CLASS
:
3671 return "vendor-class";
3673 case FTO_VENDOR_OPTIONS
:
3674 return "vendor-options";
3677 sprintf(optbuf
, "unknown-option-%u", type
);
3682 failover_option_t
*dhcp_failover_option_printf (unsigned code
,
3686 const char *fmt
, ...)
3691 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3692 * It is unclear what the effects of truncation here are, or
3693 * how that condition should be handled. It seems that this
3694 * function is used for formatting messages in the failover
3695 * command channel. For now the safest thing is for
3696 * overflow-truncation to cause a fatal log.
3699 if (vsnprintf (tbuf
, sizeof tbuf
, fmt
, va
) >= sizeof tbuf
)
3700 log_fatal ("%s: vsnprintf would truncate",
3701 "dhcp_failover_make_option");
3704 return dhcp_failover_make_option (code
, obuf
, obufix
, obufmax
,
3705 strlen (tbuf
), tbuf
);
3708 failover_option_t
*dhcp_failover_make_option (unsigned code
,
3709 char *obuf
, unsigned *obufix
,
3710 unsigned obufmax
, ...)
3713 struct failover_option_info
*info
;
3715 unsigned size
, count
;
3721 #if defined (DEBUG_FAILOVER_MESSAGES)
3725 /* Note that the failover_option structure is used differently on
3726 input than on output - on input, count is an element count, and
3727 on output it's the number of bytes total in the option, including
3728 the option code and option length. */
3729 failover_option_t option
, *op
;
3732 /* Bogus option code? */
3733 if (code
< 1 || code
> FTO_MAX
|| ft_options
[code
].type
== FT_UNDEF
) {
3734 return &null_failover_option
;
3736 info
= &ft_options
[code
];
3738 va_start (va
, obufmax
);
3740 /* Get the number of elements and the size of the buffer we need
3742 if (info
-> type
== FT_DDNS
|| info
-> type
== FT_DDNS1
) {
3743 count
= info
-> type
== FT_DDNS
? 1 : 2;
3744 size
= va_arg (va
, int) + count
;
3746 /* Find out how many items in this list. */
3747 if (info
-> num_present
)
3748 count
= info
-> num_present
;
3750 count
= va_arg (va
, int);
3752 /* Figure out size. */
3753 switch (info
-> type
) {
3760 case FT_TEXT_OR_BYTES
:
3762 txt
= va_arg (va
, char *);
3767 ilen
= va_arg (va
, unsigned);
3768 size
= count
* ilen
;
3780 /* shouldn't get here. */
3781 log_fatal ("bogus type in failover_make_option: %d",
3783 return &null_failover_option
;
3789 /* Allocate a buffer for the option. */
3790 option
.count
= size
;
3791 option
.data
= dmalloc (option
.count
, MDL
);
3794 return &null_failover_option
;
3797 /* Put in the option code and option length. */
3798 putUShort (option
.data
, code
);
3799 putUShort (&option
.data
[2], size
- 4);
3801 #if defined (DEBUG_FAILOVER_MESSAGES)
3802 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3803 * It is unclear what the effects of truncation here are, or
3804 * how that condition should be handled. It seems that this
3805 * message may be sent over the failover command channel.
3806 * For now the safest thing is for overflow-truncation to cause
3809 if (snprintf (tbuf
, sizeof tbuf
, " (%s<%d>", info
-> name
,
3810 option
.count
) >= sizeof tbuf
)
3811 log_fatal ("dhcp_failover_make_option: tbuf overflow");
3812 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3815 /* Now put in the data. */
3816 switch (info
-> type
) {
3818 for (i
= 0; i
< count
; i
++) {
3819 val
= va_arg (va
, unsigned);
3820 #if defined (DEBUG_FAILOVER_MESSAGES)
3821 /* %Audit% Cannot exceed 24 bytes. %2004.06.17,Safe% */
3822 sprintf (tbuf
, " %d", val
);
3823 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3825 option
.data
[i
+ 4] = val
;
3830 for (i
= 0; i
< count
; i
++) {
3831 iaddr
= va_arg (va
, u_int8_t
*);
3833 dfree (option
.data
, MDL
);
3834 log_error ("IP addrlen=%d, should be 4.",
3837 return &null_failover_option
;
3840 #if defined (DEBUG_FAILOVER_MESSAGES)
3841 /*%Audit% Cannot exceed 17 bytes. %2004.06.17,Safe%*/
3842 sprintf (tbuf
, " %u.%u.%u.%u",
3843 iaddr
[0], iaddr
[1], iaddr
[2], iaddr
[3]);
3844 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3846 memcpy (&option
.data
[4 + i
* ilen
], iaddr
, ilen
);
3851 for (i
= 0; i
< count
; i
++) {
3852 val
= va_arg (va
, unsigned);
3853 #if defined (DEBUG_FAILOVER_MESSAGES)
3854 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3855 sprintf (tbuf
, " %d", val
);
3856 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3858 putULong (&option
.data
[4 + i
* 4], val
);
3864 bval
= va_arg (va
, u_int8_t
*);
3865 #if defined (DEBUG_FAILOVER_MESSAGES)
3866 for (i
= 0; i
< count
; i
++) {
3867 /* 23 bytes plus nul, safe. */
3868 sprintf (tbuf
, " %d", bval
[i
]);
3869 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3872 memcpy (&option
.data
[4], bval
, count
);
3875 /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
3876 terminated. Note that the caller should be careful not
3877 to provide a format and data that amount to more than 256
3878 bytes of data, since it will cause a fatal error. */
3879 case FT_TEXT_OR_BYTES
:
3881 #if defined (DEBUG_FAILOVER_MESSAGES)
3882 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3883 * It is unclear what the effects of truncation here are, or
3884 * how that condition should be handled. It seems that this
3885 * function is used for formatting messages in the failover
3886 * command channel. For now the safest thing is for
3887 * overflow-truncation to cause a fatal log.
3889 if (snprintf (tbuf
, sizeof tbuf
, "\"%s\"", txt
) >= sizeof tbuf
)
3890 log_fatal ("dhcp_failover_make_option: tbuf overflow");
3891 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3893 memcpy (&option
.data
[4], txt
, count
);
3898 option
.data
[4] = va_arg (va
, unsigned);
3900 option
.data
[5] = va_arg (va
, unsigned);
3901 bval
= va_arg (va
, u_int8_t
*);
3902 memcpy (&option
.data
[4 + count
], bval
, size
- count
- 4);
3903 #if defined (DEBUG_FAILOVER_MESSAGES)
3904 for (i
= 4; i
< size
; i
++) {
3905 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3906 sprintf (tbuf
, " %d", option
.data
[i
]);
3907 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3913 for (i
= 0; i
< count
; i
++) {
3914 val
= va_arg (va
, u_int32_t
);
3915 #if defined (DEBUG_FAILOVER_MESSAGES)
3916 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3917 sprintf (tbuf
, " %d", val
);
3918 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3920 putUShort (&option
.data
[4 + i
* 2], val
);
3929 #if defined DEBUG_FAILOVER_MESSAGES
3930 failover_print (obuf
, obufix
, obufmax
, ")");
3934 /* Now allocate a place to store what we just set up. */
3935 op
= dmalloc (sizeof (failover_option_t
), MDL
);
3937 dfree (option
.data
, MDL
);
3938 return &null_failover_option
;
3945 /* Send a failover message header. */
3947 isc_result_t
dhcp_failover_put_message (dhcp_failover_link_t
*link
,
3948 omapi_object_t
*connection
,
3949 int msg_type
, u_int32_t xid
, ...)
3956 failover_option_t
*option
;
3957 unsigned char *opbuf
;
3958 isc_result_t status
= ISC_R_SUCCESS
;
3961 /* Run through the argument list once to compute the length of
3962 the option portion of the message. */
3963 va_start (list
, xid
);
3964 while ((option
= va_arg (list
, failover_option_t
*))) {
3965 if (option
!= &skip_failover_option
)
3966 size
+= option
-> count
;
3967 if (option
== &null_failover_option
)
3972 /* Allocate an option buffer, unless we got an error. */
3973 if (!bad_option
&& size
) {
3974 opbuf
= dmalloc (size
, MDL
);
3976 status
= ISC_R_NOMEMORY
;
3978 opbuf
= (unsigned char *)0;
3980 va_start (list
, xid
);
3981 while ((option
= va_arg (list
, failover_option_t
*))) {
3982 if (option
== &skip_failover_option
)
3984 if (!bad_option
&& opbuf
)
3985 memcpy (&opbuf
[opix
],
3986 option
-> data
, option
-> count
);
3987 if (option
!= &null_failover_option
&&
3988 option
!= &skip_failover_option
) {
3989 opix
+= option
-> count
;
3990 dfree (option
-> data
, MDL
);
3991 dfree (option
, MDL
);
3997 return ISC_R_INVALIDARG
;
3999 /* Now send the message header. */
4001 /* Message length. */
4002 status
= omapi_connection_put_uint16 (connection
, size
+ 12);
4003 if (status
!= ISC_R_SUCCESS
)
4008 status
= omapi_connection_copyin (connection
, &cbuf
, 1);
4009 if (status
!= ISC_R_SUCCESS
)
4012 /* Payload offset. */
4014 status
= omapi_connection_copyin (connection
, &cbuf
, 1);
4015 if (status
!= ISC_R_SUCCESS
)
4019 status
= omapi_connection_put_uint32 (connection
, (u_int32_t
)cur_time
);
4020 if (status
!= ISC_R_SUCCESS
)
4023 /* Transaction ID. */
4024 status
= omapi_connection_put_uint32(connection
, xid
);
4025 if (status
!= ISC_R_SUCCESS
)
4030 status
= omapi_connection_copyin (connection
, opbuf
, size
);
4031 if (status
!= ISC_R_SUCCESS
)
4035 if (link
-> state_object
&&
4036 link
-> state_object
-> link_to_peer
== link
) {
4037 #if defined (DEBUG_FAILOVER_TIMING)
4038 log_info ("add_timeout +%d %s",
4039 (int)(link
-> state_object
->
4040 partner
.max_response_delay
) / 3,
4041 "dhcp_failover_send_contact");
4043 add_timeout (cur_time
+
4044 (int)(link
-> state_object
->
4045 partner
.max_response_delay
) / 3,
4046 dhcp_failover_send_contact
, link
-> state_object
,
4047 (tvref_t
)dhcp_failover_state_reference
,
4048 (tvunref_t
)dhcp_failover_state_dereference
);
4055 log_info ("dhcp_failover_put_message: something went wrong.");
4056 omapi_disconnect (connection
, 1);
4060 void dhcp_failover_timeout (void *vstate
)
4062 dhcp_failover_state_t
*state
= vstate
;
4063 dhcp_failover_link_t
*link
;
4064 isc_result_t status
;
4066 #if defined (DEBUG_FAILOVER_TIMING)
4067 log_info ("dhcp_failover_timeout");
4070 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
4072 link
= state
-> link_to_peer
;
4075 link
-> outer
-> type
!= omapi_type_connection
)
4078 log_error ("timeout waiting for failover peer %s", state
-> name
);
4080 /* If we haven't gotten a timely response, blow away the connection.
4081 This will cause the state to change automatically. */
4082 omapi_disconnect (link
-> outer
, 1);
4085 void dhcp_failover_send_contact (void *vstate
)
4087 dhcp_failover_state_t
*state
= vstate
;
4088 dhcp_failover_link_t
*link
;
4089 isc_result_t status
;
4091 #if defined (DEBUG_FAILOVER_MESSAGES)
4093 unsigned obufix
= 0;
4095 # define FMA obuf, &obufix, sizeof obuf
4096 failover_print (FMA
, "(contact");
4098 # define FMA (char *)0, (unsigned *)0, 0
4101 #if defined (DEBUG_FAILOVER_TIMING)
4102 log_info ("dhcp_failover_send_contact");
4105 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
4107 link
= state
-> link_to_peer
;
4110 link
-> outer
-> type
!= omapi_type_connection
)
4113 status
= (dhcp_failover_put_message
4114 (link
, link
-> outer
,
4115 FTM_CONTACT
, link
->xid
++,
4116 (failover_option_t
*)0));
4118 #if defined (DEBUG_FAILOVER_MESSAGES)
4119 if (status
!= ISC_R_SUCCESS
)
4120 failover_print (FMA
, " (failed)");
4121 failover_print (FMA
, ")");
4123 log_debug ("%s", obuf
);
4129 isc_result_t
dhcp_failover_send_state (dhcp_failover_state_t
*state
)
4131 dhcp_failover_link_t
*link
;
4132 isc_result_t status
;
4134 #if defined (DEBUG_FAILOVER_MESSAGES)
4136 unsigned obufix
= 0;
4138 # define FMA obuf, &obufix, sizeof obuf
4139 failover_print (FMA
, "(state");
4141 # define FMA (char *)0, (unsigned *)0, 0
4144 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
4145 return ISC_R_INVALIDARG
;
4146 link
= state
-> link_to_peer
;
4149 link
-> outer
-> type
!= omapi_type_connection
)
4150 return ISC_R_INVALIDARG
;
4152 status
= (dhcp_failover_put_message
4153 (link
, link
-> outer
,
4154 FTM_STATE
, link
->xid
++,
4155 dhcp_failover_make_option (FTO_SERVER_STATE
, FMA
,
4156 (state
-> me
.state
== startup
4157 ? state
-> saved_state
4158 : state
-> me
.state
)),
4159 dhcp_failover_make_option
4160 (FTO_SERVER_FLAGS
, FMA
,
4161 (state
-> service_state
== service_startup
4162 ? FTF_SERVER_STARTUP
: 0)),
4163 dhcp_failover_make_option (FTO_STOS
, FMA
, state
-> me
.stos
),
4164 (failover_option_t
*)0));
4166 #if defined (DEBUG_FAILOVER_MESSAGES)
4167 if (status
!= ISC_R_SUCCESS
)
4168 failover_print (FMA
, " (failed)");
4169 failover_print (FMA
, ")");
4171 log_debug ("%s", obuf
);
4174 return ISC_R_SUCCESS
;
4177 /* Send a connect message. */
4179 isc_result_t
dhcp_failover_send_connect (omapi_object_t
*l
)
4181 dhcp_failover_link_t
*link
;
4182 dhcp_failover_state_t
*state
;
4183 isc_result_t status
;
4185 #if defined (DEBUG_FAILOVER_MESSAGES)
4187 unsigned obufix
= 0;
4189 # define FMA obuf, &obufix, sizeof obuf
4190 failover_print (FMA
, "(connect");
4192 # define FMA (char *)0, (unsigned *)0, 0
4195 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
4196 return ISC_R_INVALIDARG
;
4197 link
= (dhcp_failover_link_t
*)l
;
4198 state
= link
-> state_object
;
4199 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
4200 return ISC_R_INVALIDARG
;
4203 (dhcp_failover_put_message
4205 FTM_CONNECT
, link
->xid
++,
4206 dhcp_failover_make_option(FTO_RELATIONSHIP_NAME
, FMA
,
4207 strlen(state
->name
), state
->name
),
4208 dhcp_failover_make_option (FTO_MAX_UNACKED
, FMA
,
4209 state
-> me
.max_flying_updates
),
4210 dhcp_failover_make_option (FTO_RECEIVE_TIMER
, FMA
,
4211 state
-> me
.max_response_delay
),
4212 dhcp_failover_option_printf(FTO_VENDOR_CLASS
, FMA
,
4213 "isc-%s", PACKAGE_VERSION
),
4214 dhcp_failover_make_option (FTO_PROTOCOL_VERSION
, FMA
,
4215 DHCP_FAILOVER_VERSION
),
4216 dhcp_failover_make_option (FTO_TLS_REQUEST
, FMA
,
4218 dhcp_failover_make_option (FTO_MCLT
, FMA
,
4221 ? dhcp_failover_make_option (FTO_HBA
, FMA
, 32, state
-> hba
)
4222 : &skip_failover_option
),
4223 (failover_option_t
*)0));
4225 #if defined (DEBUG_FAILOVER_MESSAGES)
4226 if (status
!= ISC_R_SUCCESS
)
4227 failover_print (FMA
, " (failed)");
4228 failover_print (FMA
, ")");
4230 log_debug ("%s", obuf
);
4236 isc_result_t
dhcp_failover_send_connectack (omapi_object_t
*l
,
4237 dhcp_failover_state_t
*state
,
4238 int reason
, const char *errmsg
)
4240 dhcp_failover_link_t
*link
;
4241 isc_result_t status
;
4242 #if defined (DEBUG_FAILOVER_MESSAGES)
4244 unsigned obufix
= 0;
4246 # define FMA obuf, &obufix, sizeof obuf
4247 failover_print (FMA
, "(connectack");
4249 # define FMA (char *)0, (unsigned *)0, 0
4252 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
4253 return ISC_R_INVALIDARG
;
4254 link
= (dhcp_failover_link_t
*)l
;
4255 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
4256 return ISC_R_INVALIDARG
;
4259 (dhcp_failover_put_message
4261 FTM_CONNECTACK
, link
->imsg
->xid
,
4263 ? dhcp_failover_make_option(FTO_RELATIONSHIP_NAME
, FMA
,
4264 strlen(state
->name
), state
->name
)
4265 : (link
->imsg
->options_present
& FTB_RELATIONSHIP_NAME
)
4266 ? &link
->imsg
->relationship_name
4267 : &skip_failover_option
,
4269 ? dhcp_failover_make_option (FTO_MAX_UNACKED
, FMA
,
4270 state
-> me
.max_flying_updates
)
4271 : &skip_failover_option
,
4273 ? dhcp_failover_make_option (FTO_RECEIVE_TIMER
, FMA
,
4274 state
-> me
.max_response_delay
)
4275 : &skip_failover_option
,
4276 dhcp_failover_option_printf(FTO_VENDOR_CLASS
, FMA
,
4277 "isc-%s", PACKAGE_VERSION
),
4278 dhcp_failover_make_option (FTO_PROTOCOL_VERSION
, FMA
,
4279 DHCP_FAILOVER_VERSION
),
4280 (link
->imsg
->options_present
& FTB_TLS_REQUEST
)
4281 ? dhcp_failover_make_option(FTO_TLS_REPLY
, FMA
,
4283 : &skip_failover_option
,
4285 ? dhcp_failover_make_option (FTO_REJECT_REASON
,
4287 : &skip_failover_option
,
4289 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4290 strlen (errmsg
), errmsg
)
4291 : &skip_failover_option
,
4292 (failover_option_t
*)0));
4294 #if defined (DEBUG_FAILOVER_MESSAGES)
4295 if (status
!= ISC_R_SUCCESS
)
4296 failover_print (FMA
, " (failed)");
4297 failover_print (FMA
, ")");
4299 log_debug ("%s", obuf
);
4305 isc_result_t
dhcp_failover_send_disconnect (omapi_object_t
*l
,
4307 const char *message
)
4309 dhcp_failover_link_t
*link
;
4310 dhcp_failover_state_t
*state
;
4311 isc_result_t status
;
4312 #if defined (DEBUG_FAILOVER_MESSAGES)
4314 unsigned obufix
= 0;
4316 # define FMA obuf, &obufix, sizeof obuf
4317 failover_print (FMA
, "(disconnect");
4319 # define FMA (char *)0, (unsigned *)0, 0
4322 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
4323 return ISC_R_INVALIDARG
;
4324 link
= (dhcp_failover_link_t
*)l
;
4325 state
= link
-> state_object
;
4326 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
4327 return ISC_R_INVALIDARG
;
4329 if (!message
&& reason
)
4330 message
= dhcp_failover_reject_reason_print (reason
);
4332 status
= (dhcp_failover_put_message
4334 FTM_DISCONNECT
, link
->xid
++,
4335 dhcp_failover_make_option (FTO_REJECT_REASON
,
4338 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4339 strlen (message
), message
)
4340 : &skip_failover_option
),
4341 (failover_option_t
*)0));
4343 #if defined (DEBUG_FAILOVER_MESSAGES)
4344 if (status
!= ISC_R_SUCCESS
)
4345 failover_print (FMA
, " (failed)");
4346 failover_print (FMA
, ")");
4348 log_debug ("%s", obuf
);
4354 /* Send a Bind Update message. */
4356 isc_result_t
dhcp_failover_send_bind_update (dhcp_failover_state_t
*state
,
4357 struct lease
*lease
)
4359 dhcp_failover_link_t
*link
;
4360 isc_result_t status
;
4362 binding_state_t transmit_state
;
4363 #if defined (DEBUG_FAILOVER_MESSAGES)
4365 unsigned obufix
= 0;
4367 # define FMA obuf, &obufix, sizeof obuf
4368 failover_print (FMA
, "(bndupd");
4370 # define FMA (char *)0, (unsigned *)0, 0
4373 if (!state
-> link_to_peer
||
4374 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4375 return ISC_R_INVALIDARG
;
4376 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4378 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4379 return ISC_R_INVALIDARG
;
4381 transmit_state
= lease
->desired_binding_state
;
4382 if (lease
->flags
& RESERVED_LEASE
) {
4383 /* If we are listing an allocable (not yet ACTIVE etc) lease
4384 * as reserved, toggle to the peer's 'free state', per the
4385 * draft. This gives the peer permission to alloc it to the
4386 * chaddr/uid-named client.
4388 if ((state
->i_am
== primary
) && (transmit_state
== FTS_FREE
))
4389 transmit_state
= FTS_BACKUP
;
4390 else if ((state
->i_am
== secondary
) &&
4391 (transmit_state
== FTS_BACKUP
))
4392 transmit_state
= FTS_FREE
;
4394 flags
|= FTF_IP_FLAG_RESERVE
;
4396 if (lease
->flags
& BOOTP_LEASE
)
4397 flags
|= FTF_IP_FLAG_BOOTP
;
4399 /* last_xid == 0 is illegal, seek past zero if we hit it. */
4403 lease
->last_xid
= link
->xid
++;
4405 /* Send the update. */
4406 status
= (dhcp_failover_put_message
4407 (link
, link
-> outer
,
4408 FTM_BNDUPD
, lease
->last_xid
,
4409 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS
, FMA
,
4410 lease
-> ip_addr
.len
,
4411 lease
-> ip_addr
.iabuf
),
4412 dhcp_failover_make_option (FTO_BINDING_STATUS
, FMA
,
4413 lease
-> desired_binding_state
),
4415 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER
, FMA
,
4418 : &skip_failover_option
,
4419 lease
-> hardware_addr
.hlen
4420 ? dhcp_failover_make_option (FTO_CHADDR
, FMA
,
4421 lease
-> hardware_addr
.hlen
,
4422 lease
-> hardware_addr
.hbuf
)
4423 : &skip_failover_option
,
4424 dhcp_failover_make_option (FTO_LEASE_EXPIRY
, FMA
,
4426 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY
, FMA
,
4428 dhcp_failover_make_option (FTO_STOS
, FMA
,
4430 dhcp_failover_make_option (FTO_CLTT
, FMA
,
4432 flags
? dhcp_failover_make_option(FTO_IP_FLAGS
, FMA
,
4434 &skip_failover_option
, /* No IP_FLAGS */
4435 &skip_failover_option
, /* XXX DDNS */
4436 &skip_failover_option
, /* XXX request options */
4437 &skip_failover_option
, /* XXX reply options */
4438 (failover_option_t
*)0));
4440 #if defined (DEBUG_FAILOVER_MESSAGES)
4441 if (status
!= ISC_R_SUCCESS
)
4442 failover_print (FMA
, " (failed)");
4443 failover_print (FMA
, ")");
4445 log_debug ("%s", obuf
);
4451 /* Send a Bind ACK message. */
4453 isc_result_t
dhcp_failover_send_bind_ack (dhcp_failover_state_t
*state
,
4454 failover_message_t
*msg
,
4455 int reason
, const char *message
)
4457 dhcp_failover_link_t
*link
;
4458 isc_result_t status
;
4459 #if defined (DEBUG_FAILOVER_MESSAGES)
4461 unsigned obufix
= 0;
4463 # define FMA obuf, &obufix, sizeof obuf
4464 failover_print (FMA
, "(bndack");
4466 # define FMA (char *)0, (unsigned *)0, 0
4469 if (!state
-> link_to_peer
||
4470 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4471 return ISC_R_INVALIDARG
;
4472 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4474 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4475 return ISC_R_INVALIDARG
;
4477 if (!message
&& reason
)
4478 message
= dhcp_failover_reject_reason_print (reason
);
4480 /* Send the update. */
4481 status
= (dhcp_failover_put_message
4482 (link
, link
-> outer
,
4483 FTM_BNDACK
, msg
->xid
,
4484 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS
, FMA
,
4485 sizeof msg
-> assigned_addr
,
4486 &msg
-> assigned_addr
),
4487 #ifdef DO_BNDACK_SHOULD_NOT
4488 dhcp_failover_make_option (FTO_BINDING_STATUS
, FMA
,
4489 msg
-> binding_status
),
4490 (msg
-> options_present
& FTB_CLIENT_IDENTIFIER
)
4491 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER
, FMA
,
4492 msg
-> client_identifier
.count
,
4493 msg
-> client_identifier
.data
)
4494 : &skip_failover_option
,
4495 (msg
-> options_present
& FTB_CHADDR
)
4496 ? dhcp_failover_make_option (FTO_CHADDR
, FMA
,
4497 msg
-> chaddr
.count
,
4499 : &skip_failover_option
,
4500 dhcp_failover_make_option (FTO_LEASE_EXPIRY
, FMA
,
4502 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY
, FMA
,
4503 msg
-> potential_expiry
),
4504 dhcp_failover_make_option (FTO_STOS
, FMA
,
4506 dhcp_failover_make_option (FTO_CLTT
, FMA
,
4508 ((msg
->options_present
& FTB_IP_FLAGS
) && msg
->ip_flags
) ?
4509 dhcp_failover_make_option(FTO_IP_FLAGS
, FMA
,
4511 : &skip_failover_option
,
4512 #endif /* DO_BNDACK_SHOULD_NOT */
4514 ? dhcp_failover_make_option(FTO_REJECT_REASON
, FMA
, reason
)
4515 : &skip_failover_option
,
4517 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4518 strlen (message
), message
)
4519 : &skip_failover_option
,
4520 #ifdef DO_BNDACK_SHOULD_NOT
4521 &skip_failover_option
, /* XXX DDNS */
4522 &skip_failover_option
, /* XXX request options */
4523 &skip_failover_option
, /* XXX reply options */
4524 #endif /* DO_BNDACK_SHOULD_NOT */
4525 (failover_option_t
*)0));
4527 #if defined (DEBUG_FAILOVER_MESSAGES)
4528 if (status
!= ISC_R_SUCCESS
)
4529 failover_print (FMA
, " (failed)");
4530 failover_print (FMA
, ")");
4532 log_debug ("%s", obuf
);
4538 isc_result_t
dhcp_failover_send_poolreq (dhcp_failover_state_t
*state
)
4540 dhcp_failover_link_t
*link
;
4541 isc_result_t status
;
4542 #if defined (DEBUG_FAILOVER_MESSAGES)
4544 unsigned obufix
= 0;
4546 # define FMA obuf, &obufix, sizeof obuf
4547 failover_print (FMA
, "(poolreq");
4549 # define FMA (char *)0, (unsigned *)0, 0
4552 if (!state
-> link_to_peer
||
4553 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4554 return ISC_R_INVALIDARG
;
4555 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4557 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4558 return ISC_R_INVALIDARG
;
4560 status
= (dhcp_failover_put_message
4561 (link
, link
-> outer
,
4562 FTM_POOLREQ
, link
->xid
++,
4563 (failover_option_t
*)0));
4565 #if defined (DEBUG_FAILOVER_MESSAGES)
4566 if (status
!= ISC_R_SUCCESS
)
4567 failover_print (FMA
, " (failed)");
4568 failover_print (FMA
, ")");
4570 log_debug ("%s", obuf
);
4576 isc_result_t
dhcp_failover_send_poolresp (dhcp_failover_state_t
*state
,
4579 dhcp_failover_link_t
*link
;
4580 isc_result_t status
;
4581 #if defined (DEBUG_FAILOVER_MESSAGES)
4583 unsigned obufix
= 0;
4585 # define FMA obuf, &obufix, sizeof obuf
4586 failover_print (FMA
, "(poolresp");
4588 # define FMA (char *)0, (unsigned *)0, 0
4591 if (!state
-> link_to_peer
||
4592 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4593 return ISC_R_INVALIDARG
;
4594 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4596 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4597 return ISC_R_INVALIDARG
;
4599 status
= (dhcp_failover_put_message
4600 (link
, link
-> outer
,
4601 FTM_POOLRESP
, link
->imsg
->xid
,
4602 dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED
, FMA
,
4604 (failover_option_t
*)0));
4606 #if defined (DEBUG_FAILOVER_MESSAGES)
4607 if (status
!= ISC_R_SUCCESS
)
4608 failover_print (FMA
, " (failed)");
4609 failover_print (FMA
, ")");
4611 log_debug ("%s", obuf
);
4617 isc_result_t
dhcp_failover_send_update_request (dhcp_failover_state_t
*state
)
4619 dhcp_failover_link_t
*link
;
4620 isc_result_t status
;
4621 #if defined (DEBUG_FAILOVER_MESSAGES)
4623 unsigned obufix
= 0;
4625 # define FMA obuf, &obufix, sizeof obuf
4626 failover_print (FMA
, "(updreq");
4628 # define FMA (char *)0, (unsigned *)0, 0
4631 if (!state
-> link_to_peer
||
4632 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4633 return ISC_R_INVALIDARG
;
4634 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4636 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4637 return ISC_R_INVALIDARG
;
4639 if (state
-> curUPD
)
4640 return ISC_R_ALREADYRUNNING
;
4642 status
= (dhcp_failover_put_message
4643 (link
, link
-> outer
,
4644 FTM_UPDREQ
, link
->xid
++,
4645 (failover_option_t
*)0));
4647 if (status
== ISC_R_SUCCESS
)
4648 state
-> curUPD
= FTM_UPDREQ
;
4650 #if defined (DEBUG_FAILOVER_MESSAGES)
4651 if (status
!= ISC_R_SUCCESS
)
4652 failover_print (FMA
, " (failed)");
4653 failover_print (FMA
, ")");
4655 log_debug ("%s", obuf
);
4658 log_info ("Sent update request message to %s", state
-> name
);
4662 isc_result_t
dhcp_failover_send_update_request_all (dhcp_failover_state_t
4665 dhcp_failover_link_t
*link
;
4666 isc_result_t status
;
4667 #if defined (DEBUG_FAILOVER_MESSAGES)
4669 unsigned obufix
= 0;
4671 # define FMA obuf, &obufix, sizeof obuf
4672 failover_print (FMA
, "(updreqall");
4674 # define FMA (char *)0, (unsigned *)0, 0
4677 if (!state
-> link_to_peer
||
4678 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4679 return ISC_R_INVALIDARG
;
4680 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4682 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4683 return ISC_R_INVALIDARG
;
4685 /* If there is an UPDREQ in progress, then upgrade to UPDREQALL. */
4686 if (state
-> curUPD
&& (state
-> curUPD
!= FTM_UPDREQ
))
4687 return ISC_R_ALREADYRUNNING
;
4689 status
= (dhcp_failover_put_message
4690 (link
, link
-> outer
,
4691 FTM_UPDREQALL
, link
->xid
++,
4692 (failover_option_t
*)0));
4694 if (status
== ISC_R_SUCCESS
)
4695 state
-> curUPD
= FTM_UPDREQALL
;
4697 #if defined (DEBUG_FAILOVER_MESSAGES)
4698 if (status
!= ISC_R_SUCCESS
)
4699 failover_print (FMA
, " (failed)");
4700 failover_print (FMA
, ")");
4702 log_debug ("%s", obuf
);
4705 log_info ("Sent update request all message to %s", state
-> name
);
4709 isc_result_t
dhcp_failover_send_update_done (dhcp_failover_state_t
*state
)
4711 dhcp_failover_link_t
*link
;
4712 isc_result_t status
;
4713 #if defined (DEBUG_FAILOVER_MESSAGES)
4715 unsigned obufix
= 0;
4717 # define FMA obuf, &obufix, sizeof obuf
4718 failover_print (FMA
, "(upddone");
4720 # define FMA (char *)0, (unsigned *)0, 0
4723 if (!state
-> link_to_peer
||
4724 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4725 return ISC_R_INVALIDARG
;
4726 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4728 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4729 return ISC_R_INVALIDARG
;
4731 status
= (dhcp_failover_put_message
4732 (link
, link
-> outer
,
4733 FTM_UPDDONE
, state
->updxid
,
4734 (failover_option_t
*)0));
4736 #if defined (DEBUG_FAILOVER_MESSAGES)
4737 if (status
!= ISC_R_SUCCESS
)
4738 failover_print (FMA
, " (failed)");
4739 failover_print (FMA
, ")");
4741 log_debug ("%s", obuf
);
4745 log_info ("Sent update done message to %s", state
-> name
);
4747 state
->updxid
--; /* Paranoia, just so it mismatches. */
4749 /* There may be uncommitted leases at this point (since
4750 dhcp_failover_process_bind_ack() doesn't commit leases);
4751 commit the lease file. */
4757 isc_result_t
dhcp_failover_process_bind_update (dhcp_failover_state_t
*state
,
4758 failover_message_t
*msg
)
4760 struct lease
*lt
, *lease
;
4762 int reason
= FTR_MISC_REJECT
;
4763 const char *message
;
4764 int new_binding_state
;
4765 int send_to_backup
= 0;
4767 ia
.len
= sizeof msg
-> assigned_addr
;
4768 memcpy (ia
.iabuf
, &msg
-> assigned_addr
, ia
.len
);
4770 lease
= (struct lease
*)0;
4771 lt
= (struct lease
*)0;
4772 if (!find_lease_by_ip_addr (&lease
, ia
, MDL
)) {
4773 message
= "unknown IP address";
4774 reason
= FTR_ILLEGAL_IP_ADDR
;
4778 /* XXX check for conflicts. */
4780 /* Install the new info. */
4781 if (!lease_copy (<
, lease
, MDL
)) {
4782 message
= "no memory";
4786 if (msg
-> options_present
& FTB_CHADDR
) {
4787 if (msg
->binding_status
== FTS_ABANDONED
) {
4788 message
= "BNDUPD to ABANDONED with a CHADDR";
4791 if (msg
-> chaddr
.count
> sizeof lt
-> hardware_addr
.hbuf
) {
4792 message
= "chaddr too long";
4795 lt
-> hardware_addr
.hlen
= msg
-> chaddr
.count
;
4796 memcpy (lt
-> hardware_addr
.hbuf
, msg
-> chaddr
.data
,
4797 msg
-> chaddr
.count
);
4798 } else if (msg
->binding_status
== FTS_ACTIVE
||
4799 msg
->binding_status
== FTS_EXPIRED
||
4800 msg
->binding_status
== FTS_RELEASED
) {
4801 message
= "BNDUPD without CHADDR";
4803 } else if (msg
->binding_status
== FTS_ABANDONED
) {
4804 lt
->hardware_addr
.hlen
= 0;
4806 binding_scope_dereference(<
->scope
, MDL
);
4809 /* There is no explicit message content to indicate that the client
4810 * supplied no client-identifier. So if we don't hear of a value,
4811 * we discard the last one.
4813 if (msg
->options_present
& FTB_CLIENT_IDENTIFIER
) {
4814 if (msg
->binding_status
== FTS_ABANDONED
) {
4815 message
= "BNDUPD to ABANDONED with client-id";
4819 lt
->uid_len
= msg
->client_identifier
.count
;
4821 /* Allocate the lt->uid buffer if we haven't already, or
4822 * re-allocate the lt-uid buffer if we have one that is not
4823 * large enough. Otherwise, just use the extant buffer.
4825 if (!lt
->uid
|| lt
->uid
== lt
->uid_buf
||
4826 lt
->uid_len
> lt
->uid_max
) {
4827 if (lt
->uid
&& lt
->uid
!= lt
->uid_buf
)
4828 dfree(lt
->uid
, MDL
);
4830 if (lt
->uid_len
> sizeof(lt
->uid_buf
)) {
4831 lt
->uid_max
= lt
->uid_len
;
4832 lt
->uid
= dmalloc(lt
->uid_len
, MDL
);
4834 message
= "no memory";
4838 lt
->uid_max
= sizeof(lt
->uid_buf
);
4839 lt
->uid
= lt
->uid_buf
;
4843 msg
-> client_identifier
.data
, lt
-> uid_len
);
4844 } else if (lt
->uid
&& msg
->binding_status
!= FTS_RESET
&&
4845 msg
->binding_status
!= FTS_FREE
&&
4846 msg
->binding_status
!= FTS_BACKUP
) {
4847 if (lt
->uid
!= lt
->uid_buf
)
4848 dfree (lt
->uid
, MDL
);
4850 lt
->uid_max
= lt
->uid_len
= 0;
4853 /* If the lease was expired, also remove the stale binding scope. */
4854 if (lt
->scope
&& lt
->ends
< cur_time
)
4855 binding_scope_dereference(<
->scope
, MDL
);
4857 /* XXX Times may need to be adjusted based on clock skew! */
4858 if (msg
-> options_present
& FTB_STOS
) {
4859 lt
-> starts
= msg
-> stos
;
4861 if (msg
-> options_present
& FTB_LEASE_EXPIRY
) {
4862 lt
-> ends
= msg
-> expiry
;
4864 if (msg
-> options_present
& FTB_CLTT
) {
4865 lt
-> cltt
= msg
-> cltt
;
4867 if (msg
->options_present
& FTB_POTENTIAL_EXPIRY
) {
4868 lt
->atsfp
= lt
->tsfp
= msg
->potential_expiry
;
4870 if (msg
->options_present
& FTB_IP_FLAGS
) {
4871 if (msg
->ip_flags
& FTF_IP_FLAG_RESERVE
) {
4872 if ((((state
->i_am
== primary
) &&
4873 (lease
->binding_state
== FTS_FREE
)) ||
4874 ((state
->i_am
== secondary
) &&
4875 (lease
->binding_state
== FTS_BACKUP
))) &&
4876 !(lease
->flags
& RESERVED_LEASE
)) {
4877 message
= "Address is not reserved.";
4878 reason
= FTR_IP_NOT_RESERVED
;
4882 lt
->flags
|= RESERVED_LEASE
;
4884 lt
->flags
&= ~RESERVED_LEASE
;
4886 if (msg
->ip_flags
& FTF_IP_FLAG_BOOTP
) {
4887 if ((((state
->i_am
== primary
) &&
4888 (lease
->binding_state
== FTS_FREE
)) ||
4889 ((state
->i_am
== secondary
) &&
4890 (lease
->binding_state
== FTS_BACKUP
))) &&
4891 !(lease
->flags
& BOOTP_LEASE
)) {
4892 message
= "Address is not allocated to BOOTP.";
4895 lt
->flags
|= BOOTP_LEASE
;
4897 lt
->flags
&= ~BOOTP_LEASE
;
4899 if (msg
->ip_flags
& ~(FTF_IP_FLAG_RESERVE
| FTF_IP_FLAG_BOOTP
))
4900 log_info("Unknown IP-flags set in BNDUPD (0x%x).",
4902 } else /* Flags may only not appear if the values are zero. */
4903 lt
->flags
&= ~(RESERVED_LEASE
| BOOTP_LEASE
);
4905 if (msg
-> options_present
& FTB_BINDING_STATUS
) {
4906 #if defined (DEBUG_LEASE_STATE_TRANSITIONS)
4907 log_info ("processing state transition for %s: %s to %s",
4908 piaddr (lease
-> ip_addr
),
4909 binding_state_print (lease
-> binding_state
),
4910 binding_state_print (msg
-> binding_status
));
4913 /* If we're in normal state, make sure the state transition
4915 if (state
-> me
.state
== normal
) {
4917 (normal_binding_state_transition_check
4918 (lease
, state
, msg
-> binding_status
,
4919 msg
-> potential_expiry
));
4920 /* XXX if the transition the peer asked for isn't
4921 XXX allowed, maybe we should make the transition
4922 XXX into potential-conflict at this point. */
4925 (conflict_binding_state_transition_check
4926 (lease
, state
, msg
-> binding_status
,
4927 msg
-> potential_expiry
));
4929 if (new_binding_state
!= msg
-> binding_status
) {
4932 if (snprintf (outbuf
, sizeof outbuf
,
4933 "%s: invalid state transition: %s to %s",
4934 piaddr (lease
-> ip_addr
),
4935 binding_state_print (lease
-> binding_state
),
4936 binding_state_print (msg
-> binding_status
))
4938 log_fatal ("%s: impossible outbuf overflow",
4939 "dhcp_failover_process_bind_update");
4941 dhcp_failover_send_bind_ack (state
, msg
,
4946 if (new_binding_state
== FTS_EXPIRED
||
4947 new_binding_state
== FTS_RELEASED
||
4948 new_binding_state
== FTS_RESET
) {
4949 lt
-> next_binding_state
= FTS_FREE
;
4951 /* Mac address affinity. Assign the lease to
4952 * BACKUP state if we are the primary and the
4953 * peer is more likely to reallocate this lease
4954 * to a returning client.
4956 if ((state
->i_am
== primary
) &&
4957 !(lt
->flags
& (RESERVED_LEASE
| BOOTP_LEASE
)))
4958 send_to_backup
= peer_wants_lease(lt
);
4960 lt
-> next_binding_state
= new_binding_state
;
4962 msg
-> binding_status
= lt
-> next_binding_state
;
4965 /* Try to install the new information. */
4966 if (!supersede_lease (lease
, lt
, 0, 0, 0) ||
4967 !write_lease (lease
)) {
4968 message
= "database update failed";
4970 dhcp_failover_send_bind_ack (state
, msg
, reason
, message
);
4973 dhcp_failover_queue_ack (state
, msg
);
4976 /* If it is probably wise, assign lease to backup state if the peer
4977 * is not already hoarding leases.
4979 if (send_to_backup
&& secondary_not_hoarding(state
, lease
->pool
)) {
4980 lease
->next_binding_state
= FTS_BACKUP
;
4981 lease
->tstp
= cur_time
;
4982 lease
->starts
= cur_time
;
4984 if (!supersede_lease(lease
, NULL
, 0, 1, 0) ||
4985 !write_lease(lease
))
4986 log_error("can't commit lease %s for mac addr "
4987 "affinity", piaddr(lease
->ip_addr
));
4989 dhcp_failover_send_updates(state
);
4994 lease_dereference (<
, MDL
);
4996 lease_dereference (&lease
, MDL
);
4998 return ISC_R_SUCCESS
;
5001 /* This was hairy enough I didn't want to do it all in an if statement.
5003 * Returns: Truth is the secondary is allowed to get more leases based upon
5004 * MAC address affinity. False otherwise.
5007 secondary_not_hoarding(dhcp_failover_state_t
*state
, struct pool
*p
) {
5012 total
= p
->free_leases
+ p
->backup_leases
;
5014 /* How many leases is one side or the other allowed to "hold"? */
5015 hold
= ((total
* state
->max_lease_ownership
) + 50) / 100;
5017 /* If we were to send leases (or if the secondary were to send us
5018 * leases in the negative direction), how many would that be?
5020 lts
= (p
->free_leases
- p
->backup_leases
) / 2;
5022 /* The peer is not hoarding leases if we would send them more leases
5023 * (or they would take fewer leases) than the maximum they are allowed
5024 * to hold (the negative hold).
5026 return(lts
> -hold
);
5029 isc_result_t
dhcp_failover_process_bind_ack (dhcp_failover_state_t
*state
,
5030 failover_message_t
*msg
)
5032 struct lease
*lt
= (struct lease
*)0;
5033 struct lease
*lease
= (struct lease
*)0;
5035 const char *message
= "no memory";
5036 u_int32_t pot_expire
;
5037 int send_to_backup
= ISC_FALSE
;
5039 ia
.len
= sizeof msg
-> assigned_addr
;
5040 memcpy (ia
.iabuf
, &msg
-> assigned_addr
, ia
.len
);
5042 if (!find_lease_by_ip_addr (&lease
, ia
, MDL
)) {
5043 message
= "no such lease";
5047 /* XXX check for conflicts. */
5048 if (msg
-> options_present
& FTB_REJECT_REASON
) {
5049 log_error ("bind update on %s from %s rejected: %.*s",
5050 piaddr (ia
), state
-> name
,
5051 (int)((msg
-> options_present
& FTB_MESSAGE
)
5052 ? msg
-> message
.count
5053 : strlen (dhcp_failover_reject_reason_print
5054 (msg
-> reject_reason
))),
5055 (msg
-> options_present
& FTB_MESSAGE
)
5056 ? (const char *)(msg
-> message
.data
)
5057 : (dhcp_failover_reject_reason_print
5058 (msg
-> reject_reason
)));
5062 /* Silently discard acks for leases we did not update (or multiple
5065 if (!lease
->last_xid
)
5068 if (lease
->last_xid
!= msg
->xid
) {
5069 message
= "xid mismatch";
5073 /* XXX Times may need to be adjusted based on clock skew! */
5074 if (msg
->options_present
& FTO_POTENTIAL_EXPIRY
)
5075 pot_expire
= msg
->potential_expiry
;
5077 pot_expire
= lease
->tstp
;
5079 /* If the lease was desired to enter a binding state, we set
5080 * such a value upon transmitting a bndupd. We do not clear it
5081 * if we receive a bndupd in the meantime (or change the state
5082 * of the lease again ourselves), but we do set binding_state
5083 * if we get a bndupd.
5085 * So desired_binding_state tells us what we sent a bndupd for,
5086 * and binding_state tells us what we have since determined in
5089 if (lease
->desired_binding_state
== FTS_EXPIRED
||
5090 lease
->desired_binding_state
== FTS_RESET
||
5091 lease
->desired_binding_state
== FTS_RELEASED
)
5093 /* It is not a problem to do this directly as we call
5094 * supersede_lease immediately after: the lease is requeued
5095 * even if its sort order (tsfp) has changed.
5097 lease
->atsfp
= lease
->tsfp
= pot_expire
;
5098 if ((state
->i_am
== secondary
) &&
5099 (lease
->flags
& RESERVED_LEASE
))
5100 lease
->next_binding_state
= FTS_BACKUP
;
5102 lease
->next_binding_state
= FTS_FREE
;
5103 /* Clear this condition for the next go-round. */
5104 lease
->desired_binding_state
= lease
->next_binding_state
;
5105 supersede_lease(lease
, (struct lease
*)0, 0, 0, 0);
5108 /* Lease has returned to FREE state from the
5109 * transitional states. If the lease 'belongs'
5110 * to a client that would be served by the
5111 * peer, process a binding update now to send
5112 * the lease to backup state. But not if we
5113 * think we already have.
5115 if (state
->i_am
== primary
&&
5116 !(lease
->flags
& (RESERVED_LEASE
| BOOTP_LEASE
)) &&
5117 peer_wants_lease(lease
))
5118 send_to_backup
= ISC_TRUE
;
5120 if (!send_to_backup
&& state
->me
.state
== normal
)
5123 /* XXX It could be a problem to do this directly if the lease
5124 * XXX is sorted by tsfp.
5126 lease
->atsfp
= lease
->tsfp
= pot_expire
;
5127 if (lease
->desired_binding_state
!= lease
->binding_state
) {
5128 lease
->next_binding_state
=
5129 lease
->desired_binding_state
;
5130 supersede_lease(lease
,
5131 (struct lease
*)0, 0, 0, 0);
5134 /* Commit the lease only after a two-second timeout,
5135 so that if we get a bunch of acks in quick
5136 successtion (e.g., when stealing leases from the
5137 secondary), we do not do an immediate commit for
5139 add_timeout(cur_time
+ 2,
5140 commit_leases_timeout
, (void *)0, 0, 0);
5144 dhcp_failover_ack_queue_remove (state
, lease
);
5146 /* If we are supposed to send an update done after we send
5147 this lease, go ahead and send it. */
5148 if (state
-> send_update_done
== lease
) {
5149 lease_dereference (&state
-> send_update_done
, MDL
);
5150 dhcp_failover_send_update_done (state
);
5153 /* Now that the lease is off the ack queue, consider putting it
5154 * back on the update queue for mac address affinity.
5156 if (send_to_backup
&& secondary_not_hoarding(state
, lease
->pool
)) {
5157 lease
->next_binding_state
= FTS_BACKUP
;
5158 lease
->tstp
= lease
->starts
= cur_time
;
5160 if (!supersede_lease(lease
, NULL
, 0, 1, 0) ||
5161 !write_lease(lease
))
5162 log_error("can't commit lease %s for "
5163 "client affinity", piaddr(lease
->ip_addr
));
5165 if (state
->me
.state
== normal
)
5169 /* If there are updates pending, we've created space to send at
5171 dhcp_failover_send_updates (state
);
5174 lease_dereference (&lease
, MDL
);
5176 lease_dereference (<
, MDL
);
5178 return ISC_R_SUCCESS
;
5181 log_info ("bind update on %s got ack from %s: %s.",
5182 piaddr (ia
), state
-> name
, message
);
5186 isc_result_t
dhcp_failover_generate_update_queue (dhcp_failover_state_t
*state
,
5189 struct shared_network
*s
;
5191 struct lease
*l
, *n
;
5193 #define FREE_LEASES 0
5194 #define ACTIVE_LEASES 1
5195 #define EXPIRED_LEASES 2
5196 #define ABANDONED_LEASES 3
5197 #define BACKUP_LEASES 4
5198 #define RESERVED_LEASES 5
5199 struct lease
**lptr
[RESERVED_LEASES
+1];
5201 /* Loop through each pool in each shared network and call the
5202 expiry routine on the pool. */
5203 for (s
= shared_networks
; s
; s
= s
-> next
) {
5204 for (p
= s
-> pools
; p
; p
= p
-> next
) {
5205 if (p
->failover_peer
!= state
)
5208 lptr
[FREE_LEASES
] = &p
->free
;
5209 lptr
[ACTIVE_LEASES
] = &p
->active
;
5210 lptr
[EXPIRED_LEASES
] = &p
->expired
;
5211 lptr
[ABANDONED_LEASES
] = &p
->abandoned
;
5212 lptr
[BACKUP_LEASES
] = &p
->backup
;
5213 lptr
[RESERVED_LEASES
] = &p
->reserved
;
5215 for (i
= FREE_LEASES
; i
<= RESERVED_LEASES
; i
++) {
5216 for (l
= *(lptr
[i
]); l
; l
= l
-> next
) {
5217 if ((l
->flags
& ON_QUEUE
) == 0 &&
5219 (l
->tstp
> l
->atsfp
) ||
5220 (i
== EXPIRED_LEASES
))) {
5221 l
-> desired_binding_state
= l
-> binding_state
;
5222 dhcp_failover_queue_update (l
, 0);
5228 return ISC_R_SUCCESS
;
5232 dhcp_failover_process_update_request (dhcp_failover_state_t
*state
,
5233 failover_message_t
*msg
)
5235 if (state
->send_update_done
) {
5236 log_info("Received update request while old update still "
5237 "flying! Silently discarding old request.");
5238 lease_dereference(&state
->send_update_done
, MDL
);
5241 /* Generate a fresh update queue. */
5242 dhcp_failover_generate_update_queue (state
, 0);
5244 state
->updxid
= msg
->xid
;
5246 /* If there's anything on the update queue (there shouldn't be
5247 anything on the ack queue), trigger an update done message
5248 when we get an ack for that lease. */
5249 if (state
-> update_queue_tail
) {
5250 lease_reference (&state
-> send_update_done
,
5251 state
-> update_queue_tail
, MDL
);
5252 dhcp_failover_send_updates (state
);
5253 log_info ("Update request from %s: sending update",
5256 /* Otherwise, there are no updates to send, so we can
5257 just send an UPDDONE message immediately. */
5258 dhcp_failover_send_update_done (state
);
5259 log_info ("Update request from %s: nothing pending",
5263 return ISC_R_SUCCESS
;
5267 dhcp_failover_process_update_request_all (dhcp_failover_state_t
*state
,
5268 failover_message_t
*msg
)
5270 if (state
->send_update_done
) {
5271 log_info("Received update request while old update still "
5272 "flying! Silently discarding old request.");
5273 lease_dereference(&state
->send_update_done
, MDL
);
5276 /* Generate a fresh update queue that includes every lease. */
5277 dhcp_failover_generate_update_queue (state
, 1);
5279 state
->updxid
= msg
->xid
;
5281 if (state
-> update_queue_tail
) {
5282 lease_reference (&state
-> send_update_done
,
5283 state
-> update_queue_tail
, MDL
);
5284 dhcp_failover_send_updates (state
);
5285 log_info ("Update request all from %s: sending update",
5288 /* This should really never happen, but it could happen
5289 on a server that currently has no leases configured. */
5290 dhcp_failover_send_update_done (state
);
5291 log_info ("Update request all from %s: nothing pending",
5295 return ISC_R_SUCCESS
;
5299 dhcp_failover_process_update_done (dhcp_failover_state_t
*state
,
5300 failover_message_t
*msg
)
5302 log_info ("failover peer %s: peer update completed.",
5305 state
-> curUPD
= 0;
5307 switch (state
-> me
.state
) {
5311 case communications_interrupted
:
5312 case resolution_interrupted
:
5318 break; /* shouldn't happen. */
5320 /* We got the UPDDONE, so we can go into normal state! */
5321 case potential_conflict
:
5322 if (state
->partner
.state
== conflict_done
) {
5323 if (state
->i_am
== secondary
) {
5324 dhcp_failover_set_state (state
, normal
);
5326 log_error("Secondary is in conflict_done "
5327 "state after conflict resolution, "
5328 "this is illegal.");
5329 dhcp_failover_set_state (state
, shut_down
);
5332 if (state
->i_am
== primary
)
5333 dhcp_failover_set_state (state
, conflict_done
);
5335 log_error("Spurious update-done message.");
5341 log_error("Spurious update-done message.");
5345 /* Wait for MCLT to expire before moving to recover_done,
5346 except that if both peers come up in recover, there is
5347 no point in waiting for MCLT to expire - this probably
5348 indicates the initial startup of a newly-configured
5350 if (state
-> me
.stos
+ state
-> mclt
> cur_time
&&
5351 state
-> partner
.state
!= recover
&&
5352 state
-> partner
.state
!= recover_done
) {
5353 dhcp_failover_set_state (state
, recover_wait
);
5354 #if defined (DEBUG_FAILOVER_TIMING)
5355 log_info ("add_timeout +%d %s",
5357 state
-> me
.stos
+ state
-> mclt
),
5358 "dhcp_failover_recover_done");
5360 add_timeout ((int)(state
-> me
.stos
+ state
-> mclt
),
5361 dhcp_failover_recover_done
,
5363 (tvref_t
)omapi_object_reference
,
5365 omapi_object_dereference
);
5367 dhcp_failover_recover_done (state
);
5370 return ISC_R_SUCCESS
;
5373 void dhcp_failover_recover_done (void *sp
)
5375 dhcp_failover_state_t
*state
= sp
;
5377 #if defined (DEBUG_FAILOVER_TIMING)
5378 log_info ("dhcp_failover_recover_done");
5381 dhcp_failover_set_state (state
, recover_done
);
5384 #if defined (DEBUG_FAILOVER_MESSAGES)
5385 /* Print hunks of failover messages, doing line breaks as appropriate.
5386 Note that this assumes syslog is being used, rather than, e.g., the
5387 Windows NT logging facility, where just dumping the whole message in
5388 one hunk would be more appropriate. */
5390 void failover_print (char *obuf
,
5391 unsigned *obufix
, unsigned obufmax
, const char *s
)
5393 int len
= strlen (s
);
5395 while (len
+ *obufix
+ 1 >= obufmax
) {
5396 log_debug ("%s", obuf
);
5398 log_debug ("%s", s
);
5404 strcpy (&obuf
[*obufix
], s
);
5407 #endif /* defined (DEBUG_FAILOVER_MESSAGES) */
5409 /* Taken from draft-ietf-dhc-loadb-01.txt: */
5410 /* A "mixing table" of 256 distinct values, in pseudo-random order. */
5411 unsigned char loadb_mx_tbl
[256] = {
5412 251, 175, 119, 215, 81, 14, 79, 191, 103, 49,
5413 181, 143, 186, 157, 0, 232, 31, 32, 55, 60,
5414 152, 58, 17, 237, 174, 70, 160, 144, 220, 90,
5415 57, 223, 59, 3, 18, 140, 111, 166, 203, 196,
5416 134, 243, 124, 95, 222, 179, 197, 65, 180, 48,
5417 36, 15, 107, 46, 233, 130, 165, 30, 123, 161,
5418 209, 23, 97, 16, 40, 91, 219, 61, 100, 10,
5419 210, 109, 250, 127, 22, 138, 29, 108, 244, 67,
5420 207, 9, 178, 204, 74, 98, 126, 249, 167, 116,
5421 34, 77, 193, 200, 121, 5, 20, 113, 71, 35,
5422 128, 13, 182, 94, 25, 226, 227, 199, 75, 27,
5423 41, 245, 230, 224, 43, 225, 177, 26, 155, 150,
5424 212, 142, 218, 115, 241, 73, 88, 105, 39, 114,
5425 62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
5426 154, 122, 12, 84, 82, 163, 44, 139, 228, 236,
5427 205, 242, 217, 11, 187, 146, 159, 64, 86, 239,
5428 195, 42, 106, 198, 118, 112, 184, 172, 87, 2,
5429 173, 117, 176, 229, 247, 253, 137, 185, 99, 164,
5430 102, 147, 45, 66, 231, 52, 141, 211, 194, 206,
5431 246, 238, 56, 110, 78, 248, 63, 240, 189, 93,
5432 92, 51, 53, 183, 19, 171, 72, 50, 33, 104,
5433 101, 69, 8, 252, 83, 120, 76, 135, 85, 54,
5434 202, 125, 188, 213, 96, 235, 136, 208, 162, 129,
5435 190, 132, 156, 38, 47, 1, 7, 254, 24, 4,
5436 216, 131, 89, 21, 28, 133, 37, 153, 149, 80,
5437 170, 68, 6, 169, 234, 151 };
5439 static unsigned char loadb_p_hash (const unsigned char *, unsigned);
5440 static unsigned char loadb_p_hash (const unsigned char *key
, unsigned len
)
5442 unsigned char hash
= len
;
5444 for(i
= len
; i
> 0; )
5445 hash
= loadb_mx_tbl
[hash
^ (key
[--i
])];
5449 int load_balance_mine (struct packet
*packet
, dhcp_failover_state_t
*state
)
5451 struct option_cache
*oc
;
5452 struct data_string ds
;
5453 unsigned char hbaix
;
5456 if (state
-> load_balance_max_secs
< ntohs (packet
-> raw
-> secs
)) {
5460 /* If we don't have a hash bucket array, we can't tell if this
5461 one's ours, so we assume it's not. */
5465 oc
= lookup_option (&dhcp_universe
, packet
-> options
,
5466 DHO_DHCP_CLIENT_IDENTIFIER
);
5467 memset (&ds
, 0, sizeof ds
);
5469 evaluate_option_cache (&ds
, packet
, (struct lease
*)0,
5470 (struct client_state
*)0,
5471 packet
-> options
, (struct option_state
*)0,
5472 &global_scope
, oc
, MDL
)) {
5473 hbaix
= loadb_p_hash (ds
.data
, ds
.len
);
5475 hbaix
= loadb_p_hash (packet
-> raw
-> chaddr
,
5476 packet
-> raw
-> hlen
);
5479 hm
= state
->hba
[(hbaix
>> 3) & 0x1F] & (1 << (hbaix
& 0x07));
5481 if (state
-> i_am
== primary
)
5487 /* The inverse of load_balance_mine ("load balance theirs"). We can't
5488 * use the regular load_balance_mine() and invert it because of the case
5489 * where there might not be an HBA, and we want to indicate false here
5490 * in this case only.
5493 peer_wants_lease(struct lease
*lp
)
5495 dhcp_failover_state_t
*state
;
5496 unsigned char hbaix
;
5502 state
= lp
->pool
->failover_peer
;
5504 if (!state
|| !state
->hba
)
5508 hbaix
= loadb_p_hash (lp
->uid
, lp
->uid_len
);
5510 hbaix
= loadb_p_hash (lp
->hardware_addr
.hbuf
,
5511 lp
->hardware_addr
.hlen
);
5513 hm
= state
->hba
[(hbaix
>> 3) & 0x1F] & (1 << (hbaix
& 0x07));
5515 if (state
->i_am
== primary
)
5521 /* This deals with what to do with bind updates when
5522 we're in the normal state
5524 Note that tsfp had better be set from the latest bind update
5525 _before_ this function is called! */
5528 normal_binding_state_transition_check (struct lease
*lease
,
5529 dhcp_failover_state_t
*state
,
5530 binding_state_t binding_state
,
5533 binding_state_t new_state
;
5535 /* If there is no transition, it's no problem. */
5536 if (binding_state
== lease
-> binding_state
)
5537 return binding_state
;
5539 switch (lease
-> binding_state
) {
5542 switch (binding_state
) {
5549 /* If the lease was free, and our peer is primary,
5550 then it can make it active, or abandoned, or
5551 backup. Abandoned is treated like free in
5553 if (state
-> i_am
== secondary
)
5554 return binding_state
;
5556 /* Otherwise, it can't legitimately do any sort of
5557 state transition. Because the lease was free,
5558 and the error has already been made, we allow the
5559 peer to change its state anyway, but log a warning
5560 message in hopes that the error will be fixed. */
5561 case FTS_FREE
: /* for compiler */
5562 new_state
= binding_state
;
5566 log_fatal ("Impossible case at %s:%d.", MDL
);
5570 /* The secondary can't change the state of an active
5572 if (state
-> i_am
== primary
) {
5573 /* Except that the client may send the DHCPRELEASE
5574 to the secondary, and we have to accept that. */
5575 if (binding_state
== FTS_RELEASED
)
5576 return binding_state
;
5577 new_state
= lease
-> binding_state
;
5581 /* So this is only for transitions made by the primary: */
5582 switch (binding_state
) {
5585 /* Can't set a lease to free or backup until the
5586 peer agrees that it's expired. */
5587 if (tsfp
> cur_time
) {
5588 new_state
= lease
-> binding_state
;
5591 return binding_state
;
5594 /* XXX 65 should be the clock skew between the peers
5595 XXX plus a fudge factor. This code will result
5596 XXX in problems if MCLT is really short or the
5597 XXX max-lease-time is really short (less than the
5598 XXX fudge factor. */
5599 if (lease
-> ends
- 65 > cur_time
) {
5600 new_state
= lease
-> binding_state
;
5608 return binding_state
;
5611 log_fatal ("Impossible case at %s:%d.", MDL
);
5616 switch (binding_state
) {
5619 /* Can't set a lease to free or backup until the
5620 peer agrees that it's expired. */
5621 if (tsfp
> cur_time
) {
5622 new_state
= lease
-> binding_state
;
5625 return binding_state
;
5632 return binding_state
;
5635 log_fatal ("Impossible case at %s:%d.", MDL
);
5639 switch (binding_state
) {
5643 /* These are invalid state transitions - should we
5650 return binding_state
;
5653 log_fatal ("Impossible case at %s:%d.", MDL
);
5657 switch (binding_state
) {
5660 /* Can't set a lease to free or backup until the
5661 peer agrees that it's expired. */
5662 if (tsfp
> cur_time
) {
5663 new_state
= lease
-> binding_state
;
5666 return binding_state
;
5673 return binding_state
;
5676 log_fatal ("Impossible case at %s:%d.", MDL
);
5680 switch (binding_state
) {
5686 /* If the lease was in backup, and our peer
5687 is secondary, then it can make it active
5689 if (state
-> i_am
== primary
)
5690 return binding_state
;
5692 /* Either the primary or the secondary can
5693 reasonably move a lease from the backup
5694 state to the free state. */
5696 return binding_state
;
5699 new_state
= lease
-> binding_state
;
5703 log_fatal ("Impossible case at %s:%d.", MDL
);
5708 log_fatal ("Impossible case at %s:%d.", MDL
);
5715 /* Determine whether the state transition is okay when we're potentially
5716 in conflict with the peer. */
5718 conflict_binding_state_transition_check (struct lease
*lease
,
5719 dhcp_failover_state_t
*state
,
5720 binding_state_t binding_state
,
5723 binding_state_t new_state
;
5725 /* If there is no transition, it's no problem. */
5726 if (binding_state
== lease
-> binding_state
)
5727 new_state
= binding_state
;
5729 switch (lease
-> binding_state
) {
5730 /* If we think the lease is not in use, then the
5731 state into which the partner put it is just fine,
5739 new_state
= binding_state
;
5742 /* If we think the lease *is* in use, then we're not
5743 going to take the partner's change if the partner
5744 thinks it's free. */
5746 switch (binding_state
) {
5749 new_state
= lease
-> binding_state
;
5753 /* If we don't agree about expiry, it's
5754 * invalid. 65 should allow for max
5755 * clock skew (60) plus some fudge.
5756 * XXX: should we refetch cur_time?
5758 if ((lease
->ends
- 65) > cur_time
)
5759 new_state
= lease
->binding_state
;
5761 new_state
= binding_state
;
5764 /* RELEASED, RESET, and ABANDONED indicate
5765 * that our partner has information about
5766 * this lease that we did not witness. Our
5772 new_state
= binding_state
;
5776 log_fatal ("Impossible case at %s:%d.", MDL
);
5782 log_fatal ("Impossible case at %s:%d.", MDL
);
5789 /* We can reallocate a lease under the following circumstances:
5791 (1) It belongs to us - it's FTS_FREE, and we're primary, or it's
5792 FTS_BACKUP, and we're secondary.
5793 (2) We're in partner_down, and the lease is not active, and we
5794 can be sure that the other server didn't make it active.
5795 We can only be sure that the server didn't make it active
5796 when we are in the partner_down state and one of the following
5797 two conditions holds:
5798 (a) in the case that the time sent from the peer is earlier than
5799 the time we entered the partner_down state, at least MCLT has
5800 gone by since we entered partner_down, or
5801 (b) in the case that the time sent from the peer is later than
5802 the time when we entered partner_down, the current time is
5803 later than the time sent from the peer by at least MCLT. */
5805 int lease_mine_to_reallocate (struct lease
*lease
)
5807 dhcp_failover_state_t
*peer
;
5809 if (lease
&& lease
->pool
&&
5810 (peer
= lease
->pool
->failover_peer
)) {
5811 switch (lease
->binding_state
) {
5813 /* ACTIVE leases may not be reallocated. */
5818 /* FREE leases may only be allocated by the primary,
5819 * unless the secondary is acting in partner_down
5820 * state and stos+mclt or tsfp+mclt has expired,
5821 * whichever is greater.
5823 * ABANDONED are treated the same as FREE for all
5824 * purposes here. Note that servers will only try
5825 * for ABANDONED leases as a last resort anyway.
5827 if (peer
-> i_am
== primary
)
5830 return(peer
->service_state
== service_partner_down
&&
5831 ((lease
->tsfp
< peer
->me
.stos
) ?
5832 (peer
->me
.stos
+ peer
->mclt
< cur_time
) :
5833 (lease
->tsfp
+ peer
->mclt
< cur_time
)));
5838 /* These three lease states go onto the 'expired'
5839 * queue. Upon entry into partner-down state, this
5840 * queue of leases has their tsfp values modified
5841 * to equal stos+mclt, the point at which the server
5842 * is allowed to remove them from these transitional
5843 * states without an acknowledgement.
5845 * Note that although tsfp has been possibly extended
5846 * past the actual tsfp we received from the peer, we
5847 * don't have to take any special action. Since tsfp
5848 * is now in the past (or now), we can guarantee that
5849 * this server will only allocate a lease time equal
5850 * to MCLT, rather than a TSFP-optimal lease, which is
5851 * the only danger for a lease in one of these states.
5853 return((peer
->service_state
== service_partner_down
) &&
5854 (lease
->tsfp
< cur_time
));
5857 /* Only the secondary may allocate BACKUP leases,
5858 * unless in partner_down state in which case at
5859 * least TSFP+MCLT or STOS+MCLT must have expired,
5860 * whichever is greater.
5862 if (peer
->i_am
== secondary
)
5865 return((peer
->service_state
== service_partner_down
) &&
5866 ((lease
->tsfp
< peer
->me
.stos
) ?
5867 (peer
->me
.stos
+ peer
->mclt
< cur_time
) :
5868 (lease
->tsfp
+ peer
->mclt
< cur_time
)));
5871 /* All lease states appear above. */
5872 log_fatal("Impossible case at %s:%d.", MDL
);
5878 return(lease
->binding_state
== FTS_FREE
||
5879 lease
->binding_state
== FTS_BACKUP
);
5884 static isc_result_t
failover_message_reference (failover_message_t
**mp
,
5885 failover_message_t
*m
,
5886 const char *file
, int line
)
5890 return ISC_R_SUCCESS
;
5893 static isc_result_t
failover_message_dereference (failover_message_t
**mp
,
5894 const char *file
, int line
)
5896 failover_message_t
*m
;
5899 if (m
-> refcnt
== 0) {
5901 failover_message_dereference (&m
-> next
,
5903 if (m
-> chaddr
.data
)
5904 dfree (m
-> chaddr
.data
, file
, line
);
5905 if (m
-> client_identifier
.data
)
5906 dfree (m
-> client_identifier
.data
, file
, line
);
5908 dfree (m
-> hba
.data
, file
, line
);
5909 if (m
-> message
.data
)
5910 dfree (m
-> message
.data
, file
, line
);
5911 if (m
-> reply_options
.data
)
5912 dfree (m
-> reply_options
.data
, file
, line
);
5913 if (m
-> request_options
.data
)
5914 dfree (m
-> request_options
.data
, file
, line
);
5915 if (m
-> vendor_class
.data
)
5916 dfree (m
-> vendor_class
.data
, file
, line
);
5917 if (m
-> vendor_options
.data
)
5918 dfree (m
-> vendor_options
.data
, file
, line
);
5920 dfree (m
-> ddns
.data
, file
, line
);
5921 dfree (*mp
, file
, line
);
5924 return ISC_R_SUCCESS
;
5927 OMAPI_OBJECT_ALLOC (dhcp_failover_state
, dhcp_failover_state_t
,
5928 dhcp_type_failover_state
)
5929 OMAPI_OBJECT_ALLOC (dhcp_failover_listener
, dhcp_failover_listener_t
,
5930 dhcp_type_failover_listener
)
5931 OMAPI_OBJECT_ALLOC (dhcp_failover_link
, dhcp_failover_link_t
,
5932 dhcp_type_failover_link
)
5933 #endif /* defined (FAILOVER_PROTOCOL) */
5935 const char *binding_state_print (enum failover_state state
)