3 Failover protocol support code... */
6 * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1999-2003 by Internet Software Consortium
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 * Internet Systems Consortium, Inc.
23 * Redwood City, CA 94063
27 * This software has been written for Internet Systems Consortium
28 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29 * To learn more about Internet Systems Consortium, see
30 * ``http://www.isc.org/''. To learn more about Vixie Enterprises,
31 * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
32 * ``http://www.nominum.com''.
36 #include <omapip/omapip_p.h>
38 #if defined (FAILOVER_PROTOCOL)
39 dhcp_failover_state_t
*failover_states
;
40 static isc_result_t
do_a_failover_option (omapi_object_t
*,
41 dhcp_failover_link_t
*);
42 dhcp_failover_listener_t
*failover_listeners
;
44 static isc_result_t
failover_message_reference (failover_message_t
**,
46 const char *file
, int line
);
47 static isc_result_t
failover_message_dereference (failover_message_t
**,
48 const char *file
, int line
);
50 static void dhcp_failover_pool_balance(dhcp_failover_state_t
*state
);
51 static void dhcp_failover_pool_reqbalance(dhcp_failover_state_t
*state
);
52 static int dhcp_failover_pool_dobalance(dhcp_failover_state_t
*state
);
53 static INLINE
int secondary_not_hoarding(dhcp_failover_state_t
*state
,
57 void dhcp_failover_startup ()
59 dhcp_failover_state_t
*state
;
62 for (state
= failover_states
; state
; state
= state
-> next
) {
63 dhcp_failover_state_transition (state
, "startup");
65 if (state
-> pool_count
== 0) {
66 log_error ("failover peer declaration with no %s",
68 log_error ("In order to use failover, you MUST %s",
69 "refer to your main failover declaration");
70 log_error ("in each pool declaration. You MUST %s",
71 "NOT use range declarations outside");
72 log_fatal ("of pool declarations.");
74 /* In case the peer is already running, immediately try
75 to establish a connection with it. */
76 status
= dhcp_failover_link_initiate ((omapi_object_t
*)state
);
77 if (status
!= ISC_R_SUCCESS
&& status
!= ISC_R_INCOMPLETE
) {
78 #if defined (DEBUG_FAILOVER_TIMING)
79 log_info ("add_timeout +90 dhcp_failover_reconnect");
81 add_timeout (cur_time
+ 90,
82 dhcp_failover_reconnect
, state
,
84 dhcp_failover_state_reference
,
86 dhcp_failover_state_dereference
);
87 log_error ("failover peer %s: %s", state
-> name
,
88 isc_result_totext (status
));
91 status
= (dhcp_failover_listen
92 ((omapi_object_t
*)state
));
93 if (status
!= ISC_R_SUCCESS
) {
94 #if defined (DEBUG_FAILOVER_TIMING)
95 log_info ("add_timeout +90 %s",
96 "dhcp_failover_listener_restart");
98 add_timeout (cur_time
+ 90,
99 dhcp_failover_listener_restart
,
101 (tvref_t
)omapi_object_reference
,
102 (tvunref_t
)omapi_object_dereference
);
107 int dhcp_failover_write_all_states ()
109 dhcp_failover_state_t
*state
;
111 for (state
= failover_states
; state
; state
= state
-> next
) {
112 if (!write_failover_state (state
))
118 isc_result_t
enter_failover_peer (peer
)
119 dhcp_failover_state_t
*peer
;
121 dhcp_failover_state_t
*dup
= (dhcp_failover_state_t
*)0;
124 status
= find_failover_peer (&dup
, peer
-> name
, MDL
);
125 if (status
== ISC_R_NOTFOUND
) {
126 if (failover_states
) {
127 dhcp_failover_state_reference (&peer
-> next
,
128 failover_states
, MDL
);
129 dhcp_failover_state_dereference (&failover_states
,
132 dhcp_failover_state_reference (&failover_states
, peer
, MDL
);
133 return ISC_R_SUCCESS
;
135 dhcp_failover_state_dereference (&dup
, MDL
);
136 if (status
== ISC_R_SUCCESS
)
141 isc_result_t
find_failover_peer (peer
, name
, file
, line
)
142 dhcp_failover_state_t
**peer
;
147 dhcp_failover_state_t
*p
;
149 for (p
= failover_states
; p
; p
= p
-> next
)
150 if (!strcmp (name
, p
-> name
))
153 return dhcp_failover_state_reference (peer
, p
, file
, line
);
154 return ISC_R_NOTFOUND
;
157 /* The failover protocol has three objects associated with it. For
158 each failover partner declaration in the dhcpd.conf file, primary
159 or secondary, there is a failover_state object. For any primary or
160 secondary state object that has a connection to its peer, there is
161 also a failover_link object, which has its own input state separate
162 from the failover protocol state for managing the actual bytes
163 coming in off the wire. Finally, there will be one listener object
164 for every distinct port number associated with a secondary
165 failover_state object. Normally all secondary failover_state
166 objects are expected to listen on the same port number, so there
167 need be only one listener object, but if different port numbers are
168 specified for each failover object, there could be as many as one
169 listener object for each secondary failover_state object. */
171 /* This, then, is the implemention of the failover link object. */
173 isc_result_t
dhcp_failover_link_initiate (omapi_object_t
*h
)
176 dhcp_failover_link_t
*obj
;
177 dhcp_failover_state_t
*state
;
180 struct data_string ds
;
181 omapi_addr_list_t
*addrs
= (omapi_addr_list_t
*)0;
182 omapi_addr_t local_addr
;
184 /* Find the failover state in the object chain. */
185 for (o
= h
; o
-> outer
; o
= o
-> outer
)
187 for (; o
; o
= o
-> inner
) {
188 if (o
-> type
== dhcp_type_failover_state
)
192 return ISC_R_INVALIDARG
;
193 state
= (dhcp_failover_state_t
*)o
;
195 obj
= (dhcp_failover_link_t
*)0;
196 status
= dhcp_failover_link_allocate (&obj
, MDL
);
197 if (status
!= ISC_R_SUCCESS
)
199 option_cache_reference (&obj
-> peer_address
,
200 state
-> partner
.address
, MDL
);
201 obj
-> peer_port
= state
-> partner
.port
;
202 dhcp_failover_state_reference (&obj
-> state_object
, state
, MDL
);
204 memset (&ds
, 0, sizeof ds
);
205 if (!evaluate_option_cache (&ds
, (struct packet
*)0, (struct lease
*)0,
206 (struct client_state
*)0,
207 (struct option_state
*)0,
208 (struct option_state
*)0,
209 &global_scope
, obj
-> peer_address
, MDL
)) {
210 dhcp_failover_link_dereference (&obj
, MDL
);
211 return ISC_R_UNEXPECTED
;
214 /* Make an omapi address list out of a buffer containing zero or more
216 status
= omapi_addr_list_new (&addrs
, ds
.len
/ 4, MDL
);
217 if (status
!= ISC_R_SUCCESS
) {
218 dhcp_failover_link_dereference (&obj
, MDL
);
222 for (i
= 0; i
< addrs
-> count
; i
++) {
223 addrs
-> addresses
[i
].addrtype
= AF_INET
;
224 addrs
-> addresses
[i
].addrlen
= sizeof (struct in_addr
);
225 memcpy (addrs
-> addresses
[i
].address
,
226 &ds
.data
[i
* 4], sizeof (struct in_addr
));
227 addrs
-> addresses
[i
].port
= obj
-> peer_port
;
229 data_string_forget (&ds
, MDL
);
231 /* Now figure out the local address that we're supposed to use. */
232 if (!state
-> me
.address
||
233 !evaluate_option_cache (&ds
, (struct packet
*)0,
235 (struct client_state
*)0,
236 (struct option_state
*)0,
237 (struct option_state
*)0,
238 &global_scope
, state
-> me
.address
,
240 memset (&local_addr
, 0, sizeof local_addr
);
241 local_addr
.addrtype
= AF_INET
;
242 local_addr
.addrlen
= sizeof (struct in_addr
);
243 if (!state
-> server_identifier
.len
) {
244 log_fatal ("failover peer %s: no local address.",
248 if (ds
.len
!= sizeof (struct in_addr
)) {
249 data_string_forget (&ds
, MDL
);
250 dhcp_failover_link_dereference (&obj
, MDL
);
251 omapi_addr_list_dereference (&addrs
, MDL
);
252 return ISC_R_INVALIDARG
;
254 local_addr
.addrtype
= AF_INET
;
255 local_addr
.addrlen
= ds
.len
;
256 memcpy (local_addr
.address
, ds
.data
, ds
.len
);
257 if (!state
-> server_identifier
.len
)
258 data_string_copy (&state
-> server_identifier
,
260 data_string_forget (&ds
, MDL
);
261 local_addr
.port
= 0; /* Let the O.S. choose. */
264 status
= omapi_connect_list ((omapi_object_t
*)obj
,
266 omapi_addr_list_dereference (&addrs
, MDL
);
268 dhcp_failover_link_dereference (&obj
, MDL
);
272 isc_result_t
dhcp_failover_link_signal (omapi_object_t
*h
,
273 const char *name
, va_list ap
)
276 dhcp_failover_link_t
*link
;
278 dhcp_failover_state_t
*s
, *state
= (dhcp_failover_state_t
*)0;
282 if (h
-> type
!= dhcp_type_failover_link
) {
283 /* XXX shouldn't happen. Put an assert here? */
284 return ISC_R_UNEXPECTED
;
286 link
= (dhcp_failover_link_t
*)h
;
288 if (!strcmp (name
, "connect")) {
289 if (link
-> state_object
-> i_am
== primary
) {
290 status
= dhcp_failover_send_connect (h
);
291 if (status
!= ISC_R_SUCCESS
) {
292 log_info ("dhcp_failover_send_connect: %s",
293 isc_result_totext (status
));
294 omapi_disconnect (h
-> outer
, 1);
297 status
= ISC_R_SUCCESS
;
298 /* Allow the peer fifteen seconds to send us a
300 #if defined (DEBUG_FAILOVER_TIMING)
301 log_info ("add_timeout +15 %s",
302 "dhcp_failover_link_startup_timeout");
304 add_timeout (cur_time
+ 15,
305 dhcp_failover_link_startup_timeout
,
307 (tvref_t
)dhcp_failover_link_reference
,
308 (tvunref_t
)dhcp_failover_link_dereference
);
312 if (!strcmp (name
, "disconnect")) {
313 if (link
-> state_object
) {
314 dhcp_failover_state_reference (&state
,
315 link
-> state_object
, MDL
);
316 link
-> state
= dhcp_flink_disconnected
;
318 /* Make the transition. */
319 if (state
-> link_to_peer
== link
) {
320 dhcp_failover_state_transition (link
-> state_object
,
323 /* Start trying to reconnect. */
324 #if defined (DEBUG_FAILOVER_TIMING)
325 log_info ("add_timeout +5 %s",
326 "dhcp_failover_reconnect");
328 add_timeout (cur_time
+ 5, dhcp_failover_reconnect
,
330 (tvref_t
)dhcp_failover_state_reference
,
331 (tvunref_t
)dhcp_failover_state_dereference
);
333 dhcp_failover_state_dereference (&state
, MDL
);
335 return ISC_R_SUCCESS
;
338 if (!strcmp (name
, "status")) {
339 if (link
-> state_object
) {
342 status
= va_arg(ap
, isc_result_t
);
344 if ((status
== ISC_R_HOSTUNREACH
) || (status
== ISC_R_TIMEDOUT
)) {
345 dhcp_failover_state_reference (&state
,
346 link
-> state_object
, MDL
);
347 link
-> state
= dhcp_flink_disconnected
;
349 /* Make the transition. */
350 dhcp_failover_state_transition (link
-> state_object
,
353 /* Start trying to reconnect. */
354 #if defined (DEBUG_FAILOVER_TIMING)
355 log_info ("add_timeout +5 %s",
356 "dhcp_failover_reconnect");
358 add_timeout (cur_time
+ 5, dhcp_failover_reconnect
,
360 (tvref_t
)dhcp_failover_state_reference
,
361 (tvunref_t
)dhcp_failover_state_dereference
);
363 dhcp_failover_state_dereference (&state
, MDL
);
365 return ISC_R_SUCCESS
;
368 /* Not a signal we recognize? */
369 if (strcmp (name
, "ready")) {
370 if (h
-> inner
&& h
-> inner
-> type
-> signal_handler
)
371 return (*(h
-> inner
-> type
-> signal_handler
))
372 (h
-> inner
, name
, ap
);
373 return ISC_R_NOTFOUND
;
376 if (!h
-> outer
|| h
-> outer
-> type
!= omapi_type_connection
)
377 return ISC_R_INVALIDARG
;
380 /* We get here because we requested that we be woken up after
381 some number of bytes were read, and that number of bytes
382 has in fact been read. */
383 switch (link
-> state
) {
384 case dhcp_flink_start
:
385 link
-> state
= dhcp_flink_message_length_wait
;
386 if ((omapi_connection_require (c
, 2)) != ISC_R_SUCCESS
)
388 case dhcp_flink_message_length_wait
:
390 link
-> state
= dhcp_flink_message_wait
;
391 link
-> imsg
= dmalloc (sizeof (failover_message_t
), MDL
);
393 status
= ISC_R_NOMEMORY
;
396 failover_message_dereference (&link
->imsg
,
399 link
-> state
= dhcp_flink_disconnected
;
400 log_info ("message length wait: %s",
401 isc_result_totext (status
));
402 omapi_disconnect (c
, 1);
403 /* XXX just blow away the protocol state now?
404 XXX or will disconnect blow it away? */
405 return ISC_R_UNEXPECTED
;
407 memset (link
-> imsg
, 0, sizeof (failover_message_t
));
408 link
-> imsg
-> refcnt
= 1;
409 /* Get the length: */
410 omapi_connection_get_uint16 (c
, &link
-> imsg_len
);
411 link
-> imsg_count
= 0; /* Bytes read. */
413 /* Ensure the message is of valid length. */
414 if (link
->imsg_len
< DHCP_FAILOVER_MIN_MESSAGE_SIZE
||
415 link
->imsg_len
> DHCP_FAILOVER_MAX_MESSAGE_SIZE
) {
416 status
= ISC_R_UNEXPECTED
;
417 goto dhcp_flink_fail
;
420 if ((omapi_connection_require (c
, link
-> imsg_len
- 2U)) !=
423 case dhcp_flink_message_wait
:
424 /* Read in the message. At this point we have the
425 entire message in the input buffer. For each
426 incoming value ID, set a bit in the bitmask
427 indicating that we've gotten it. Maybe flag an
428 error message if the bit is already set. Once
429 we're done reading, we can check the bitmask to
430 make sure that the required fields for each message
431 have been included. */
433 link
-> imsg_count
+= 2; /* Count the length as read. */
435 /* Get message type. */
436 omapi_connection_copyout (&link
-> imsg
-> type
, c
, 1);
437 link
-> imsg_count
++;
439 /* Get message payload offset. */
440 omapi_connection_copyout (&link
-> imsg_payoff
, c
, 1);
441 link
-> imsg_count
++;
443 /* Get message time. */
444 omapi_connection_get_uint32 (c
, &link
-> imsg
-> time
);
445 link
-> imsg_count
+= 4;
447 /* Get transaction ID. */
448 omapi_connection_get_uint32 (c
, &link
-> imsg
-> xid
);
449 link
-> imsg_count
+= 4;
451 #if defined (DEBUG_FAILOVER_MESSAGES)
452 log_info ("link: message %s payoff %d time %ld xid %ld",
453 dhcp_failover_message_name (link
-> imsg
-> type
),
455 (unsigned long)link
-> imsg
-> time
,
456 (unsigned long)link
-> imsg
-> xid
);
458 /* Skip over any portions of the message header that we
460 if (link
-> imsg_payoff
- link
-> imsg_count
) {
461 omapi_connection_copyout ((unsigned char *)0, c
,
462 (link
-> imsg_payoff
-
463 link
-> imsg_count
));
464 link
-> imsg_count
= link
-> imsg_payoff
;
467 /* Now start sucking options off the wire. */
468 while (link
-> imsg_count
< link
-> imsg_len
) {
469 status
= do_a_failover_option (c
, link
);
470 if (status
!= ISC_R_SUCCESS
)
471 goto dhcp_flink_fail
;
474 /* If it's a connect message, try to associate it with
476 /* XXX this should be authenticated! */
477 if (link
-> imsg
-> type
== FTM_CONNECT
) {
481 if (!(link
->imsg
->options_present
&
482 FTB_RELATIONSHIP_NAME
)) {
483 errmsg
= "missing relationship-name";
484 reason
= FTR_INVALID_PARTNER
;
488 /* See if we can find a failover_state object that
489 matches this connection. This message should only
490 be received by a secondary from a primary. */
491 for (s
= failover_states
; s
; s
= s
-> next
) {
492 if (dhcp_failover_state_match_by_name(s
,
493 &link
->imsg
->relationship_name
))
497 /* If we can't find a failover protocol state
498 for this remote host, drop the connection */
500 errmsg
= "unknown failover relationship name";
501 reason
= FTR_INVALID_PARTNER
;
504 /* XXX Send a refusal message first?
505 XXX Look in protocol spec for guidance. */
509 slen
= strlen(sname
);
510 } else if (link
->imsg
->options_present
&
511 FTB_RELATIONSHIP_NAME
) {
512 sname
= (char *)link
->imsg
->
513 relationship_name
.data
;
514 slen
= link
->imsg
->relationship_name
.count
;
517 slen
= strlen(sname
);
520 log_error("Failover CONNECT from %.*s: %s",
521 slen
, sname
, errmsg
);
522 dhcp_failover_send_connectack
523 ((omapi_object_t
*)link
, state
,
525 log_info ("failover: disconnect: %s", errmsg
);
526 omapi_disconnect (c
, 0);
527 link
-> state
= dhcp_flink_disconnected
;
528 return ISC_R_SUCCESS
;
531 if ((cur_time
> link
-> imsg
-> time
&&
532 cur_time
- link
-> imsg
-> time
> 60) ||
533 (cur_time
< link
-> imsg
-> time
&&
534 link
-> imsg
-> time
- cur_time
> 60)) {
535 errmsg
= "time offset too large";
536 reason
= FTR_TIMEMISMATCH
;
540 if (!(link
-> imsg
-> options_present
& FTB_HBA
) ||
541 link
-> imsg
-> hba
.count
!= 32) {
542 errmsg
= "invalid HBA";
543 reason
= FTR_HBA_CONFLICT
; /* XXX */
547 dfree (state
-> hba
, MDL
);
548 state
-> hba
= dmalloc (32, MDL
);
550 errmsg
= "no memory";
551 reason
= FTR_MISC_REJECT
;
554 memcpy (state
-> hba
, link
-> imsg
-> hba
.data
, 32);
556 if (!link
-> state_object
)
557 dhcp_failover_state_reference
558 (&link
-> state_object
, state
, MDL
);
559 if (!link
-> peer_address
)
560 option_cache_reference
561 (&link
-> peer_address
,
562 state
-> partner
.address
, MDL
);
565 /* If we don't have a state object at this point, it's
566 some kind of bogus situation, so just drop the
568 if (!link
-> state_object
) {
569 log_info ("failover: connect: no matching state.");
570 omapi_disconnect (c
, 1);
571 link
-> state
= dhcp_flink_disconnected
;
572 return ISC_R_INVALIDARG
;
575 /* Once we have the entire message, and we've validated
576 it as best we can here, pass it to the parent. */
577 omapi_signal ((omapi_object_t
*)link
-> state_object
,
579 link
-> state
= dhcp_flink_message_length_wait
;
580 failover_message_dereference (&link
-> imsg
, MDL
);
581 /* XXX This is dangerous because we could get into a tight
582 XXX loop reading input without servicing any other stuff.
583 XXX There needs to be a way to relinquish control but
584 XXX get it back immediately if there's no other work to
586 if ((omapi_connection_require (c
, 2)) == ISC_R_SUCCESS
)
591 log_fatal("Impossible case at %s:%d.", MDL
);
594 return ISC_R_SUCCESS
;
597 static isc_result_t
do_a_failover_option (c
, link
)
599 dhcp_failover_link_t
*link
;
601 u_int16_t option_code
;
602 u_int16_t option_len
;
608 if (link
-> imsg_count
+ 2 > link
-> imsg_len
) {
609 log_error ("FAILOVER: message overflow at option code.");
610 return ISC_R_PROTOCOLERROR
;
613 /* Get option code. */
614 omapi_connection_get_uint16 (c
, &option_code
);
615 link
-> imsg_count
+= 2;
617 if (link
-> imsg_count
+ 2 > link
-> imsg_len
) {
618 log_error ("FAILOVER: message overflow at length.");
619 return ISC_R_PROTOCOLERROR
;
622 /* Get option length. */
623 omapi_connection_get_uint16 (c
, &option_len
);
624 link
-> imsg_count
+= 2;
626 if (link
-> imsg_count
+ option_len
> link
-> imsg_len
) {
627 log_error ("FAILOVER: message overflow at data.");
628 return ISC_R_PROTOCOLERROR
;
631 /* If it's an unknown code, skip over it. */
632 if (option_code
> FTO_MAX
) {
633 #if defined (DEBUG_FAILOVER_MESSAGES)
634 log_debug (" option code %d (%s) len %d (not recognized)",
636 dhcp_failover_option_name (option_code
),
639 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
640 link
-> imsg_count
+= option_len
;
641 return ISC_R_SUCCESS
;
644 /* If it's the digest, do it now. */
645 if (ft_options
[option_code
].type
== FT_DIGEST
) {
646 link
-> imsg_count
+= option_len
;
647 if (link
-> imsg_count
!= link
-> imsg_len
) {
648 log_error ("FAILOVER: digest not at end of message");
649 return ISC_R_PROTOCOLERROR
;
651 #if defined (DEBUG_FAILOVER_MESSAGES)
652 log_debug (" option %s len %d",
653 ft_options
[option_code
].name
, option_len
);
655 /* For now, just dump it. */
656 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
657 return ISC_R_SUCCESS
;
660 /* Only accept an option once. */
661 if (link
-> imsg
-> options_present
& ft_options
[option_code
].bit
) {
662 log_error ("FAILOVER: duplicate option %s",
663 ft_options
[option_code
].name
);
664 return ISC_R_PROTOCOLERROR
;
667 /* Make sure the option is appropriate for this type of message.
668 Really, any option is generally allowed for any message, and the
669 cases where this is not true are too complicated to represent in
670 this way - what this code is doing is to just avoid saving the
671 value of an option we don't have any way to use, which allows
672 us to make the failover_message structure smaller. */
673 if (ft_options
[option_code
].bit
&&
674 !(fto_allowed
[link
-> imsg
-> type
] &
675 ft_options
[option_code
].bit
)) {
676 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
677 link
-> imsg_count
+= option_len
;
678 return ISC_R_SUCCESS
;
681 /* Figure out how many elements, how big they are, and where
683 if (ft_options
[option_code
].num_present
) {
684 /* If this option takes a fixed number of elements,
685 we expect the space for them to be preallocated,
686 and we can just read the data in. */
688 op
= ((unsigned char *)link
-> imsg
) +
689 ft_options
[option_code
].offset
;
690 op_size
= ft_sizes
[ft_options
[option_code
].type
];
691 op_count
= ft_options
[option_code
].num_present
;
693 if (option_len
!= op_size
* op_count
) {
694 log_error ("FAILOVER: option size (%d:%d), option %s",
696 (ft_sizes
[ft_options
[option_code
].type
] *
697 ft_options
[option_code
].num_present
),
698 ft_options
[option_code
].name
);
699 return ISC_R_PROTOCOLERROR
;
702 failover_option_t
*fo
;
704 /* FT_DDNS* are special - one or two bytes of status
705 followed by the client FQDN. */
706 if (ft_options
[option_code
].type
== FT_DDNS1
||
707 ft_options
[option_code
].type
== FT_DDNS1
) {
710 (((char *)link
-> imsg
) +
711 ft_options
[option_code
].offset
));
713 op_count
= (ft_options
[option_code
].type
== FT_DDNS1
716 omapi_connection_copyout (&ddns
-> codes
[0],
718 link
-> imsg_count
+= op_count
;
720 ddns
-> codes
[1] = 0;
722 op_count
= option_len
- op_count
;
724 ddns
-> length
= op_count
;
725 ddns
-> data
= dmalloc (op_count
, MDL
);
727 log_error ("FAILOVER: no memory getting%s(%d)",
728 " DNS data ", op_count
);
730 /* Actually, NO_MEMORY, but if we lose here
731 we have to drop the connection. */
732 return ISC_R_PROTOCOLERROR
;
734 omapi_connection_copyout (ddns
-> data
, c
, op_count
);
738 /* A zero for num_present means that any number of
739 elements can appear, so we have to figure out how
740 many we got from the length of the option, and then
741 fill out a failover_option structure describing the
743 op_size
= ft_sizes
[ft_options
[option_code
].type
];
745 /* Make sure that option data length is a multiple of the
746 size of the data type being sent. */
747 if (op_size
> 1 && option_len
% op_size
) {
748 log_error ("FAILOVER: option_len %d not %s%d",
749 option_len
, "multiple of ", op_size
);
750 return ISC_R_PROTOCOLERROR
;
753 op_count
= option_len
/ op_size
;
755 fo
= ((failover_option_t
*)
756 (((char *)link
-> imsg
) +
757 ft_options
[option_code
].offset
));
759 fo
-> count
= op_count
;
760 fo
-> data
= dmalloc (option_len
, MDL
);
762 log_error ("FAILOVER: no memory getting %s (%d)",
763 "option data", op_count
);
765 return ISC_R_PROTOCOLERROR
;
770 /* For single-byte message values and multi-byte values that
771 don't need swapping, just read them in all at once. */
772 if (op_size
== 1 || ft_options
[option_code
].type
== FT_IPADDR
) {
773 omapi_connection_copyout ((unsigned char *)op
, c
, option_len
);
774 link
-> imsg_count
+= option_len
;
778 /* For values that require swapping, read them in one at a time
779 using routines that swap bytes. */
780 for (i
= 0; i
< op_count
; i
++) {
781 switch (ft_options
[option_code
].type
) {
783 omapi_connection_get_uint32 (c
, (u_int32_t
*)op
);
785 link
-> imsg_count
+= 4;
789 omapi_connection_get_uint16 (c
, (u_int16_t
*)op
);
791 link
-> imsg_count
+= 2;
795 /* Everything else should have been handled
797 log_error ("FAILOVER: option %s: bad type %d",
798 ft_options
[option_code
].name
,
799 ft_options
[option_code
].type
);
800 return ISC_R_PROTOCOLERROR
;
804 /* Remember that we got this option. */
805 link
-> imsg
-> options_present
|= ft_options
[option_code
].bit
;
806 return ISC_R_SUCCESS
;
809 isc_result_t
dhcp_failover_link_set_value (omapi_object_t
*h
,
811 omapi_data_string_t
*name
,
812 omapi_typed_data_t
*value
)
814 if (h
-> type
!= omapi_type_protocol
)
815 return ISC_R_INVALIDARG
;
817 /* Never valid to set these. */
818 if (!omapi_ds_strcmp (name
, "link-port") ||
819 !omapi_ds_strcmp (name
, "link-name") ||
820 !omapi_ds_strcmp (name
, "link-state"))
823 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
824 return (*(h
-> inner
-> type
-> set_value
))
825 (h
-> inner
, id
, name
, value
);
826 return ISC_R_NOTFOUND
;
829 isc_result_t
dhcp_failover_link_get_value (omapi_object_t
*h
,
831 omapi_data_string_t
*name
,
832 omapi_value_t
**value
)
834 dhcp_failover_link_t
*link
;
836 if (h
-> type
!= omapi_type_protocol
)
837 return ISC_R_INVALIDARG
;
838 link
= (dhcp_failover_link_t
*)h
;
840 if (!omapi_ds_strcmp (name
, "link-port")) {
841 return omapi_make_int_value (value
, name
,
842 (int)link
-> peer_port
, MDL
);
843 } else if (!omapi_ds_strcmp (name
, "link-state")) {
844 if (link
-> state
< 0 ||
845 link
-> state
>= dhcp_flink_state_max
)
846 return omapi_make_string_value (value
, name
,
847 "invalid link state",
849 return omapi_make_string_value
851 dhcp_flink_state_names
[link
-> state
], MDL
);
854 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
855 return (*(h
-> inner
-> type
-> get_value
))
856 (h
-> inner
, id
, name
, value
);
857 return ISC_R_NOTFOUND
;
860 isc_result_t
dhcp_failover_link_destroy (omapi_object_t
*h
,
861 const char *file
, int line
)
863 dhcp_failover_link_t
*link
;
864 if (h
-> type
!= dhcp_type_failover_link
)
865 return ISC_R_INVALIDARG
;
866 link
= (dhcp_failover_link_t
*)h
;
868 if (link
-> peer_address
)
869 option_cache_dereference (&link
-> peer_address
, file
, line
);
871 failover_message_dereference (&link
-> imsg
, file
, line
);
872 if (link
-> state_object
)
873 dhcp_failover_state_dereference (&link
-> state_object
,
875 return ISC_R_SUCCESS
;
878 /* Write all the published values associated with the object through the
879 specified connection. */
881 isc_result_t
dhcp_failover_link_stuff_values (omapi_object_t
*c
,
885 dhcp_failover_link_t
*link
;
888 if (l
-> type
!= dhcp_type_failover_link
)
889 return ISC_R_INVALIDARG
;
890 link
= (dhcp_failover_link_t
*)l
;
892 status
= omapi_connection_put_name (c
, "link-port");
893 if (status
!= ISC_R_SUCCESS
)
895 status
= omapi_connection_put_uint32 (c
, sizeof (int));
896 if (status
!= ISC_R_SUCCESS
)
898 status
= omapi_connection_put_uint32 (c
, link
-> peer_port
);
899 if (status
!= ISC_R_SUCCESS
)
902 status
= omapi_connection_put_name (c
, "link-state");
903 if (status
!= ISC_R_SUCCESS
)
905 if (link
-> state
< 0 ||
906 link
-> state
>= dhcp_flink_state_max
)
907 status
= omapi_connection_put_string (c
, "invalid link state");
909 status
= (omapi_connection_put_string
910 (c
, dhcp_flink_state_names
[link
-> state
]));
911 if (status
!= ISC_R_SUCCESS
)
914 if (link
-> inner
&& link
-> inner
-> type
-> stuff_values
)
915 return (*(link
-> inner
-> type
-> stuff_values
)) (c
, id
,
917 return ISC_R_SUCCESS
;
920 /* Set up a listener for the omapi protocol. The handle stored points to
921 a listener object, not a protocol object. */
923 isc_result_t
dhcp_failover_listen (omapi_object_t
*h
)
926 dhcp_failover_listener_t
*obj
, *l
;
927 omapi_value_t
*value
= (omapi_value_t
*)0;
928 omapi_addr_t local_addr
;
931 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
932 "local-port", &value
);
933 if (status
!= ISC_R_SUCCESS
)
935 if (!value
-> value
) {
936 omapi_value_dereference (&value
, MDL
);
937 return ISC_R_INVALIDARG
;
940 status
= omapi_get_int_value (&port
, value
-> value
);
941 omapi_value_dereference (&value
, MDL
);
942 if (status
!= ISC_R_SUCCESS
)
944 local_addr
.port
= port
;
946 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
947 "local-address", &value
);
948 if (status
!= ISC_R_SUCCESS
)
950 if (!value
-> value
) {
952 omapi_value_dereference (&value
, MDL
);
953 return ISC_R_INVALIDARG
;
956 if (value
-> value
-> type
!= omapi_datatype_data
||
957 value
-> value
-> u
.buffer
.len
!= sizeof (struct in_addr
))
960 memcpy (local_addr
.address
, value
-> value
-> u
.buffer
.value
,
961 value
-> value
-> u
.buffer
.len
);
962 local_addr
.addrlen
= value
-> value
-> u
.buffer
.len
;
963 local_addr
.addrtype
= AF_INET
;
965 omapi_value_dereference (&value
, MDL
);
967 /* Are we already listening on this port and address? */
968 for (l
= failover_listeners
; l
; l
= l
-> next
) {
969 if (l
-> address
.port
== local_addr
.port
&&
970 l
-> address
.addrtype
== local_addr
.addrtype
&&
971 l
-> address
.addrlen
== local_addr
.addrlen
&&
972 !memcmp (l
-> address
.address
, local_addr
.address
,
976 /* Already listening. */
978 return ISC_R_SUCCESS
;
980 obj
= (dhcp_failover_listener_t
*)0;
981 status
= dhcp_failover_listener_allocate (&obj
, MDL
);
982 if (status
!= ISC_R_SUCCESS
)
984 obj
-> address
= local_addr
;
986 status
= omapi_listen_addr ((omapi_object_t
*)obj
, &obj
-> address
, 1);
987 if (status
!= ISC_R_SUCCESS
)
990 status
= omapi_object_reference (&h
-> outer
,
991 (omapi_object_t
*)obj
, MDL
);
992 if (status
!= ISC_R_SUCCESS
) {
993 dhcp_failover_listener_dereference (&obj
, MDL
);
996 status
= omapi_object_reference (&obj
-> inner
, h
, MDL
);
997 if (status
!= ISC_R_SUCCESS
) {
998 dhcp_failover_listener_dereference (&obj
, MDL
);
1002 /* Put this listener on the list. */
1003 if (failover_listeners
) {
1004 dhcp_failover_listener_reference (&obj
-> next
,
1005 failover_listeners
, MDL
);
1006 dhcp_failover_listener_dereference (&failover_listeners
, MDL
);
1008 dhcp_failover_listener_reference (&failover_listeners
, obj
, MDL
);
1010 return dhcp_failover_listener_dereference (&obj
, MDL
);
1013 /* Signal handler for protocol listener - if we get a connect signal,
1014 create a new protocol connection, otherwise pass the signal down. */
1016 isc_result_t
dhcp_failover_listener_signal (omapi_object_t
*o
,
1017 const char *name
, va_list ap
)
1019 isc_result_t status
;
1020 omapi_connection_object_t
*c
;
1021 dhcp_failover_link_t
*obj
;
1022 dhcp_failover_listener_t
*p
;
1023 dhcp_failover_state_t
*s
, *state
= (dhcp_failover_state_t
*)0;
1025 if (!o
|| o
-> type
!= dhcp_type_failover_listener
)
1026 return ISC_R_INVALIDARG
;
1027 p
= (dhcp_failover_listener_t
*)o
;
1029 /* Not a signal we recognize? */
1030 if (strcmp (name
, "connect")) {
1031 if (p
-> inner
&& p
-> inner
-> type
-> signal_handler
)
1032 return (*(p
-> inner
-> type
-> signal_handler
))
1033 (p
-> inner
, name
, ap
);
1034 return ISC_R_NOTFOUND
;
1037 c
= va_arg (ap
, omapi_connection_object_t
*);
1038 if (!c
|| c
-> type
!= omapi_type_connection
)
1039 return ISC_R_INVALIDARG
;
1041 /* See if we can find a failover_state object that
1042 matches this connection. */
1043 for (s
= failover_states
; s
; s
= s
-> next
) {
1044 if (dhcp_failover_state_match
1045 (s
, (u_int8_t
*)&c
-> remote_addr
.sin_addr
,
1046 sizeof c
-> remote_addr
.sin_addr
)) {
1052 log_info ("failover: listener: no matching state");
1053 return omapi_disconnect ((omapi_object_t
*)c
, 1);
1056 obj
= (dhcp_failover_link_t
*)0;
1057 status
= dhcp_failover_link_allocate (&obj
, MDL
);
1058 if (status
!= ISC_R_SUCCESS
)
1060 obj
-> peer_port
= ntohs (c
-> remote_addr
.sin_port
);
1062 status
= omapi_object_reference (&obj
-> outer
,
1063 (omapi_object_t
*)c
, MDL
);
1064 if (status
!= ISC_R_SUCCESS
) {
1066 dhcp_failover_link_dereference (&obj
, MDL
);
1067 log_info ("failover: listener: picayune failure.");
1068 omapi_disconnect ((omapi_object_t
*)c
, 1);
1072 status
= omapi_object_reference (&c
-> inner
,
1073 (omapi_object_t
*)obj
, MDL
);
1074 if (status
!= ISC_R_SUCCESS
)
1077 status
= dhcp_failover_state_reference (&obj
-> state_object
,
1079 if (status
!= ISC_R_SUCCESS
)
1082 omapi_signal_in ((omapi_object_t
*)obj
, "connect");
1084 return dhcp_failover_link_dereference (&obj
, MDL
);
1087 isc_result_t
dhcp_failover_listener_set_value (omapi_object_t
*h
,
1089 omapi_data_string_t
*name
,
1090 omapi_typed_data_t
*value
)
1092 if (h
-> type
!= dhcp_type_failover_listener
)
1093 return ISC_R_INVALIDARG
;
1095 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
1096 return (*(h
-> inner
-> type
-> set_value
))
1097 (h
-> inner
, id
, name
, value
);
1098 return ISC_R_NOTFOUND
;
1101 isc_result_t
dhcp_failover_listener_get_value (omapi_object_t
*h
,
1103 omapi_data_string_t
*name
,
1104 omapi_value_t
**value
)
1106 if (h
-> type
!= dhcp_type_failover_listener
)
1107 return ISC_R_INVALIDARG
;
1109 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
1110 return (*(h
-> inner
-> type
-> get_value
))
1111 (h
-> inner
, id
, name
, value
);
1112 return ISC_R_NOTFOUND
;
1115 isc_result_t
dhcp_failover_listener_destroy (omapi_object_t
*h
,
1116 const char *file
, int line
)
1118 dhcp_failover_listener_t
*l
;
1120 if (h
-> type
!= dhcp_type_failover_listener
)
1121 return ISC_R_INVALIDARG
;
1122 l
= (dhcp_failover_listener_t
*)h
;
1124 dhcp_failover_listener_dereference (&l
-> next
, file
, line
);
1126 return ISC_R_SUCCESS
;
1129 /* Write all the published values associated with the object through the
1130 specified connection. */
1132 isc_result_t
dhcp_failover_listener_stuff (omapi_object_t
*c
,
1136 if (p
-> type
!= dhcp_type_failover_listener
)
1137 return ISC_R_INVALIDARG
;
1139 if (p
-> inner
&& p
-> inner
-> type
-> stuff_values
)
1140 return (*(p
-> inner
-> type
-> stuff_values
)) (c
, id
,
1142 return ISC_R_SUCCESS
;
1145 /* Set up master state machine for the failover protocol. */
1147 isc_result_t
dhcp_failover_register (omapi_object_t
*h
)
1149 isc_result_t status
;
1150 dhcp_failover_state_t
*obj
;
1152 omapi_value_t
*value
= (omapi_value_t
*)0;
1154 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
1155 "local-port", &value
);
1156 if (status
!= ISC_R_SUCCESS
)
1158 if (!value
-> value
) {
1159 omapi_value_dereference (&value
, MDL
);
1160 return ISC_R_INVALIDARG
;
1163 status
= omapi_get_int_value (&port
, value
-> value
);
1164 omapi_value_dereference (&value
, MDL
);
1165 if (status
!= ISC_R_SUCCESS
)
1168 obj
= (dhcp_failover_state_t
*)0;
1169 dhcp_failover_state_allocate (&obj
, MDL
);
1170 obj
-> me
.port
= port
;
1172 status
= omapi_listen ((omapi_object_t
*)obj
, port
, 1);
1173 if (status
!= ISC_R_SUCCESS
) {
1174 dhcp_failover_state_dereference (&obj
, MDL
);
1178 status
= omapi_object_reference (&h
-> outer
, (omapi_object_t
*)obj
,
1180 if (status
!= ISC_R_SUCCESS
) {
1181 dhcp_failover_state_dereference (&obj
, MDL
);
1184 status
= omapi_object_reference (&obj
-> inner
, h
, MDL
);
1185 dhcp_failover_state_dereference (&obj
, MDL
);
1189 /* Signal handler for protocol state machine. */
1191 isc_result_t
dhcp_failover_state_signal (omapi_object_t
*o
,
1192 const char *name
, va_list ap
)
1194 isc_result_t status
;
1195 dhcp_failover_state_t
*state
;
1196 dhcp_failover_link_t
*link
;
1198 if (!o
|| o
-> type
!= dhcp_type_failover_state
)
1199 return ISC_R_INVALIDARG
;
1200 state
= (dhcp_failover_state_t
*)o
;
1202 /* Not a signal we recognize? */
1203 if (strcmp (name
, "disconnect") &&
1204 strcmp (name
, "message")) {
1205 if (state
-> inner
&& state
-> inner
-> type
-> signal_handler
)
1206 return (*(state
-> inner
-> type
-> signal_handler
))
1207 (state
-> inner
, name
, ap
);
1208 return ISC_R_NOTFOUND
;
1211 /* Handle connect signals by seeing what state we're in
1212 and potentially doing a state transition. */
1213 if (!strcmp (name
, "disconnect")) {
1214 link
= va_arg (ap
, dhcp_failover_link_t
*);
1216 dhcp_failover_link_dereference (&state
-> link_to_peer
, MDL
);
1217 dhcp_failover_state_transition (state
, "disconnect");
1218 if (state
-> i_am
== primary
) {
1219 #if defined (DEBUG_FAILOVER_TIMING)
1220 log_info ("add_timeout +90 %s",
1221 "dhcp_failover_reconnect");
1223 add_timeout (cur_time
+ 90, dhcp_failover_reconnect
,
1225 (tvref_t
)dhcp_failover_state_reference
,
1227 dhcp_failover_state_dereference
);
1229 } else if (!strcmp (name
, "message")) {
1230 link
= va_arg (ap
, dhcp_failover_link_t
*);
1232 if (link
-> imsg
-> type
== FTM_CONNECT
) {
1233 /* If we already have a link to the peer, it must be
1235 XXX Is this the right thing to do?
1236 XXX Probably not - what if both peers start at
1237 XXX the same time? */
1238 if (state
-> link_to_peer
) {
1239 dhcp_failover_send_connectack
1240 ((omapi_object_t
*)link
, state
,
1242 "already connected");
1243 omapi_disconnect (link
-> outer
, 1);
1244 return ISC_R_SUCCESS
;
1246 if (!(link
-> imsg
-> options_present
& FTB_MCLT
)) {
1247 dhcp_failover_send_connectack
1248 ((omapi_object_t
*)link
, state
,
1250 "no MCLT provided");
1251 omapi_disconnect (link
-> outer
, 1);
1252 return ISC_R_SUCCESS
;
1255 dhcp_failover_link_reference (&state
-> link_to_peer
,
1257 status
= (dhcp_failover_send_connectack
1258 ((omapi_object_t
*)link
, state
, 0, 0));
1259 if (status
!= ISC_R_SUCCESS
) {
1260 dhcp_failover_link_dereference
1261 (&state
-> link_to_peer
, MDL
);
1262 log_info ("dhcp_failover_send_connectack: %s",
1263 isc_result_totext (status
));
1264 omapi_disconnect (link
-> outer
, 1);
1265 return ISC_R_SUCCESS
;
1267 if (link
-> imsg
-> options_present
& FTB_MAX_UNACKED
)
1268 state
-> partner
.max_flying_updates
=
1269 link
-> imsg
-> max_unacked
;
1270 if (link
-> imsg
-> options_present
& FTB_RECEIVE_TIMER
)
1271 state
-> partner
.max_response_delay
=
1272 link
-> imsg
-> receive_timer
;
1273 state
-> mclt
= link
-> imsg
-> mclt
;
1274 dhcp_failover_send_state (state
);
1275 cancel_timeout (dhcp_failover_link_startup_timeout
,
1277 } else if (link
-> imsg
-> type
== FTM_CONNECTACK
) {
1282 cancel_timeout (dhcp_failover_link_startup_timeout
,
1285 if (!(link
->imsg
->options_present
&
1286 FTB_RELATIONSHIP_NAME
)) {
1287 errmsg
= "missing relationship-name";
1288 reason
= FTR_INVALID_PARTNER
;
1292 if (link
->imsg
->options_present
& FTB_REJECT_REASON
) {
1293 /* XXX: add message option to text output. */
1294 log_error ("Failover CONNECT to %s rejected: %s",
1295 state
? state
->name
: "unknown",
1296 (dhcp_failover_reject_reason_print
1297 (link
-> imsg
-> reject_reason
)));
1298 /* XXX print message from peer if peer sent message. */
1299 omapi_disconnect (link
-> outer
, 1);
1300 return ISC_R_SUCCESS
;
1303 if (!dhcp_failover_state_match_by_name(state
,
1304 &link
->imsg
->relationship_name
)) {
1305 /* XXX: Overflow results in log truncation, safe. */
1306 snprintf(errbuf
, sizeof(errbuf
), "remote failover "
1307 "relationship name %.*s does not match",
1308 (int)link
->imsg
->relationship_name
.count
,
1309 link
->imsg
->relationship_name
.data
);
1311 reason
= FTR_INVALID_PARTNER
;
1313 log_error("Failover CONNECTACK from %s: %s",
1314 state
->name
, errmsg
);
1315 dhcp_failover_send_disconnect ((omapi_object_t
*)link
,
1317 omapi_disconnect (link
-> outer
, 0);
1318 return ISC_R_SUCCESS
;
1321 if (state
-> link_to_peer
) {
1322 errmsg
= "already connected";
1323 reason
= FTR_DUP_CONNECTION
;
1327 if ((cur_time
> link
-> imsg
-> time
&&
1328 cur_time
- link
-> imsg
-> time
> 60) ||
1329 (cur_time
< link
-> imsg
-> time
&&
1330 link
-> imsg
-> time
- cur_time
> 60)) {
1331 errmsg
= "time offset too large";
1332 reason
= FTR_TIMEMISMATCH
;
1336 dhcp_failover_link_reference (&state
-> link_to_peer
,
1339 /* XXX This is probably the right thing to do, but
1340 XXX for release three, to make the smallest possible
1341 XXX change, we are doing this when the peer state
1342 XXX changes instead. */
1343 if (state
-> me
.state
== startup
)
1344 dhcp_failover_set_state (state
,
1345 state
-> saved_state
);
1348 dhcp_failover_send_state (state
);
1350 if (link
-> imsg
-> options_present
& FTB_MAX_UNACKED
)
1351 state
-> partner
.max_flying_updates
=
1352 link
-> imsg
-> max_unacked
;
1353 if (link
-> imsg
-> options_present
& FTB_RECEIVE_TIMER
)
1354 state
-> partner
.max_response_delay
=
1355 link
-> imsg
-> receive_timer
;
1356 #if defined (DEBUG_FAILOVER_TIMING)
1357 log_info ("add_timeout +%d %s",
1358 (int)state
-> partner
.max_response_delay
/ 3,
1359 "dhcp_failover_send_contact");
1361 add_timeout (cur_time
+
1362 (int)state
-> partner
.max_response_delay
/ 3,
1363 dhcp_failover_send_contact
, state
,
1364 (tvref_t
)dhcp_failover_state_reference
,
1365 (tvunref_t
)dhcp_failover_state_dereference
);
1366 #if defined (DEBUG_FAILOVER_TIMING)
1367 log_info ("add_timeout +%d %s",
1368 (int)state
-> me
.max_response_delay
,
1369 "dhcp_failover_timeout");
1371 add_timeout (cur_time
+
1372 (int)state
-> me
.max_response_delay
,
1373 dhcp_failover_timeout
, state
,
1374 (tvref_t
)dhcp_failover_state_reference
,
1375 (tvunref_t
)dhcp_failover_state_dereference
);
1376 } else if (link
-> imsg
-> type
== FTM_DISCONNECT
) {
1377 if (link
-> imsg
-> reject_reason
) {
1378 log_error ("Failover DISCONNECT from %s: %s",
1379 state
? state
->name
: "unknown",
1380 (dhcp_failover_reject_reason_print
1381 (link
-> imsg
-> reject_reason
)));
1383 omapi_disconnect (link
-> outer
, 1);
1384 } else if (link
-> imsg
-> type
== FTM_BNDUPD
) {
1385 dhcp_failover_process_bind_update (state
,
1387 } else if (link
-> imsg
-> type
== FTM_BNDACK
) {
1388 dhcp_failover_process_bind_ack (state
, link
-> imsg
);
1389 } else if (link
-> imsg
-> type
== FTM_UPDREQ
) {
1390 dhcp_failover_process_update_request (state
,
1392 } else if (link
-> imsg
-> type
== FTM_UPDREQALL
) {
1393 dhcp_failover_process_update_request_all
1394 (state
, link
-> imsg
);
1395 } else if (link
-> imsg
-> type
== FTM_UPDDONE
) {
1396 dhcp_failover_process_update_done (state
,
1398 } else if (link
-> imsg
-> type
== FTM_POOLREQ
) {
1399 dhcp_failover_pool_reqbalance(state
);
1400 } else if (link
-> imsg
-> type
== FTM_POOLRESP
) {
1401 log_info ("pool response: %ld leases",
1403 link
-> imsg
-> addresses_transferred
);
1404 } else if (link
-> imsg
-> type
== FTM_STATE
) {
1405 dhcp_failover_peer_state_changed (state
,
1409 /* Add a timeout so that if the partner doesn't send
1410 another message for the maximum transmit idle time
1411 plus a grace of one second, we close the
1413 if (state
-> link_to_peer
&&
1414 state
-> link_to_peer
== link
&&
1415 state
-> link_to_peer
-> state
!= dhcp_flink_disconnected
)
1417 #if defined (DEBUG_FAILOVER_TIMING)
1418 log_info ("add_timeout +%d %s",
1419 (int)state
-> me
.max_response_delay
,
1420 "dhcp_failover_timeout");
1422 add_timeout (cur_time
+
1423 (int)state
-> me
.max_response_delay
,
1424 dhcp_failover_timeout
, state
,
1425 (tvref_t
)dhcp_failover_state_reference
,
1426 (tvunref_t
)dhcp_failover_state_dereference
);
1431 /* Handle all the events we care about... */
1432 return ISC_R_SUCCESS
;
1435 isc_result_t
dhcp_failover_state_transition (dhcp_failover_state_t
*state
,
1438 isc_result_t status
;
1440 /* XXX Check these state transitions against the spec! */
1441 if (!strcmp (name
, "disconnect")) {
1442 if (state
-> link_to_peer
) {
1443 log_info ("peer %s: disconnected", state
-> name
);
1444 if (state
-> link_to_peer
-> state_object
)
1445 dhcp_failover_state_dereference
1446 (&state
-> link_to_peer
-> state_object
, MDL
);
1447 dhcp_failover_link_dereference (&state
-> link_to_peer
,
1450 cancel_timeout (dhcp_failover_send_contact
, state
);
1451 cancel_timeout (dhcp_failover_timeout
, state
);
1452 cancel_timeout (dhcp_failover_startup_timeout
, state
);
1454 switch (state
-> me
.state
== startup
?
1455 state
-> saved_state
: state
-> me
.state
) {
1456 /* In these situations, we remain in the current
1457 * state, or if in startup enter those states.
1459 case communications_interrupted
:
1466 case resolution_interrupted
:
1468 /* Already in the right state? */
1469 if (state
-> me
.state
== startup
)
1470 return (dhcp_failover_set_state
1471 (state
, state
-> saved_state
));
1472 return ISC_R_SUCCESS
;
1474 case potential_conflict
:
1475 return dhcp_failover_set_state
1476 (state
, resolution_interrupted
);
1479 return dhcp_failover_set_state
1480 (state
, communications_interrupted
);
1483 return dhcp_failover_set_state
1484 (state
, resolution_interrupted
);
1487 log_fatal("Impossible case at %s:%d.", MDL
);
1488 break; /* can't happen. */
1490 } else if (!strcmp (name
, "connect")) {
1491 switch (state
-> me
.state
) {
1492 case communications_interrupted
:
1493 status
= dhcp_failover_set_state (state
, normal
);
1494 dhcp_failover_send_updates (state
);
1497 case resolution_interrupted
:
1498 return dhcp_failover_set_state (state
,
1499 potential_conflict
);
1503 case potential_conflict
:
1512 return dhcp_failover_send_state (state
);
1515 log_fatal("Impossible case at %s:%d.", MDL
);
1518 } else if (!strcmp (name
, "startup")) {
1519 dhcp_failover_set_state (state
, startup
);
1520 return ISC_R_SUCCESS
;
1521 } else if (!strcmp (name
, "connect-timeout")) {
1522 switch (state
-> me
.state
) {
1523 case communications_interrupted
:
1525 case resolution_interrupted
:
1530 return ISC_R_SUCCESS
;
1537 return dhcp_failover_set_state
1538 (state
, communications_interrupted
);
1540 case potential_conflict
:
1541 return dhcp_failover_set_state
1542 (state
, resolution_interrupted
);
1545 log_fatal("Impossible case at %s:%d.", MDL
);
1549 return ISC_R_INVALIDARG
;
1552 isc_result_t
dhcp_failover_set_service_state (dhcp_failover_state_t
*state
)
1554 switch (state
-> me
.state
) {
1556 state
-> service_state
= not_responding
;
1557 state
-> nrr
= " (my state unknown)";
1561 state
-> service_state
= service_partner_down
;
1566 state
-> service_state
= cooperating
;
1570 case communications_interrupted
:
1571 state
-> service_state
= not_cooperating
;
1575 case resolution_interrupted
:
1576 case potential_conflict
:
1578 state
-> service_state
= not_responding
;
1579 state
-> nrr
= " (resolving conflicts)";
1583 state
-> service_state
= not_responding
;
1584 state
-> nrr
= " (recovering)";
1588 state
-> service_state
= not_responding
;
1589 state
-> nrr
= " (shut down)";
1593 state
-> service_state
= not_responding
;
1594 state
-> nrr
= " (paused)";
1598 state
-> service_state
= not_responding
;
1599 state
-> nrr
= " (recover wait)";
1603 state
-> service_state
= not_responding
;
1604 state
-> nrr
= " (recover done)";
1608 state
-> service_state
= service_startup
;
1609 state
-> nrr
= " (startup)";
1613 log_fatal("Impossible case at %s:%d.\n", MDL
);
1617 /* Some peer states can require us not to respond, even if our
1619 /* XXX hm. I suspect this isn't true anymore. */
1620 if (state
-> service_state
!= not_responding
) {
1621 switch (state
-> partner
.state
) {
1623 state
-> service_state
= not_responding
;
1624 state
-> nrr
= " (peer demands: recovering)";
1627 case potential_conflict
:
1629 case resolution_interrupted
:
1630 state
-> service_state
= not_responding
;
1631 state
-> nrr
= " (peer demands: resolving conflicts)";
1634 /* Other peer states don't affect our behaviour. */
1640 return ISC_R_SUCCESS
;
1643 isc_result_t
dhcp_failover_set_state (dhcp_failover_state_t
*state
,
1644 enum failover_state new_state
)
1646 enum failover_state saved_state
;
1649 struct shared_network
*s
;
1652 /* If we're in certain states where we're sending updates, and the peer
1653 * state changes, we need to re-schedule any pending updates just to
1654 * be on the safe side. This results in retransmission.
1656 switch (state
-> me
.state
) {
1658 case potential_conflict
:
1660 if (state
-> ack_queue_tail
) {
1663 /* Zap the flags. */
1664 for (lp
= state
-> ack_queue_head
; lp
; lp
= lp
-> next_pending
)
1665 lp
-> flags
= ((lp
-> flags
& ~ON_ACK_QUEUE
) |
1668 /* Now hook the ack queue to the beginning of the update
1670 if (state
-> update_queue_head
) {
1671 lease_reference (&state
-> ack_queue_tail
-> next_pending
,
1672 state
-> update_queue_head
, MDL
);
1673 lease_dereference (&state
-> update_queue_head
, MDL
);
1675 lease_reference (&state
-> update_queue_head
,
1676 state
-> ack_queue_head
, MDL
);
1677 if (!state
-> update_queue_tail
) {
1678 #if defined (POINTER_DEBUG)
1679 if (state
-> ack_queue_tail
-> next_pending
) {
1680 log_error ("next pending on ack queue tail.");
1684 lease_reference (&state
-> update_queue_tail
,
1685 state
-> ack_queue_tail
, MDL
);
1687 lease_dereference (&state
-> ack_queue_tail
, MDL
);
1688 lease_dereference (&state
-> ack_queue_head
, MDL
);
1689 state
-> cur_unacked_updates
= 0;
1691 /* We will re-queue a timeout later, if applicable. */
1692 cancel_timeout (dhcp_failover_keepalive
, state
);
1699 /* Tentatively make the transition. */
1700 saved_state
= state
-> me
.state
;
1701 saved_stos
= state
-> me
.stos
;
1703 /* Keep the old stos if we're going into recover_wait or if we're
1704 coming into or out of startup. */
1705 if (new_state
!= recover_wait
&& new_state
!= startup
&&
1706 saved_state
!= startup
)
1707 state
-> me
.stos
= cur_time
;
1709 /* If we're in shutdown, peer is in partner_down, and we're moving
1710 to recover, we can skip waiting for MCLT to expire. This happens
1711 when a server is moved administratively into shutdown prior to
1712 actually shutting down. Of course, if there are any updates
1713 pending we can't actually do this. */
1714 if (new_state
== recover
&& saved_state
== shut_down
&&
1715 state
-> partner
.state
== partner_down
&&
1716 !state
-> update_queue_head
&& !state
-> ack_queue_head
)
1717 state
-> me
.stos
= cur_time
- state
-> mclt
;
1719 state
-> me
.state
= new_state
;
1720 if (new_state
== startup
&& saved_state
!= startup
)
1721 state
-> saved_state
= saved_state
;
1723 /* If we can't record the new state, we can't make a state transition. */
1724 if (!write_failover_state (state
) || !commit_leases ()) {
1725 log_error ("Unable to record current failover state for %s",
1727 state
-> me
.state
= saved_state
;
1728 state
-> me
.stos
= saved_stos
;
1729 return ISC_R_IOERROR
;
1732 log_info ("failover peer %s: I move from %s to %s",
1733 state
-> name
, dhcp_failover_state_name_print (saved_state
),
1734 dhcp_failover_state_name_print (state
-> me
.state
));
1736 /* If we were in startup and we just left it, cancel the timeout. */
1737 if (new_state
!= startup
&& saved_state
== startup
)
1738 cancel_timeout (dhcp_failover_startup_timeout
, state
);
1740 /* Set our service state. */
1741 dhcp_failover_set_service_state (state
);
1743 /* Tell the peer about it. */
1744 if (state
-> link_to_peer
)
1745 dhcp_failover_send_state (state
);
1747 switch (new_state
) {
1749 /* Upon entering normal state, the server is expected to retransmit
1750 * all pending binding updates. This is a good opportunity to
1751 * rebalance the pool (potentially making new pending updates),
1752 * which also schedules the next pool rebalance.
1754 dhcp_failover_pool_balance(state
);
1755 dhcp_failover_generate_update_queue(state
, 0);
1757 if (state
->update_queue_tail
!= NULL
) {
1758 dhcp_failover_send_updates(state
);
1759 log_info("Sending updates to %s.", state
->name
);
1764 case potential_conflict
:
1765 if (state
-> i_am
== primary
)
1766 dhcp_failover_send_update_request (state
);
1770 #if defined (DEBUG_FAILOVER_TIMING)
1771 log_info ("add_timeout +15 %s",
1772 "dhcp_failover_startup_timeout");
1774 add_timeout (cur_time
+ 15,
1775 dhcp_failover_startup_timeout
,
1777 (tvref_t
)omapi_object_reference
,
1779 omapi_object_dereference
);
1782 /* If we come back in recover_wait and there's still waiting
1783 to do, set a timeout. */
1785 if (state
-> me
.stos
+ state
-> mclt
> cur_time
) {
1786 #if defined (DEBUG_FAILOVER_TIMING)
1787 log_info ("add_timeout +%d %s",
1789 state
-> me
.stos
+ state
-> mclt
),
1790 "dhcp_failover_startup_timeout");
1792 add_timeout ((int)(state
-> me
.stos
+ state
-> mclt
),
1793 dhcp_failover_recover_done
,
1795 (tvref_t
)omapi_object_reference
,
1797 omapi_object_dereference
);
1799 dhcp_failover_recover_done (state
);
1803 /* XXX: We're supposed to calculate if updreq or updreqall is
1804 * needed. In theory, we should only have to updreqall if we
1805 * are positive we lost our stable storage.
1807 if (state
-> link_to_peer
)
1808 dhcp_failover_send_update_request_all (state
);
1812 /* For every expired lease, set a timeout for it to become free. */
1813 for (s
= shared_networks
; s
; s
= s
-> next
) {
1814 for (p
= s
-> pools
; p
; p
= p
-> next
) {
1815 if (p
-> failover_peer
== state
) {
1816 for (l
= p
->expired
; l
; l
= l
->next
) {
1817 l
->tsfp
= state
->me
.stos
+ state
->mclt
;
1818 l
->sort_time
= (l
->tsfp
> l
->ends
) ?
1822 (p
->expired
->sort_time
< p
->next_event_time
)) {
1824 p
->next_event_time
= p
->expired
->sort_time
;
1825 #if defined (DEBUG_FAILOVER_TIMING)
1826 log_info ("add_timeout +%d %s",
1827 (int)(cur_time
- p
->next_event_time
),
1830 add_timeout(p
->next_event_time
, pool_timer
, p
,
1831 (tvref_t
)pool_reference
,
1832 (tvunref_t
)pool_dereference
);
1844 return ISC_R_SUCCESS
;
1847 isc_result_t
dhcp_failover_peer_state_changed (dhcp_failover_state_t
*state
,
1848 failover_message_t
*msg
)
1850 enum failover_state previous_state
= state
-> partner
.state
;
1851 enum failover_state new_state
;
1854 new_state
= msg
-> server_state
;
1855 startupp
= (msg
-> server_flags
& FTF_SERVER_STARTUP
) ? 1 : 0;
1857 if (state
-> partner
.state
== new_state
&& state
-> me
.state
) {
1858 switch (state
-> me
.state
) {
1860 dhcp_failover_set_state (state
, state
-> saved_state
);
1861 return ISC_R_SUCCESS
;
1865 case potential_conflict
:
1870 return ISC_R_SUCCESS
;
1872 /* If we get a peer state change when we're
1873 disconnected, we always process it. */
1875 case communications_interrupted
:
1876 case resolution_interrupted
:
1882 log_fatal("Impossible case at %s:%d.", MDL
);
1887 state
-> partner
.state
= new_state
;
1889 log_info ("failover peer %s: peer moves from %s to %s",
1891 dhcp_failover_state_name_print (previous_state
),
1892 dhcp_failover_state_name_print (state
-> partner
.state
));
1894 if (!write_failover_state (state
) || !commit_leases ()) {
1895 /* This is bad, but it's not fatal. Of course, if we
1896 can't write to the lease database, we're not going to
1897 get much done anyway. */
1898 log_error ("Unable to record current failover state for %s",
1902 /* Quickly validate the new state as being one of the 13 known
1905 switch (new_state
) {
1909 case communications_interrupted
:
1911 case potential_conflict
:
1916 case resolution_interrupted
:
1922 log_error("failover peer %s: Invalid state: %d", state
->name
,
1924 dhcp_failover_set_state(state
, shut_down
);
1925 return ISC_R_SUCCESS
;
1928 /* Do any state transitions that are required as a result of the
1929 peer's state transition. */
1931 switch (state
-> me
.state
== startup
?
1932 state
-> saved_state
: state
-> me
.state
) {
1934 switch (new_state
) {
1936 dhcp_failover_state_pool_check (state
);
1940 if (state
-> me
.state
== startup
)
1941 dhcp_failover_set_state (state
, recover
);
1943 dhcp_failover_set_state (state
,
1944 potential_conflict
);
1947 case potential_conflict
:
1948 case resolution_interrupted
:
1950 /* None of these transitions should ever occur. */
1951 log_error("Peer %s: Invalid state transition %s "
1952 "to %s.", state
->name
,
1953 dhcp_failover_state_name_print(previous_state
),
1954 dhcp_failover_state_name_print(new_state
));
1955 dhcp_failover_set_state (state
, shut_down
);
1960 dhcp_failover_set_state (state
, partner_down
);
1964 dhcp_failover_set_state (state
,
1965 communications_interrupted
);
1969 /* recover_wait, recover_done, unknown_state, startup,
1970 * communications_interrupted
1977 switch (new_state
) {
1979 log_info ("failover peer %s: requesting %s",
1980 state
-> name
, "full update from peer");
1981 /* Don't send updreqall if we're really in the
1982 startup state, because that will result in two
1984 if (state
-> me
.state
== recover
)
1985 dhcp_failover_send_update_request_all (state
);
1988 case potential_conflict
:
1989 case resolution_interrupted
:
1992 dhcp_failover_set_state (state
, potential_conflict
);
1996 case communications_interrupted
:
1997 /* We're supposed to send an update request at this
1999 /* XXX we don't currently have code here to do any
2000 XXX clever detection of when we should send an
2001 XXX UPDREQALL message rather than an UPDREQ
2002 XXX message. What to do, what to do? */
2003 /* Currently when we enter recover state, no matter
2004 * the reason, we send an UPDREQALL. So, it makes
2005 * the most sense to stick to that until something
2007 * Furthermore, we only want to send the update
2008 * request if we are not in startup state.
2010 if (state
-> me
.state
== recover
)
2011 dhcp_failover_send_update_request_all (state
);
2015 /* XXX We're not explicitly told what to do in this
2016 XXX case, but this transition is consistent with
2017 XXX what is elsewhere in the draft. */
2018 dhcp_failover_set_state (state
, partner_down
);
2021 /* We can't really do anything in this case. */
2023 /* paused, recover_done, recover_wait, unknown_state,
2030 case potential_conflict
:
2031 switch (new_state
) {
2033 /* This is an illegal transition. */
2034 log_error("Peer %s moves to normal during conflict "
2035 "resolution - panic, shutting down.",
2037 dhcp_failover_set_state(state
, shut_down
);
2041 if (previous_state
== potential_conflict
)
2042 dhcp_failover_send_update_request (state
);
2044 log_error("Peer %s: Unexpected move to "
2045 "conflict-done.", state
->name
);
2050 case potential_conflict
:
2052 case communications_interrupted
:
2053 case resolution_interrupted
:
2058 dhcp_failover_set_state (state
, recover
);
2062 dhcp_failover_set_state (state
, partner_down
);
2066 /* unknown_state, startup */
2072 switch (new_state
) {
2075 dhcp_failover_set_state(state
, new_state
);
2079 log_fatal("Peer %s: Invalid attempt to move from %s "
2080 "to %s while local state is conflict-done.",
2082 dhcp_failover_state_name_print(previous_state
),
2083 dhcp_failover_state_name_print(new_state
));
2088 /* Take no action if other server is starting up. */
2092 switch (new_state
) {
2093 /* This is where we should be. */
2099 dhcp_failover_set_state (state
, normal
);
2103 case potential_conflict
:
2105 case communications_interrupted
:
2106 case resolution_interrupted
:
2108 dhcp_failover_set_state (state
, potential_conflict
);
2112 /* shut_down, paused, unknown_state, startup */
2117 case communications_interrupted
:
2118 switch (new_state
) {
2120 /* Stick with the status quo. */
2123 /* If we're in communications-interrupted and an
2124 amnesiac peer connects, go to the partner_down
2125 state immediately. */
2127 dhcp_failover_set_state (state
, partner_down
);
2131 case communications_interrupted
:
2134 /* XXX so we don't need to do this specially in
2135 XXX the CONNECT and CONNECTACK handlers. */
2136 dhcp_failover_send_updates (state
);
2137 dhcp_failover_set_state (state
, normal
);
2140 case potential_conflict
:
2142 case resolution_interrupted
:
2144 dhcp_failover_set_state (state
, potential_conflict
);
2148 dhcp_failover_set_state (state
, partner_down
);
2152 /* unknown_state, startup */
2157 case resolution_interrupted
:
2158 switch (new_state
) {
2161 case potential_conflict
:
2163 case communications_interrupted
:
2164 case resolution_interrupted
:
2168 dhcp_failover_set_state (state
, potential_conflict
);
2172 dhcp_failover_set_state (state
, partner_down
);
2176 /* paused, unknown_state, startup */
2181 /* Make no transitions while in recover_wait...just wait. */
2186 switch (new_state
) {
2188 log_error("Both servers have entered recover-done!");
2190 dhcp_failover_set_state (state
, normal
);
2194 dhcp_failover_set_state (state
, partner_down
);
2198 /* potential_conflict, partner_down,
2199 * communications_interrupted, resolution_interrupted,
2200 * paused, recover, recover_wait, unknown_state,
2207 /* We are essentially dead in the water when we're in
2208 either shut_down or paused states, and do not do any
2209 automatic state transitions. */
2214 /* XXX: Shouldn't this be a fatal condition? */
2219 log_fatal("Impossible condition at %s:%d.", MDL
);
2224 /* If we didn't make a transition out of startup as a result of
2225 the peer's state change, do it now as a result of the fact that
2226 we got a state change from the peer. */
2227 if (state
-> me
.state
== startup
&& state
-> saved_state
!= startup
)
2228 dhcp_failover_set_state (state
, state
-> saved_state
);
2230 /* For now, just set the service state based on the peer's state
2232 dhcp_failover_set_service_state (state
);
2234 return ISC_R_SUCCESS
;
2237 /* Balance operation manual entry. */
2239 dhcp_failover_pool_balance(dhcp_failover_state_t
*state
)
2241 /* Cancel pending event. */
2242 cancel_timeout(dhcp_failover_pool_rebalance
, state
);
2243 state
->sched_balance
= 0;
2245 dhcp_failover_pool_dobalance(state
);
2248 /* Balance operation entry from timer event. */
2250 dhcp_failover_pool_rebalance(void *failover_state
)
2252 dhcp_failover_state_t
*state
;
2254 state
= (dhcp_failover_state_t
*)failover_state
;
2256 /* Clear scheduled event indicator. */
2257 state
->sched_balance
= 0;
2259 if (dhcp_failover_pool_dobalance(state
))
2260 dhcp_failover_send_updates(state
);
2263 /* Balance operation entry from POOLREQ protocol message. */
2265 dhcp_failover_pool_reqbalance(dhcp_failover_state_t
*state
)
2269 /* Cancel pending event. */
2270 cancel_timeout(dhcp_failover_pool_rebalance
, state
);
2271 state
->sched_balance
= 0;
2273 queued
= dhcp_failover_pool_dobalance(state
);
2275 dhcp_failover_send_poolresp(state
, queued
);
2278 dhcp_failover_send_updates(state
);
2280 log_info("peer %s: Got POOLREQ, answering negatively! "
2281 "Peer may be out of leases or database inconsistent.",
2285 /* Do the meat of the work common to all forms of pool rebalance. */
2286 static int dhcp_failover_pool_dobalance(dhcp_failover_state_t
*state
)
2288 int lts
, total
, thresh
, hold
, pass
;
2289 int leases_queued
= 0;
2291 struct lease
*lp
= (struct lease
*)0;
2292 struct lease
*next
= (struct lease
*)0;
2293 struct shared_network
*s
;
2295 binding_state_t peer_lease_state
;
2296 binding_state_t my_lease_state
;
2298 int (*log_func
)(const char *, ...);
2301 if (state
-> me
.state
!= normal
)
2304 state
->last_balance
= cur_time
;
2306 for (s
= shared_networks
; s
; s
= s
->next
) {
2307 for (p
= s
->pools
; p
; p
= p
->next
) {
2308 if (p
->failover_peer
!= state
)
2311 /* Right now we're giving the peer half of the free leases.
2312 If we have more leases than the peer (i.e., more than
2313 half), then the number of leases we have, less the number
2314 of leases the peer has, will be how many more leases we
2315 have than the peer has. So if we send half that number
2316 to the peer, we should be even. */
2317 if (p
->failover_peer
->i_am
== primary
) {
2318 lts
= (p
->free_leases
- p
->backup_leases
) / 2;
2319 peer_lease_state
= FTS_BACKUP
;
2320 my_lease_state
= FTS_FREE
;
2323 lts
= (p
->backup_leases
- p
->free_leases
) / 2;
2324 peer_lease_state
= FTS_FREE
;
2325 my_lease_state
= FTS_BACKUP
;
2329 total
= p
->backup_leases
+ p
->free_leases
;
2331 thresh
= ((total
* state
->max_lease_misbalance
) + 50) / 100;
2332 hold
= ((total
* state
->max_lease_ownership
) + 50) / 100;
2334 log_info("balancing pool %lx %s total %d free %d "
2335 "backup %d lts %d max-own (+/-)%d",
2337 (p
->shared_network
?
2338 p
->shared_network
->name
: ""), p
->lease_count
,
2339 p
->free_leases
, p
->backup_leases
, lts
, hold
);
2341 /* If lts is in the negatives (we need leases) more than
2342 * negative double the thresh%, panic and send poolreq to
2343 * hopefully wake up the peer.
2345 if (!reqsent
&& (lts
< (thresh
* -2))) {
2346 dhcp_failover_send_poolreq(state
);
2350 /* In the first pass, try to allocate leases to the
2351 * peer which it would normally be responsible for (if
2352 * the lease has a hardware address or client-identifier,
2353 * and the load-balance-algorithm chooses the peer to
2354 * answer that address), up to a hold% excess in the peer's
2355 * favor. In the second pass, just send the oldest (first
2356 * on the list) leases up to a hold% excess in our favor.
2358 * This could make for additional pool rebalance
2359 * events, but preserving MAC possession should be
2363 lease_reference(&lp
, *lq
, MDL
);
2365 /* In the case where there are 2 leases, hold is zero, and
2366 * lts is 1 if both leases are on the local server. If
2367 * there is only 1 lease, both lts and hold are zero. Let's
2368 * not play ping pong.
2370 while (lp
&& (lts
> (pass
? hold
: -hold
))) {
2372 lease_dereference(&next
, MDL
);
2374 lease_reference(&next
, lp
->next
, MDL
);
2376 if (pass
|| peer_wants_lease(lp
)) {
2379 lp
->next_binding_state
= peer_lease_state
;
2380 lp
->tstp
= cur_time
;
2381 lp
->starts
= cur_time
;
2383 if (!supersede_lease(lp
, NULL
, 0, 1, 0) ||
2385 log_error("can't commit lease %s on "
2386 "giveaway", piaddr(lp
->ip_addr
));
2389 lease_dereference(&lp
, MDL
);
2391 lease_reference(&lp
, next
, MDL
);
2394 lease_reference(&lp
, *lq
, MDL
);
2399 lease_dereference(&next
, MDL
);
2401 lease_dereference(&lp
, MDL
);
2404 result
= "IMBALANCED";
2405 log_func
= log_error
;
2407 result
= "balanced";
2408 log_func
= log_info
;
2411 log_func("%s pool %lx %s total %d free %d backup %d "
2412 "lts %d max-misbal %d", result
, (unsigned long)p
,
2413 (p
->shared_network
?
2414 p
->shared_network
->name
: ""), p
->lease_count
,
2415 p
->free_leases
, p
->backup_leases
, lts
, thresh
);
2417 /* Recalculate next rebalance event timer. */
2418 dhcp_failover_pool_check(p
);
2425 return leases_queued
;
2428 /* dhcp_failover_pool_check: Called whenever FREE or BACKUP leases change
2429 * states, on both servers. Check the scheduled time to rebalance the pool
2430 * and lower it if applicable.
2433 dhcp_failover_pool_check(struct pool
*pool
)
2435 dhcp_failover_state_t
*peer
;
2438 peer
= pool
->failover_peer
;
2440 if(!peer
|| peer
->me
.state
!= normal
)
2443 /* Estimate the time left until lease exhaustion.
2444 * The first lease on the backup or free lists is also the oldest
2445 * lease. It is reasonable to guess that it will take at least
2446 * as much time for a pool to run out of leases, as the present
2447 * age of the oldest lease (seconds since it expired).
2449 * Note that this isn't so sane of an assumption if the oldest
2450 * lease is a virgin (ends = 0), we wind up sending this against
2451 * the max_balance bounds check.
2453 if(pool
->free
&& pool
->free
->ends
< cur_time
)
2454 est1
= cur_time
- pool
->free
->ends
;
2458 if(pool
->backup
&& pool
->backup
->ends
< cur_time
)
2459 est2
= cur_time
- pool
->backup
->ends
;
2463 /* We don't want to schedule rebalance for when we think we'll run
2464 * out of leases, we want to schedule the rebalance for when we think
2465 * the disparity will be 'large enough' to warrant action.
2467 est1
= ((est1
* peer
->max_lease_misbalance
) + 50) / 100;
2468 est2
= ((est2
* peer
->max_lease_misbalance
) + 50) / 100;
2470 /* Guess when the local system will begin issuing POOLREQ panic
2471 * attacks because "max_lease_misbalance*2" has been exceeded.
2473 if(peer
->i_am
== primary
)
2478 /* Select the smallest time. */
2482 /* Bounded by the maximum configured value. */
2483 if(est1
> peer
->max_balance
)
2484 est1
= peer
->max_balance
;
2486 /* Project this time into the future. */
2489 /* Do not move the time down under the minimum. */
2490 est2
= peer
->last_balance
+ peer
->min_balance
;
2491 if(peer
->last_balance
&& (est1
< est2
))
2494 /* Introduce a random delay. */
2495 est1
+= random() % 5;
2497 /* Do not move the time forward, or reset to the same time. */
2498 if(peer
->sched_balance
) {
2499 if (est1
>= peer
->sched_balance
)
2502 /* We are about to schedule the time down, cancel the
2505 cancel_timeout(dhcp_failover_pool_rebalance
, peer
);
2508 /* The time is different, and lower, use it. */
2509 peer
->sched_balance
= est1
;
2511 #if defined(DEBUG_FAILOVER_TIMING)
2512 log_info("add_timeout +%d dhcp_failover_pool_rebalance",
2515 add_timeout(est1
, dhcp_failover_pool_rebalance
, peer
,
2516 (tvref_t
)dhcp_failover_state_reference
,
2517 (tvunref_t
)dhcp_failover_state_dereference
);
2520 int dhcp_failover_state_pool_check (dhcp_failover_state_t
*state
)
2522 struct shared_network
*s
;
2525 for (s
= shared_networks
; s
; s
= s
-> next
) {
2526 for (p
= s
-> pools
; p
; p
= p
-> next
) {
2527 if (p
-> failover_peer
!= state
)
2529 dhcp_failover_pool_check (p
);
2535 isc_result_t
dhcp_failover_send_updates (dhcp_failover_state_t
*state
)
2537 struct lease
*lp
= (struct lease
*)0;
2538 isc_result_t status
;
2540 /* Can't update peer if we're not talking to it! */
2541 if (!state
-> link_to_peer
)
2542 return ISC_R_SUCCESS
;
2544 /* If there are acks pending, transmit them prior to potentialy
2545 * sending new updates for the same lease.
2547 if (state
->toack_queue_head
!= NULL
)
2548 dhcp_failover_send_acks(state
);
2550 while ((state
-> partner
.max_flying_updates
>
2551 state
-> cur_unacked_updates
) && state
-> update_queue_head
) {
2552 /* Grab the head of the update queue. */
2553 lease_reference (&lp
, state
-> update_queue_head
, MDL
);
2555 /* Send the update to the peer. */
2556 status
= dhcp_failover_send_bind_update (state
, lp
);
2557 if (status
!= ISC_R_SUCCESS
) {
2558 lease_dereference (&lp
, MDL
);
2561 lp
-> flags
&= ~ON_UPDATE_QUEUE
;
2563 /* Take it off the head of the update queue and put the next
2564 item in the update queue at the head. */
2565 lease_dereference (&state
-> update_queue_head
, MDL
);
2566 if (lp
-> next_pending
) {
2567 lease_reference (&state
-> update_queue_head
,
2568 lp
-> next_pending
, MDL
);
2569 lease_dereference (&lp
-> next_pending
, MDL
);
2571 lease_dereference (&state
-> update_queue_tail
, MDL
);
2574 if (state
-> ack_queue_head
) {
2576 (&state
-> ack_queue_tail
-> next_pending
,
2578 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2580 lease_reference (&state
-> ack_queue_head
, lp
, MDL
);
2582 #if defined (POINTER_DEBUG)
2583 if (lp
-> next_pending
) {
2584 log_error ("ack_queue_tail: lp -> next_pending");
2588 lease_reference (&state
-> ack_queue_tail
, lp
, MDL
);
2589 lp
-> flags
|= ON_ACK_QUEUE
;
2590 lease_dereference (&lp
, MDL
);
2592 /* Count the object as an unacked update. */
2593 state
-> cur_unacked_updates
++;
2595 return ISC_R_SUCCESS
;
2598 /* Queue an update for a lease. Always returns 1 at this point - it's
2599 not an error for this to be called on a lease for which there's no
2602 int dhcp_failover_queue_update (struct lease
*lease
, int immediate
)
2604 dhcp_failover_state_t
*state
;
2606 if (!lease
-> pool
||
2607 !lease
-> pool
-> failover_peer
)
2610 /* If it's already on the update queue, leave it there. */
2611 if (lease
-> flags
& ON_UPDATE_QUEUE
)
2614 /* Get the failover state structure for this lease. */
2615 state
= lease
-> pool
-> failover_peer
;
2617 /* If it's on the ack queue, take it off. */
2618 if (lease
-> flags
& ON_ACK_QUEUE
)
2619 dhcp_failover_ack_queue_remove (state
, lease
);
2621 if (state
-> update_queue_head
) {
2622 lease_reference (&state
-> update_queue_tail
-> next_pending
,
2624 lease_dereference (&state
-> update_queue_tail
, MDL
);
2626 lease_reference (&state
-> update_queue_head
, lease
, MDL
);
2628 #if defined (POINTER_DEBUG)
2629 if (lease
-> next_pending
) {
2630 log_error ("next pending on update queue lease.");
2631 #if defined (DEBUG_RC_HISTORY)
2632 dump_rc_history (lease
);
2637 lease_reference (&state
-> update_queue_tail
, lease
, MDL
);
2638 lease
-> flags
|= ON_UPDATE_QUEUE
;
2640 dhcp_failover_send_updates (state
);
2644 int dhcp_failover_send_acks (dhcp_failover_state_t
*state
)
2646 failover_message_t
*msg
= (failover_message_t
*)0;
2648 /* Must commit all leases prior to acking them. */
2649 if (!commit_leases ())
2652 while (state
-> toack_queue_head
) {
2653 failover_message_reference
2654 (&msg
, state
-> toack_queue_head
, MDL
);
2655 failover_message_dereference
2656 (&state
-> toack_queue_head
, MDL
);
2658 failover_message_reference
2659 (&state
-> toack_queue_head
, msg
-> next
, MDL
);
2662 dhcp_failover_send_bind_ack (state
, msg
, 0, (const char *)0);
2664 failover_message_dereference (&msg
, MDL
);
2667 if (state
-> toack_queue_tail
)
2668 failover_message_dereference (&state
-> toack_queue_tail
, MDL
);
2669 state
-> pending_acks
= 0;
2674 void dhcp_failover_toack_queue_timeout (void *vs
)
2676 dhcp_failover_state_t
*state
= vs
;
2678 #if defined (DEBUG_FAILOVER_TIMING)
2679 log_info ("dhcp_failover_toack_queue_timeout");
2682 dhcp_failover_send_acks (state
);
2685 /* Queue an ack for a message. There is currently no way to queue a
2686 negative ack -- these need to be sent directly. */
2688 int dhcp_failover_queue_ack (dhcp_failover_state_t
*state
,
2689 failover_message_t
*msg
)
2691 if (state
-> toack_queue_head
) {
2692 failover_message_reference
2693 (&state
-> toack_queue_tail
-> next
, msg
, MDL
);
2694 failover_message_dereference (&state
-> toack_queue_tail
, MDL
);
2696 failover_message_reference (&state
-> toack_queue_head
,
2699 failover_message_reference (&state
-> toack_queue_tail
, msg
, MDL
);
2701 state
-> pending_acks
++;
2703 /* Flush the toack queue whenever we exceed half the number of
2704 allowed unacked updates. */
2705 if (state
-> pending_acks
>= state
-> partner
.max_flying_updates
/ 2) {
2706 dhcp_failover_send_acks (state
);
2709 /* Schedule a timeout to flush the ack queue. */
2710 if (state
-> pending_acks
> 0) {
2711 #if defined (DEBUG_FAILOVER_TIMING)
2712 log_info ("add_timeout +2 %s",
2713 "dhcp_failover_toack_queue_timeout");
2715 add_timeout (cur_time
+ 2,
2716 dhcp_failover_toack_queue_timeout
, state
,
2717 (tvref_t
)dhcp_failover_state_reference
,
2718 (tvunref_t
)dhcp_failover_state_dereference
);
2724 void dhcp_failover_ack_queue_remove (dhcp_failover_state_t
*state
,
2725 struct lease
*lease
)
2729 if (!(lease
-> flags
& ON_ACK_QUEUE
))
2732 if (state
-> ack_queue_head
== lease
) {
2733 lease_dereference (&state
-> ack_queue_head
, MDL
);
2734 if (lease
-> next_pending
) {
2735 lease_reference (&state
-> ack_queue_head
,
2736 lease
-> next_pending
, MDL
);
2737 lease_dereference (&lease
-> next_pending
, MDL
);
2739 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2742 for (lp
= state
-> ack_queue_head
;
2743 lp
&& lp
-> next_pending
!= lease
;
2744 lp
= lp
-> next_pending
)
2750 lease_dereference (&lp
-> next_pending
, MDL
);
2751 if (lease
-> next_pending
) {
2752 lease_reference (&lp
-> next_pending
,
2753 lease
-> next_pending
, MDL
);
2754 lease_dereference (&lease
-> next_pending
, MDL
);
2756 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2757 if (lp
-> next_pending
) {
2758 log_error ("state -> ack_queue_tail");
2761 lease_reference (&state
-> ack_queue_tail
, lp
, MDL
);
2765 lease
-> flags
&= ~ON_ACK_QUEUE
;
2766 /* Multiple acks on one XID is an error and may cause badness. */
2767 lease
->last_xid
= 0;
2768 /* XXX: this violates draft-failover. We can't send another
2769 * update just because we forgot about an old one that hasn't
2772 state
-> cur_unacked_updates
--;
2775 * When updating leases as a result of an ack, we defer the commit
2776 * for performance reasons. When there are no more acks pending,
2779 if (state
-> cur_unacked_updates
== 0) {
2784 isc_result_t
dhcp_failover_state_set_value (omapi_object_t
*h
,
2786 omapi_data_string_t
*name
,
2787 omapi_typed_data_t
*value
)
2789 isc_result_t status
;
2791 if (h
-> type
!= dhcp_type_failover_state
)
2792 return ISC_R_INVALIDARG
;
2794 /* This list of successful returns is completely wrong, but the
2795 fastest way to make dhcpctl do something vaguely sane when
2796 you try to change the local state. */
2798 if (!omapi_ds_strcmp (name
, "name")) {
2799 return ISC_R_SUCCESS
;
2800 } else if (!omapi_ds_strcmp (name
, "partner-address")) {
2801 return ISC_R_SUCCESS
;
2802 } else if (!omapi_ds_strcmp (name
, "local-address")) {
2803 return ISC_R_SUCCESS
;
2804 } else if (!omapi_ds_strcmp (name
, "partner-port")) {
2805 return ISC_R_SUCCESS
;
2806 } else if (!omapi_ds_strcmp (name
, "local-port")) {
2807 return ISC_R_SUCCESS
;
2808 } else if (!omapi_ds_strcmp (name
, "max-outstanding-updates")) {
2809 return ISC_R_SUCCESS
;
2810 } else if (!omapi_ds_strcmp (name
, "mclt")) {
2811 return ISC_R_SUCCESS
;
2812 } else if (!omapi_ds_strcmp (name
, "load-balance-max-secs")) {
2813 return ISC_R_SUCCESS
;
2814 } else if (!omapi_ds_strcmp (name
, "load-balance-hba")) {
2815 return ISC_R_SUCCESS
;
2816 } else if (!omapi_ds_strcmp (name
, "partner-state")) {
2817 return ISC_R_SUCCESS
;
2818 } else if (!omapi_ds_strcmp (name
, "local-state")) {
2820 status
= omapi_get_int_value (&l
, value
);
2821 if (status
!= ISC_R_SUCCESS
)
2823 return dhcp_failover_set_state ((dhcp_failover_state_t
*)h
, l
);
2824 } else if (!omapi_ds_strcmp (name
, "partner-stos")) {
2825 return ISC_R_SUCCESS
;
2826 } else if (!omapi_ds_strcmp (name
, "local-stos")) {
2827 return ISC_R_SUCCESS
;
2828 } else if (!omapi_ds_strcmp (name
, "hierarchy")) {
2829 return ISC_R_SUCCESS
;
2830 } else if (!omapi_ds_strcmp (name
, "last-packet-sent")) {
2831 return ISC_R_SUCCESS
;
2832 } else if (!omapi_ds_strcmp (name
, "last-timestamp-received")) {
2833 return ISC_R_SUCCESS
;
2834 } else if (!omapi_ds_strcmp (name
, "skew")) {
2835 return ISC_R_SUCCESS
;
2836 } else if (!omapi_ds_strcmp (name
, "max-response-delay")) {
2837 return ISC_R_SUCCESS
;
2838 } else if (!omapi_ds_strcmp (name
, "cur-unacked-updates")) {
2839 return ISC_R_SUCCESS
;
2842 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
2843 return (*(h
-> inner
-> type
-> set_value
))
2844 (h
-> inner
, id
, name
, value
);
2845 return ISC_R_NOTFOUND
;
2848 void dhcp_failover_keepalive (void *vs
)
2852 void dhcp_failover_reconnect (void *vs
)
2854 dhcp_failover_state_t
*state
= vs
;
2855 isc_result_t status
;
2857 #if defined (DEBUG_FAILOVER_TIMING)
2858 log_info ("dhcp_failover_reconnect");
2860 /* If we already connected the other way, let the connection
2861 recovery code initiate any retry that may be required. */
2862 if (state
-> link_to_peer
)
2865 status
= dhcp_failover_link_initiate ((omapi_object_t
*)state
);
2866 if (status
!= ISC_R_SUCCESS
&& status
!= ISC_R_INCOMPLETE
) {
2867 log_info ("failover peer %s: %s", state
-> name
,
2868 isc_result_totext (status
));
2869 #if defined (DEBUG_FAILOVER_TIMING)
2870 log_info ("add_timeout +90 %s",
2871 "dhcp_failover_listener_restart");
2873 add_timeout (cur_time
+ 90,
2874 dhcp_failover_listener_restart
, state
,
2875 (tvref_t
)dhcp_failover_state_reference
,
2876 (tvunref_t
)dhcp_failover_state_dereference
);
2880 void dhcp_failover_startup_timeout (void *vs
)
2882 dhcp_failover_state_t
*state
= vs
;
2884 #if defined (DEBUG_FAILOVER_TIMING)
2885 log_info ("dhcp_failover_startup_timeout");
2888 dhcp_failover_state_transition (state
, "disconnect");
2891 void dhcp_failover_link_startup_timeout (void *vl
)
2893 dhcp_failover_link_t
*link
= vl
;
2896 for (p
= (omapi_object_t
*)link
; p
-> inner
; p
= p
-> inner
)
2898 for (; p
; p
= p
-> outer
)
2899 if (p
-> type
== omapi_type_connection
)
2902 log_info ("failover: link startup timeout");
2903 omapi_disconnect (p
, 1);
2907 void dhcp_failover_listener_restart (void *vs
)
2909 dhcp_failover_state_t
*state
= vs
;
2910 isc_result_t status
;
2912 #if defined (DEBUG_FAILOVER_TIMING)
2913 log_info ("dhcp_failover_listener_restart");
2916 status
= dhcp_failover_listen ((omapi_object_t
*)state
);
2917 if (status
!= ISC_R_SUCCESS
) {
2918 log_info ("failover peer %s: %s", state
-> name
,
2919 isc_result_totext (status
));
2920 #if defined (DEBUG_FAILOVER_TIMING)
2921 log_info ("add_timeout +90 %s",
2922 "dhcp_failover_listener_restart");
2924 add_timeout (cur_time
+ 90,
2925 dhcp_failover_listener_restart
, state
,
2926 (tvref_t
)dhcp_failover_state_reference
,
2927 (tvunref_t
)dhcp_failover_state_dereference
);
2931 isc_result_t
dhcp_failover_state_get_value (omapi_object_t
*h
,
2933 omapi_data_string_t
*name
,
2934 omapi_value_t
**value
)
2936 dhcp_failover_state_t
*s
;
2937 struct option_cache
*oc
;
2938 struct data_string ds
;
2939 isc_result_t status
;
2941 if (h
-> type
!= dhcp_type_failover_state
)
2942 return ISC_R_INVALIDARG
;
2943 s
= (dhcp_failover_state_t
*)h
;
2945 if (!omapi_ds_strcmp (name
, "name")) {
2947 return omapi_make_string_value (value
,
2948 name
, s
-> name
, MDL
);
2949 return ISC_R_NOTFOUND
;
2950 } else if (!omapi_ds_strcmp (name
, "partner-address")) {
2951 oc
= s
-> partner
.address
;
2953 memset (&ds
, 0, sizeof ds
);
2954 if (!evaluate_option_cache (&ds
, (struct packet
*)0,
2956 (struct client_state
*)0,
2957 (struct option_state
*)0,
2958 (struct option_state
*)0,
2959 &global_scope
, oc
, MDL
)) {
2960 return ISC_R_NOTFOUND
;
2962 status
= omapi_make_const_value (value
,
2963 name
, ds
.data
, ds
.len
, MDL
);
2964 /* Disgusting kludge: */
2965 if (oc
== s
-> me
.address
&& !s
-> server_identifier
.len
)
2966 data_string_copy (&s
-> server_identifier
, &ds
, MDL
);
2967 data_string_forget (&ds
, MDL
);
2969 } else if (!omapi_ds_strcmp (name
, "local-address")) {
2970 oc
= s
-> me
.address
;
2972 } else if (!omapi_ds_strcmp (name
, "partner-port")) {
2973 return omapi_make_int_value (value
, name
,
2974 s
-> partner
.port
, MDL
);
2975 } else if (!omapi_ds_strcmp (name
, "local-port")) {
2976 return omapi_make_int_value (value
,
2977 name
, s
-> me
.port
, MDL
);
2978 } else if (!omapi_ds_strcmp (name
, "max-outstanding-updates")) {
2979 return omapi_make_uint_value (value
, name
,
2980 s
-> me
.max_flying_updates
,
2982 } else if (!omapi_ds_strcmp (name
, "mclt")) {
2983 return omapi_make_uint_value (value
, name
, s
-> mclt
, MDL
);
2984 } else if (!omapi_ds_strcmp (name
, "load-balance-max-secs")) {
2985 return omapi_make_int_value (value
, name
,
2986 s
-> load_balance_max_secs
, MDL
);
2987 } else if (!omapi_ds_strcmp (name
, "load-balance-hba")) {
2989 return omapi_make_const_value (value
, name
,
2991 return ISC_R_NOTFOUND
;
2992 } else if (!omapi_ds_strcmp (name
, "partner-state")) {
2993 return omapi_make_uint_value (value
, name
,
2994 s
-> partner
.state
, MDL
);
2995 } else if (!omapi_ds_strcmp (name
, "local-state")) {
2996 return omapi_make_uint_value (value
, name
,
2997 s
-> me
.state
, MDL
);
2998 } else if (!omapi_ds_strcmp (name
, "partner-stos")) {
2999 return omapi_make_int_value (value
, name
,
3000 s
-> partner
.stos
, MDL
);
3001 } else if (!omapi_ds_strcmp (name
, "local-stos")) {
3002 return omapi_make_int_value (value
, name
,
3004 } else if (!omapi_ds_strcmp (name
, "hierarchy")) {
3005 return omapi_make_uint_value (value
, name
, s
-> i_am
, MDL
);
3006 } else if (!omapi_ds_strcmp (name
, "last-packet-sent")) {
3007 return omapi_make_int_value (value
, name
,
3008 s
-> last_packet_sent
, MDL
);
3009 } else if (!omapi_ds_strcmp (name
, "last-timestamp-received")) {
3010 return omapi_make_int_value (value
, name
,
3011 s
-> last_timestamp_received
,
3013 } else if (!omapi_ds_strcmp (name
, "skew")) {
3014 return omapi_make_int_value (value
, name
, s
-> skew
, MDL
);
3015 } else if (!omapi_ds_strcmp (name
, "max-response-delay")) {
3016 return omapi_make_uint_value (value
, name
,
3017 s
-> me
.max_response_delay
,
3019 } else if (!omapi_ds_strcmp (name
, "cur-unacked-updates")) {
3020 return omapi_make_int_value (value
, name
,
3021 s
-> cur_unacked_updates
, MDL
);
3024 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
3025 return (*(h
-> inner
-> type
-> get_value
))
3026 (h
-> inner
, id
, name
, value
);
3027 return ISC_R_NOTFOUND
;
3030 isc_result_t
dhcp_failover_state_destroy (omapi_object_t
*h
,
3031 const char *file
, int line
)
3033 dhcp_failover_state_t
*s
;
3035 if (h
-> type
!= dhcp_type_failover_state
)
3036 return ISC_R_INVALIDARG
;
3037 s
= (dhcp_failover_state_t
*)h
;
3039 if (s
-> link_to_peer
)
3040 dhcp_failover_link_dereference (&s
-> link_to_peer
, file
, line
);
3042 dfree (s
-> name
, MDL
);
3043 s
-> name
= (char *)0;
3045 if (s
-> partner
.address
)
3046 option_cache_dereference (&s
-> partner
.address
, file
, line
);
3047 if (s
-> me
.address
)
3048 option_cache_dereference (&s
-> me
.address
, file
, line
);
3050 dfree (s
-> hba
, file
, line
);
3051 s
-> hba
= (u_int8_t
*)0;
3053 if (s
-> update_queue_head
)
3054 lease_dereference (&s
-> update_queue_head
, file
, line
);
3055 if (s
-> update_queue_tail
)
3056 lease_dereference (&s
-> update_queue_tail
, file
, line
);
3057 if (s
-> ack_queue_head
)
3058 lease_dereference (&s
-> ack_queue_head
, file
, line
);
3059 if (s
-> ack_queue_tail
)
3060 lease_dereference (&s
-> ack_queue_tail
, file
, line
);
3061 if (s
-> send_update_done
)
3062 lease_dereference (&s
-> send_update_done
, file
, line
);
3063 if (s
-> toack_queue_head
)
3064 failover_message_dereference (&s
-> toack_queue_head
,
3066 if (s
-> toack_queue_tail
)
3067 failover_message_dereference (&s
-> toack_queue_tail
,
3069 return ISC_R_SUCCESS
;
3072 /* Write all the published values associated with the object through the
3073 specified connection. */
3075 isc_result_t
dhcp_failover_state_stuff (omapi_object_t
*c
,
3079 dhcp_failover_state_t
*s
;
3080 omapi_connection_object_t
*conn
;
3081 isc_result_t status
;
3083 if (c
-> type
!= omapi_type_connection
)
3084 return ISC_R_INVALIDARG
;
3085 conn
= (omapi_connection_object_t
*)c
;
3087 if (h
-> type
!= dhcp_type_failover_state
)
3088 return ISC_R_INVALIDARG
;
3089 s
= (dhcp_failover_state_t
*)h
;
3091 status
= omapi_connection_put_name (c
, "name");
3092 if (status
!= ISC_R_SUCCESS
)
3094 status
= omapi_connection_put_string (c
, s
-> name
);
3095 if (status
!= ISC_R_SUCCESS
)
3098 status
= omapi_connection_put_name (c
, "partner-address");
3099 if (status
!= ISC_R_SUCCESS
)
3101 status
= omapi_connection_put_uint32 (c
, sizeof s
-> partner
.address
);
3102 if (status
!= ISC_R_SUCCESS
)
3104 status
= omapi_connection_copyin (c
, (u_int8_t
*)&s
-> partner
.address
,
3105 sizeof s
-> partner
.address
);
3106 if (status
!= ISC_R_SUCCESS
)
3109 status
= omapi_connection_put_name (c
, "partner-port");
3110 if (status
!= ISC_R_SUCCESS
)
3112 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3113 if (status
!= ISC_R_SUCCESS
)
3115 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> partner
.port
);
3116 if (status
!= ISC_R_SUCCESS
)
3119 status
= omapi_connection_put_name (c
, "local-address");
3120 if (status
!= ISC_R_SUCCESS
)
3122 status
= omapi_connection_put_uint32 (c
, sizeof s
-> me
.address
);
3123 if (status
!= ISC_R_SUCCESS
)
3125 status
= omapi_connection_copyin (c
, (u_int8_t
*)&s
-> me
.address
,
3126 sizeof s
-> me
.address
);
3127 if (status
!= ISC_R_SUCCESS
)
3130 status
= omapi_connection_put_name (c
, "local-port");
3131 if (status
!= ISC_R_SUCCESS
)
3133 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3134 if (status
!= ISC_R_SUCCESS
)
3136 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> me
.port
);
3137 if (status
!= ISC_R_SUCCESS
)
3140 status
= omapi_connection_put_name (c
, "max-outstanding-updates");
3141 if (status
!= ISC_R_SUCCESS
)
3143 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3144 if (status
!= ISC_R_SUCCESS
)
3146 status
= omapi_connection_put_uint32 (c
,
3147 s
-> me
.max_flying_updates
);
3148 if (status
!= ISC_R_SUCCESS
)
3151 status
= omapi_connection_put_name (c
, "mclt");
3152 if (status
!= ISC_R_SUCCESS
)
3154 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3155 if (status
!= ISC_R_SUCCESS
)
3157 status
= omapi_connection_put_uint32 (c
, s
-> mclt
);
3158 if (status
!= ISC_R_SUCCESS
)
3161 status
= omapi_connection_put_name (c
, "load-balance-max-secs");
3162 if (status
!= ISC_R_SUCCESS
)
3164 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3165 if (status
!= ISC_R_SUCCESS
)
3167 status
= (omapi_connection_put_uint32
3168 (c
, (u_int32_t
)s
-> load_balance_max_secs
));
3169 if (status
!= ISC_R_SUCCESS
)
3174 status
= omapi_connection_put_name (c
, "load-balance-hba");
3175 if (status
!= ISC_R_SUCCESS
)
3177 status
= omapi_connection_put_uint32 (c
, 32);
3178 if (status
!= ISC_R_SUCCESS
)
3180 status
= omapi_connection_copyin (c
, s
-> hba
, 32);
3181 if (status
!= ISC_R_SUCCESS
)
3185 status
= omapi_connection_put_name (c
, "partner-state");
3186 if (status
!= ISC_R_SUCCESS
)
3188 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3189 if (status
!= ISC_R_SUCCESS
)
3191 status
= omapi_connection_put_uint32 (c
, s
-> partner
.state
);
3192 if (status
!= ISC_R_SUCCESS
)
3195 status
= omapi_connection_put_name (c
, "local-state");
3196 if (status
!= ISC_R_SUCCESS
)
3198 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3199 if (status
!= ISC_R_SUCCESS
)
3201 status
= omapi_connection_put_uint32 (c
, s
-> me
.state
);
3202 if (status
!= ISC_R_SUCCESS
)
3205 status
= omapi_connection_put_name (c
, "partner-stos");
3206 if (status
!= ISC_R_SUCCESS
)
3208 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3209 if (status
!= ISC_R_SUCCESS
)
3211 status
= omapi_connection_put_uint32 (c
,
3212 (u_int32_t
)s
-> partner
.stos
);
3213 if (status
!= ISC_R_SUCCESS
)
3216 status
= omapi_connection_put_name (c
, "local-stos");
3217 if (status
!= ISC_R_SUCCESS
)
3219 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3220 if (status
!= ISC_R_SUCCESS
)
3222 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> me
.stos
);
3223 if (status
!= ISC_R_SUCCESS
)
3226 status
= omapi_connection_put_name (c
, "hierarchy");
3227 if (status
!= ISC_R_SUCCESS
)
3229 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3230 if (status
!= ISC_R_SUCCESS
)
3232 status
= omapi_connection_put_uint32 (c
, s
-> i_am
);
3233 if (status
!= ISC_R_SUCCESS
)
3236 status
= omapi_connection_put_name (c
, "last-packet-sent");
3237 if (status
!= ISC_R_SUCCESS
)
3239 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3240 if (status
!= ISC_R_SUCCESS
)
3242 status
= (omapi_connection_put_uint32
3243 (c
, (u_int32_t
)s
-> last_packet_sent
));
3244 if (status
!= ISC_R_SUCCESS
)
3247 status
= omapi_connection_put_name (c
, "last-timestamp-received");
3248 if (status
!= ISC_R_SUCCESS
)
3250 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3251 if (status
!= ISC_R_SUCCESS
)
3253 status
= (omapi_connection_put_uint32
3254 (c
, (u_int32_t
)s
-> last_timestamp_received
));
3255 if (status
!= ISC_R_SUCCESS
)
3258 status
= omapi_connection_put_name (c
, "skew");
3259 if (status
!= ISC_R_SUCCESS
)
3261 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3262 if (status
!= ISC_R_SUCCESS
)
3264 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> skew
);
3265 if (status
!= ISC_R_SUCCESS
)
3268 status
= omapi_connection_put_name (c
, "max-response-delay");
3269 if (status
!= ISC_R_SUCCESS
)
3271 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3272 if (status
!= ISC_R_SUCCESS
)
3274 status
= (omapi_connection_put_uint32
3275 (c
, (u_int32_t
)s
-> me
.max_response_delay
));
3276 if (status
!= ISC_R_SUCCESS
)
3279 status
= omapi_connection_put_name (c
, "cur-unacked-updates");
3280 if (status
!= ISC_R_SUCCESS
)
3282 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3283 if (status
!= ISC_R_SUCCESS
)
3285 status
= (omapi_connection_put_uint32
3286 (c
, (u_int32_t
)s
-> cur_unacked_updates
));
3287 if (status
!= ISC_R_SUCCESS
)
3290 if (h
-> inner
&& h
-> inner
-> type
-> stuff_values
)
3291 return (*(h
-> inner
-> type
-> stuff_values
)) (c
, id
,
3293 return ISC_R_SUCCESS
;
3296 isc_result_t
dhcp_failover_state_lookup (omapi_object_t
**sp
,
3298 omapi_object_t
*ref
)
3300 omapi_value_t
*tv
= (omapi_value_t
*)0;
3301 isc_result_t status
;
3302 dhcp_failover_state_t
*s
;
3305 return ISC_R_NOKEYS
;
3307 /* First see if we were sent a handle. */
3308 status
= omapi_get_value_str (ref
, id
, "handle", &tv
);
3309 if (status
== ISC_R_SUCCESS
) {
3310 status
= omapi_handle_td_lookup (sp
, tv
-> value
);
3312 omapi_value_dereference (&tv
, MDL
);
3313 if (status
!= ISC_R_SUCCESS
)
3316 /* Don't return the object if the type is wrong. */
3317 if ((*sp
) -> type
!= dhcp_type_failover_state
) {
3318 omapi_object_dereference (sp
, MDL
);
3319 return ISC_R_INVALIDARG
;
3323 /* Look the failover state up by peer name. */
3324 status
= omapi_get_value_str (ref
, id
, "name", &tv
);
3325 if (status
== ISC_R_SUCCESS
) {
3326 for (s
= failover_states
; s
; s
= s
-> next
) {
3327 unsigned l
= strlen (s
-> name
);
3328 if (l
== tv
-> value
-> u
.buffer
.len
&&
3330 tv
-> value
-> u
.buffer
.value
, l
))
3333 omapi_value_dereference (&tv
, MDL
);
3335 /* If we already have a lease, and it's not the same one,
3336 then the query was invalid. */
3337 if (*sp
&& *sp
!= (omapi_object_t
*)s
) {
3338 omapi_object_dereference (sp
, MDL
);
3339 return ISC_R_KEYCONFLICT
;
3342 omapi_object_dereference (sp
, MDL
);
3343 return ISC_R_NOTFOUND
;
3345 /* XXX fix so that hash lookup itself creates
3346 XXX the reference. */
3347 omapi_object_reference (sp
, (omapi_object_t
*)s
, MDL
);
3350 /* If we get to here without finding a lease, no valid key was
3353 return ISC_R_NOKEYS
;
3354 return ISC_R_SUCCESS
;
3357 isc_result_t
dhcp_failover_state_create (omapi_object_t
**sp
,
3360 return ISC_R_NOTIMPLEMENTED
;
3363 isc_result_t
dhcp_failover_state_remove (omapi_object_t
*sp
,
3366 return ISC_R_NOTIMPLEMENTED
;
3369 int dhcp_failover_state_match (dhcp_failover_state_t
*state
,
3370 u_int8_t
*addr
, unsigned addrlen
)
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
, ...)
3955 failover_option_t
*option
;
3956 unsigned char *opbuf
;
3957 isc_result_t status
= ISC_R_SUCCESS
;
3960 /* Run through the argument list once to compute the length of
3961 the option portion of the message. */
3962 va_start (list
, xid
);
3963 while ((option
= va_arg (list
, failover_option_t
*))) {
3964 if (option
!= &skip_failover_option
)
3965 size
+= option
-> count
;
3966 if (option
== &null_failover_option
)
3971 /* Allocate an option buffer, unless we got an error. */
3972 if (!bad_option
&& size
) {
3973 opbuf
= dmalloc (size
, MDL
);
3975 status
= ISC_R_NOMEMORY
;
3977 opbuf
= (unsigned char *)0;
3979 va_start (list
, xid
);
3980 while ((option
= va_arg (list
, failover_option_t
*))) {
3981 if (option
== &skip_failover_option
)
3983 if (!bad_option
&& opbuf
)
3984 memcpy (&opbuf
[opix
],
3985 option
-> data
, option
-> count
);
3986 if (option
!= &null_failover_option
&&
3987 option
!= &skip_failover_option
) {
3988 opix
+= option
-> count
;
3989 dfree (option
-> data
, MDL
);
3990 dfree (option
, MDL
);
3996 return ISC_R_INVALIDARG
;
3998 /* Now send the message header. */
4000 /* Message length. */
4001 status
= omapi_connection_put_uint16 (connection
, size
+ 12);
4002 if (status
!= ISC_R_SUCCESS
)
4007 status
= omapi_connection_copyin (connection
, &cbuf
, 1);
4008 if (status
!= ISC_R_SUCCESS
)
4011 /* Payload offset. */
4013 status
= omapi_connection_copyin (connection
, &cbuf
, 1);
4014 if (status
!= ISC_R_SUCCESS
)
4018 status
= omapi_connection_put_uint32 (connection
, (u_int32_t
)cur_time
);
4019 if (status
!= ISC_R_SUCCESS
)
4022 /* Transaction ID. */
4023 status
= omapi_connection_put_uint32(connection
, xid
);
4024 if (status
!= ISC_R_SUCCESS
)
4029 status
= omapi_connection_copyin (connection
, opbuf
, size
);
4030 if (status
!= ISC_R_SUCCESS
)
4034 if (link
-> state_object
&&
4035 link
-> state_object
-> link_to_peer
== link
) {
4036 #if defined (DEBUG_FAILOVER_TIMING)
4037 log_info ("add_timeout +%d %s",
4038 (int)(link
-> state_object
->
4039 partner
.max_response_delay
) / 3,
4040 "dhcp_failover_send_contact");
4042 add_timeout (cur_time
+
4043 (int)(link
-> state_object
->
4044 partner
.max_response_delay
) / 3,
4045 dhcp_failover_send_contact
, link
-> state_object
,
4046 (tvref_t
)dhcp_failover_state_reference
,
4047 (tvunref_t
)dhcp_failover_state_dereference
);
4054 log_info ("dhcp_failover_put_message: something went wrong.");
4055 omapi_disconnect (connection
, 1);
4059 void dhcp_failover_timeout (void *vstate
)
4061 dhcp_failover_state_t
*state
= vstate
;
4062 dhcp_failover_link_t
*link
;
4064 #if defined (DEBUG_FAILOVER_TIMING)
4065 log_info ("dhcp_failover_timeout");
4068 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
4070 link
= state
-> link_to_peer
;
4073 link
-> outer
-> type
!= omapi_type_connection
)
4076 log_error ("timeout waiting for failover peer %s", state
-> name
);
4078 /* If we haven't gotten a timely response, blow away the connection.
4079 This will cause the state to change automatically. */
4080 omapi_disconnect (link
-> outer
, 1);
4083 void dhcp_failover_send_contact (void *vstate
)
4085 dhcp_failover_state_t
*state
= vstate
;
4086 dhcp_failover_link_t
*link
;
4087 isc_result_t status
;
4089 #if defined (DEBUG_FAILOVER_MESSAGES)
4091 unsigned obufix
= 0;
4093 # define FMA obuf, &obufix, sizeof obuf
4094 failover_print (FMA
, "(contact");
4096 # define FMA (char *)0, (unsigned *)0, 0
4099 #if defined (DEBUG_FAILOVER_TIMING)
4100 log_info ("dhcp_failover_send_contact");
4103 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
4105 link
= state
-> link_to_peer
;
4108 link
-> outer
-> type
!= omapi_type_connection
)
4111 status
= (dhcp_failover_put_message
4112 (link
, link
-> outer
,
4113 FTM_CONTACT
, link
->xid
++,
4114 (failover_option_t
*)0));
4116 #if defined (DEBUG_FAILOVER_MESSAGES)
4117 if (status
!= ISC_R_SUCCESS
)
4118 failover_print (FMA
, " (failed)");
4119 failover_print (FMA
, ")");
4121 log_debug ("%s", obuf
);
4127 isc_result_t
dhcp_failover_send_state (dhcp_failover_state_t
*state
)
4129 dhcp_failover_link_t
*link
;
4130 isc_result_t status
;
4132 #if defined (DEBUG_FAILOVER_MESSAGES)
4134 unsigned obufix
= 0;
4136 # define FMA obuf, &obufix, sizeof obuf
4137 failover_print (FMA
, "(state");
4139 # define FMA (char *)0, (unsigned *)0, 0
4142 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
4143 return ISC_R_INVALIDARG
;
4144 link
= state
-> link_to_peer
;
4147 link
-> outer
-> type
!= omapi_type_connection
)
4148 return ISC_R_INVALIDARG
;
4150 status
= (dhcp_failover_put_message
4151 (link
, link
-> outer
,
4152 FTM_STATE
, link
->xid
++,
4153 dhcp_failover_make_option (FTO_SERVER_STATE
, FMA
,
4154 (state
-> me
.state
== startup
4155 ? state
-> saved_state
4156 : state
-> me
.state
)),
4157 dhcp_failover_make_option
4158 (FTO_SERVER_FLAGS
, FMA
,
4159 (state
-> service_state
== service_startup
4160 ? FTF_SERVER_STARTUP
: 0)),
4161 dhcp_failover_make_option (FTO_STOS
, FMA
, state
-> me
.stos
),
4162 (failover_option_t
*)0));
4164 #if defined (DEBUG_FAILOVER_MESSAGES)
4165 if (status
!= ISC_R_SUCCESS
)
4166 failover_print (FMA
, " (failed)");
4167 failover_print (FMA
, ")");
4169 log_debug ("%s", obuf
);
4172 return ISC_R_SUCCESS
;
4175 /* Send a connect message. */
4177 isc_result_t
dhcp_failover_send_connect (omapi_object_t
*l
)
4179 dhcp_failover_link_t
*link
;
4180 dhcp_failover_state_t
*state
;
4181 isc_result_t status
;
4182 #if defined (DEBUG_FAILOVER_MESSAGES)
4184 unsigned obufix
= 0;
4186 # define FMA obuf, &obufix, sizeof obuf
4187 failover_print (FMA
, "(connect");
4189 # define FMA (char *)0, (unsigned *)0, 0
4192 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
4193 return ISC_R_INVALIDARG
;
4194 link
= (dhcp_failover_link_t
*)l
;
4195 state
= link
-> state_object
;
4196 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
4197 return ISC_R_INVALIDARG
;
4200 (dhcp_failover_put_message
4202 FTM_CONNECT
, link
->xid
++,
4203 dhcp_failover_make_option(FTO_RELATIONSHIP_NAME
, FMA
,
4204 strlen(state
->name
), state
->name
),
4205 dhcp_failover_make_option (FTO_MAX_UNACKED
, FMA
,
4206 state
-> me
.max_flying_updates
),
4207 dhcp_failover_make_option (FTO_RECEIVE_TIMER
, FMA
,
4208 state
-> me
.max_response_delay
),
4209 dhcp_failover_option_printf(FTO_VENDOR_CLASS
, FMA
,
4210 "isc-%s", PACKAGE_VERSION
),
4211 dhcp_failover_make_option (FTO_PROTOCOL_VERSION
, FMA
,
4212 DHCP_FAILOVER_VERSION
),
4213 dhcp_failover_make_option (FTO_TLS_REQUEST
, FMA
,
4215 dhcp_failover_make_option (FTO_MCLT
, FMA
,
4218 ? dhcp_failover_make_option (FTO_HBA
, FMA
, 32, state
-> hba
)
4219 : &skip_failover_option
),
4220 (failover_option_t
*)0));
4222 #if defined (DEBUG_FAILOVER_MESSAGES)
4223 if (status
!= ISC_R_SUCCESS
)
4224 failover_print (FMA
, " (failed)");
4225 failover_print (FMA
, ")");
4227 log_debug ("%s", obuf
);
4233 isc_result_t
dhcp_failover_send_connectack (omapi_object_t
*l
,
4234 dhcp_failover_state_t
*state
,
4235 int reason
, const char *errmsg
)
4237 dhcp_failover_link_t
*link
;
4238 isc_result_t status
;
4239 #if defined (DEBUG_FAILOVER_MESSAGES)
4241 unsigned obufix
= 0;
4243 # define FMA obuf, &obufix, sizeof obuf
4244 failover_print (FMA
, "(connectack");
4246 # define FMA (char *)0, (unsigned *)0, 0
4249 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
4250 return ISC_R_INVALIDARG
;
4251 link
= (dhcp_failover_link_t
*)l
;
4252 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
4253 return ISC_R_INVALIDARG
;
4256 (dhcp_failover_put_message
4258 FTM_CONNECTACK
, link
->imsg
->xid
,
4260 ? dhcp_failover_make_option(FTO_RELATIONSHIP_NAME
, FMA
,
4261 strlen(state
->name
), state
->name
)
4262 : (link
->imsg
->options_present
& FTB_RELATIONSHIP_NAME
)
4263 ? &link
->imsg
->relationship_name
4264 : &skip_failover_option
,
4266 ? dhcp_failover_make_option (FTO_MAX_UNACKED
, FMA
,
4267 state
-> me
.max_flying_updates
)
4268 : &skip_failover_option
,
4270 ? dhcp_failover_make_option (FTO_RECEIVE_TIMER
, FMA
,
4271 state
-> me
.max_response_delay
)
4272 : &skip_failover_option
,
4273 dhcp_failover_option_printf(FTO_VENDOR_CLASS
, FMA
,
4274 "isc-%s", PACKAGE_VERSION
),
4275 dhcp_failover_make_option (FTO_PROTOCOL_VERSION
, FMA
,
4276 DHCP_FAILOVER_VERSION
),
4277 (link
->imsg
->options_present
& FTB_TLS_REQUEST
)
4278 ? dhcp_failover_make_option(FTO_TLS_REPLY
, FMA
,
4280 : &skip_failover_option
,
4282 ? dhcp_failover_make_option (FTO_REJECT_REASON
,
4284 : &skip_failover_option
,
4286 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4287 strlen (errmsg
), errmsg
)
4288 : &skip_failover_option
,
4289 (failover_option_t
*)0));
4291 #if defined (DEBUG_FAILOVER_MESSAGES)
4292 if (status
!= ISC_R_SUCCESS
)
4293 failover_print (FMA
, " (failed)");
4294 failover_print (FMA
, ")");
4296 log_debug ("%s", obuf
);
4302 isc_result_t
dhcp_failover_send_disconnect (omapi_object_t
*l
,
4304 const char *message
)
4306 dhcp_failover_link_t
*link
;
4307 dhcp_failover_state_t
*state
;
4308 isc_result_t status
;
4309 #if defined (DEBUG_FAILOVER_MESSAGES)
4311 unsigned obufix
= 0;
4313 # define FMA obuf, &obufix, sizeof obuf
4314 failover_print (FMA
, "(disconnect");
4316 # define FMA (char *)0, (unsigned *)0, 0
4319 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
4320 return ISC_R_INVALIDARG
;
4321 link
= (dhcp_failover_link_t
*)l
;
4322 state
= link
-> state_object
;
4323 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
4324 return ISC_R_INVALIDARG
;
4326 if (!message
&& reason
)
4327 message
= dhcp_failover_reject_reason_print (reason
);
4329 status
= (dhcp_failover_put_message
4331 FTM_DISCONNECT
, link
->xid
++,
4332 dhcp_failover_make_option (FTO_REJECT_REASON
,
4335 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4336 strlen (message
), message
)
4337 : &skip_failover_option
),
4338 (failover_option_t
*)0));
4340 #if defined (DEBUG_FAILOVER_MESSAGES)
4341 if (status
!= ISC_R_SUCCESS
)
4342 failover_print (FMA
, " (failed)");
4343 failover_print (FMA
, ")");
4345 log_debug ("%s", obuf
);
4351 /* Send a Bind Update message. */
4353 isc_result_t
dhcp_failover_send_bind_update (dhcp_failover_state_t
*state
,
4354 struct lease
*lease
)
4356 dhcp_failover_link_t
*link
;
4357 isc_result_t status
;
4359 binding_state_t transmit_state
;
4360 #if defined (DEBUG_FAILOVER_MESSAGES)
4362 unsigned obufix
= 0;
4364 # define FMA obuf, &obufix, sizeof obuf
4365 failover_print (FMA
, "(bndupd");
4367 # define FMA (char *)0, (unsigned *)0, 0
4370 if (!state
-> link_to_peer
||
4371 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4372 return ISC_R_INVALIDARG
;
4373 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4375 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4376 return ISC_R_INVALIDARG
;
4378 transmit_state
= lease
->desired_binding_state
;
4379 if (lease
->flags
& RESERVED_LEASE
) {
4380 /* If we are listing an allocable (not yet ACTIVE etc) lease
4381 * as reserved, toggle to the peer's 'free state', per the
4382 * draft. This gives the peer permission to alloc it to the
4383 * chaddr/uid-named client.
4385 if ((state
->i_am
== primary
) && (transmit_state
== FTS_FREE
))
4386 transmit_state
= FTS_BACKUP
;
4387 else if ((state
->i_am
== secondary
) &&
4388 (transmit_state
== FTS_BACKUP
))
4389 transmit_state
= FTS_FREE
;
4391 flags
|= FTF_IP_FLAG_RESERVE
;
4393 if (lease
->flags
& BOOTP_LEASE
)
4394 flags
|= FTF_IP_FLAG_BOOTP
;
4396 /* last_xid == 0 is illegal, seek past zero if we hit it. */
4400 lease
->last_xid
= link
->xid
++;
4402 /* Send the update. */
4403 status
= (dhcp_failover_put_message
4404 (link
, link
-> outer
,
4405 FTM_BNDUPD
, lease
->last_xid
,
4406 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS
, FMA
,
4407 lease
-> ip_addr
.len
,
4408 lease
-> ip_addr
.iabuf
),
4409 dhcp_failover_make_option (FTO_BINDING_STATUS
, FMA
,
4410 lease
-> desired_binding_state
),
4412 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER
, FMA
,
4415 : &skip_failover_option
,
4416 lease
-> hardware_addr
.hlen
4417 ? dhcp_failover_make_option (FTO_CHADDR
, FMA
,
4418 lease
-> hardware_addr
.hlen
,
4419 lease
-> hardware_addr
.hbuf
)
4420 : &skip_failover_option
,
4421 dhcp_failover_make_option (FTO_LEASE_EXPIRY
, FMA
,
4423 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY
, FMA
,
4425 dhcp_failover_make_option (FTO_STOS
, FMA
,
4427 dhcp_failover_make_option (FTO_CLTT
, FMA
,
4429 flags
? dhcp_failover_make_option(FTO_IP_FLAGS
, FMA
,
4431 &skip_failover_option
, /* No IP_FLAGS */
4432 &skip_failover_option
, /* XXX DDNS */
4433 &skip_failover_option
, /* XXX request options */
4434 &skip_failover_option
, /* XXX reply options */
4435 (failover_option_t
*)0));
4437 #if defined (DEBUG_FAILOVER_MESSAGES)
4438 if (status
!= ISC_R_SUCCESS
)
4439 failover_print (FMA
, " (failed)");
4440 failover_print (FMA
, ")");
4442 log_debug ("%s", obuf
);
4448 /* Send a Bind ACK message. */
4450 isc_result_t
dhcp_failover_send_bind_ack (dhcp_failover_state_t
*state
,
4451 failover_message_t
*msg
,
4452 int reason
, const char *message
)
4454 dhcp_failover_link_t
*link
;
4455 isc_result_t status
;
4456 #if defined (DEBUG_FAILOVER_MESSAGES)
4458 unsigned obufix
= 0;
4460 # define FMA obuf, &obufix, sizeof obuf
4461 failover_print (FMA
, "(bndack");
4463 # define FMA (char *)0, (unsigned *)0, 0
4466 if (!state
-> link_to_peer
||
4467 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4468 return ISC_R_INVALIDARG
;
4469 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4471 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4472 return ISC_R_INVALIDARG
;
4474 if (!message
&& reason
)
4475 message
= dhcp_failover_reject_reason_print (reason
);
4477 /* Send the update. */
4478 status
= (dhcp_failover_put_message
4479 (link
, link
-> outer
,
4480 FTM_BNDACK
, msg
->xid
,
4481 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS
, FMA
,
4482 sizeof msg
-> assigned_addr
,
4483 &msg
-> assigned_addr
),
4484 #ifdef DO_BNDACK_SHOULD_NOT
4485 dhcp_failover_make_option (FTO_BINDING_STATUS
, FMA
,
4486 msg
-> binding_status
),
4487 (msg
-> options_present
& FTB_CLIENT_IDENTIFIER
)
4488 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER
, FMA
,
4489 msg
-> client_identifier
.count
,
4490 msg
-> client_identifier
.data
)
4491 : &skip_failover_option
,
4492 (msg
-> options_present
& FTB_CHADDR
)
4493 ? dhcp_failover_make_option (FTO_CHADDR
, FMA
,
4494 msg
-> chaddr
.count
,
4496 : &skip_failover_option
,
4497 dhcp_failover_make_option (FTO_LEASE_EXPIRY
, FMA
,
4499 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY
, FMA
,
4500 msg
-> potential_expiry
),
4501 dhcp_failover_make_option (FTO_STOS
, FMA
,
4503 dhcp_failover_make_option (FTO_CLTT
, FMA
,
4505 ((msg
->options_present
& FTB_IP_FLAGS
) && msg
->ip_flags
) ?
4506 dhcp_failover_make_option(FTO_IP_FLAGS
, FMA
,
4508 : &skip_failover_option
,
4509 #endif /* DO_BNDACK_SHOULD_NOT */
4511 ? dhcp_failover_make_option(FTO_REJECT_REASON
, FMA
, reason
)
4512 : &skip_failover_option
,
4514 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4515 strlen (message
), message
)
4516 : &skip_failover_option
,
4517 #ifdef DO_BNDACK_SHOULD_NOT
4518 &skip_failover_option
, /* XXX DDNS */
4519 &skip_failover_option
, /* XXX request options */
4520 &skip_failover_option
, /* XXX reply options */
4521 #endif /* DO_BNDACK_SHOULD_NOT */
4522 (failover_option_t
*)0));
4524 #if defined (DEBUG_FAILOVER_MESSAGES)
4525 if (status
!= ISC_R_SUCCESS
)
4526 failover_print (FMA
, " (failed)");
4527 failover_print (FMA
, ")");
4529 log_debug ("%s", obuf
);
4535 isc_result_t
dhcp_failover_send_poolreq (dhcp_failover_state_t
*state
)
4537 dhcp_failover_link_t
*link
;
4538 isc_result_t status
;
4539 #if defined (DEBUG_FAILOVER_MESSAGES)
4541 unsigned obufix
= 0;
4543 # define FMA obuf, &obufix, sizeof obuf
4544 failover_print (FMA
, "(poolreq");
4546 # define FMA (char *)0, (unsigned *)0, 0
4549 if (!state
-> link_to_peer
||
4550 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4551 return ISC_R_INVALIDARG
;
4552 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4554 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4555 return ISC_R_INVALIDARG
;
4557 status
= (dhcp_failover_put_message
4558 (link
, link
-> outer
,
4559 FTM_POOLREQ
, link
->xid
++,
4560 (failover_option_t
*)0));
4562 #if defined (DEBUG_FAILOVER_MESSAGES)
4563 if (status
!= ISC_R_SUCCESS
)
4564 failover_print (FMA
, " (failed)");
4565 failover_print (FMA
, ")");
4567 log_debug ("%s", obuf
);
4573 isc_result_t
dhcp_failover_send_poolresp (dhcp_failover_state_t
*state
,
4576 dhcp_failover_link_t
*link
;
4577 isc_result_t status
;
4578 #if defined (DEBUG_FAILOVER_MESSAGES)
4580 unsigned obufix
= 0;
4582 # define FMA obuf, &obufix, sizeof obuf
4583 failover_print (FMA
, "(poolresp");
4585 # define FMA (char *)0, (unsigned *)0, 0
4588 if (!state
-> link_to_peer
||
4589 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4590 return ISC_R_INVALIDARG
;
4591 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4593 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4594 return ISC_R_INVALIDARG
;
4596 status
= (dhcp_failover_put_message
4597 (link
, link
-> outer
,
4598 FTM_POOLRESP
, link
->imsg
->xid
,
4599 dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED
, FMA
,
4601 (failover_option_t
*)0));
4603 #if defined (DEBUG_FAILOVER_MESSAGES)
4604 if (status
!= ISC_R_SUCCESS
)
4605 failover_print (FMA
, " (failed)");
4606 failover_print (FMA
, ")");
4608 log_debug ("%s", obuf
);
4614 isc_result_t
dhcp_failover_send_update_request (dhcp_failover_state_t
*state
)
4616 dhcp_failover_link_t
*link
;
4617 isc_result_t status
;
4618 #if defined (DEBUG_FAILOVER_MESSAGES)
4620 unsigned obufix
= 0;
4622 # define FMA obuf, &obufix, sizeof obuf
4623 failover_print (FMA
, "(updreq");
4625 # define FMA (char *)0, (unsigned *)0, 0
4628 if (!state
-> link_to_peer
||
4629 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4630 return ISC_R_INVALIDARG
;
4631 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4633 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4634 return ISC_R_INVALIDARG
;
4636 if (state
-> curUPD
)
4637 return ISC_R_ALREADYRUNNING
;
4639 status
= (dhcp_failover_put_message
4640 (link
, link
-> outer
,
4641 FTM_UPDREQ
, link
->xid
++,
4642 (failover_option_t
*)0));
4644 if (status
== ISC_R_SUCCESS
)
4645 state
-> curUPD
= FTM_UPDREQ
;
4647 #if defined (DEBUG_FAILOVER_MESSAGES)
4648 if (status
!= ISC_R_SUCCESS
)
4649 failover_print (FMA
, " (failed)");
4650 failover_print (FMA
, ")");
4652 log_debug ("%s", obuf
);
4655 log_info ("Sent update request message to %s", state
-> name
);
4659 isc_result_t
dhcp_failover_send_update_request_all (dhcp_failover_state_t
4662 dhcp_failover_link_t
*link
;
4663 isc_result_t status
;
4664 #if defined (DEBUG_FAILOVER_MESSAGES)
4666 unsigned obufix
= 0;
4668 # define FMA obuf, &obufix, sizeof obuf
4669 failover_print (FMA
, "(updreqall");
4671 # define FMA (char *)0, (unsigned *)0, 0
4674 if (!state
-> link_to_peer
||
4675 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4676 return ISC_R_INVALIDARG
;
4677 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4679 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4680 return ISC_R_INVALIDARG
;
4682 /* If there is an UPDREQ in progress, then upgrade to UPDREQALL. */
4683 if (state
-> curUPD
&& (state
-> curUPD
!= FTM_UPDREQ
))
4684 return ISC_R_ALREADYRUNNING
;
4686 status
= (dhcp_failover_put_message
4687 (link
, link
-> outer
,
4688 FTM_UPDREQALL
, link
->xid
++,
4689 (failover_option_t
*)0));
4691 if (status
== ISC_R_SUCCESS
)
4692 state
-> curUPD
= FTM_UPDREQALL
;
4694 #if defined (DEBUG_FAILOVER_MESSAGES)
4695 if (status
!= ISC_R_SUCCESS
)
4696 failover_print (FMA
, " (failed)");
4697 failover_print (FMA
, ")");
4699 log_debug ("%s", obuf
);
4702 log_info ("Sent update request all message to %s", state
-> name
);
4706 isc_result_t
dhcp_failover_send_update_done (dhcp_failover_state_t
*state
)
4708 dhcp_failover_link_t
*link
;
4709 isc_result_t status
;
4710 #if defined (DEBUG_FAILOVER_MESSAGES)
4712 unsigned obufix
= 0;
4714 # define FMA obuf, &obufix, sizeof obuf
4715 failover_print (FMA
, "(upddone");
4717 # define FMA (char *)0, (unsigned *)0, 0
4720 if (!state
-> link_to_peer
||
4721 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4722 return ISC_R_INVALIDARG
;
4723 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4725 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4726 return ISC_R_INVALIDARG
;
4728 status
= (dhcp_failover_put_message
4729 (link
, link
-> outer
,
4730 FTM_UPDDONE
, state
->updxid
,
4731 (failover_option_t
*)0));
4733 #if defined (DEBUG_FAILOVER_MESSAGES)
4734 if (status
!= ISC_R_SUCCESS
)
4735 failover_print (FMA
, " (failed)");
4736 failover_print (FMA
, ")");
4738 log_debug ("%s", obuf
);
4742 log_info ("Sent update done message to %s", state
-> name
);
4744 state
->updxid
--; /* Paranoia, just so it mismatches. */
4746 /* There may be uncommitted leases at this point (since
4747 dhcp_failover_process_bind_ack() doesn't commit leases);
4748 commit the lease file. */
4754 isc_result_t
dhcp_failover_process_bind_update (dhcp_failover_state_t
*state
,
4755 failover_message_t
*msg
)
4757 struct lease
*lt
, *lease
;
4759 int reason
= FTR_MISC_REJECT
;
4760 const char *message
;
4761 int new_binding_state
;
4762 int send_to_backup
= 0;
4764 ia
.len
= sizeof msg
-> assigned_addr
;
4765 memcpy (ia
.iabuf
, &msg
-> assigned_addr
, ia
.len
);
4767 lease
= (struct lease
*)0;
4768 lt
= (struct lease
*)0;
4769 if (!find_lease_by_ip_addr (&lease
, ia
, MDL
)) {
4770 message
= "unknown IP address";
4771 reason
= FTR_ILLEGAL_IP_ADDR
;
4775 /* XXX check for conflicts. */
4777 /* Install the new info. */
4778 if (!lease_copy (<
, lease
, MDL
)) {
4779 message
= "no memory";
4783 if (msg
-> options_present
& FTB_CHADDR
) {
4784 if (msg
->binding_status
== FTS_ABANDONED
) {
4785 message
= "BNDUPD to ABANDONED with a CHADDR";
4788 if (msg
-> chaddr
.count
> sizeof lt
-> hardware_addr
.hbuf
) {
4789 message
= "chaddr too long";
4792 lt
-> hardware_addr
.hlen
= msg
-> chaddr
.count
;
4793 memcpy (lt
-> hardware_addr
.hbuf
, msg
-> chaddr
.data
,
4794 msg
-> chaddr
.count
);
4795 } else if (msg
->binding_status
== FTS_ACTIVE
||
4796 msg
->binding_status
== FTS_EXPIRED
||
4797 msg
->binding_status
== FTS_RELEASED
) {
4798 message
= "BNDUPD without CHADDR";
4800 } else if (msg
->binding_status
== FTS_ABANDONED
) {
4801 lt
->hardware_addr
.hlen
= 0;
4803 binding_scope_dereference(<
->scope
, MDL
);
4806 /* There is no explicit message content to indicate that the client
4807 * supplied no client-identifier. So if we don't hear of a value,
4808 * we discard the last one.
4810 if (msg
->options_present
& FTB_CLIENT_IDENTIFIER
) {
4811 if (msg
->binding_status
== FTS_ABANDONED
) {
4812 message
= "BNDUPD to ABANDONED with client-id";
4816 lt
->uid_len
= msg
->client_identifier
.count
;
4818 /* Allocate the lt->uid buffer if we haven't already, or
4819 * re-allocate the lt-uid buffer if we have one that is not
4820 * large enough. Otherwise, just use the extant buffer.
4822 if (!lt
->uid
|| lt
->uid
== lt
->uid_buf
||
4823 lt
->uid_len
> lt
->uid_max
) {
4824 if (lt
->uid
&& lt
->uid
!= lt
->uid_buf
)
4825 dfree(lt
->uid
, MDL
);
4827 if (lt
->uid_len
> sizeof(lt
->uid_buf
)) {
4828 lt
->uid_max
= lt
->uid_len
;
4829 lt
->uid
= dmalloc(lt
->uid_len
, MDL
);
4831 message
= "no memory";
4835 lt
->uid_max
= sizeof(lt
->uid_buf
);
4836 lt
->uid
= lt
->uid_buf
;
4840 msg
-> client_identifier
.data
, lt
-> uid_len
);
4841 } else if (lt
->uid
&& msg
->binding_status
!= FTS_RESET
&&
4842 msg
->binding_status
!= FTS_FREE
&&
4843 msg
->binding_status
!= FTS_BACKUP
) {
4844 if (lt
->uid
!= lt
->uid_buf
)
4845 dfree (lt
->uid
, MDL
);
4847 lt
->uid_max
= lt
->uid_len
= 0;
4850 /* If the lease was expired, also remove the stale binding scope. */
4851 if (lt
->scope
&& lt
->ends
< cur_time
)
4852 binding_scope_dereference(<
->scope
, MDL
);
4854 /* XXX Times may need to be adjusted based on clock skew! */
4855 if (msg
-> options_present
& FTB_STOS
) {
4856 lt
-> starts
= msg
-> stos
;
4858 if (msg
-> options_present
& FTB_LEASE_EXPIRY
) {
4859 lt
-> ends
= msg
-> expiry
;
4861 if (msg
-> options_present
& FTB_CLTT
) {
4862 lt
-> cltt
= msg
-> cltt
;
4864 if (msg
->options_present
& FTB_POTENTIAL_EXPIRY
) {
4865 lt
->atsfp
= lt
->tsfp
= msg
->potential_expiry
;
4867 if (msg
->options_present
& FTB_IP_FLAGS
) {
4868 if (msg
->ip_flags
& FTF_IP_FLAG_RESERVE
) {
4869 if ((((state
->i_am
== primary
) &&
4870 (lease
->binding_state
== FTS_FREE
)) ||
4871 ((state
->i_am
== secondary
) &&
4872 (lease
->binding_state
== FTS_BACKUP
))) &&
4873 !(lease
->flags
& RESERVED_LEASE
)) {
4874 message
= "Address is not reserved.";
4875 reason
= FTR_IP_NOT_RESERVED
;
4879 lt
->flags
|= RESERVED_LEASE
;
4881 lt
->flags
&= ~RESERVED_LEASE
;
4883 if (msg
->ip_flags
& FTF_IP_FLAG_BOOTP
) {
4884 if ((((state
->i_am
== primary
) &&
4885 (lease
->binding_state
== FTS_FREE
)) ||
4886 ((state
->i_am
== secondary
) &&
4887 (lease
->binding_state
== FTS_BACKUP
))) &&
4888 !(lease
->flags
& BOOTP_LEASE
)) {
4889 message
= "Address is not allocated to BOOTP.";
4892 lt
->flags
|= BOOTP_LEASE
;
4894 lt
->flags
&= ~BOOTP_LEASE
;
4896 if (msg
->ip_flags
& ~(FTF_IP_FLAG_RESERVE
| FTF_IP_FLAG_BOOTP
))
4897 log_info("Unknown IP-flags set in BNDUPD (0x%x).",
4899 } else /* Flags may only not appear if the values are zero. */
4900 lt
->flags
&= ~(RESERVED_LEASE
| BOOTP_LEASE
);
4902 if (msg
-> options_present
& FTB_BINDING_STATUS
) {
4903 #if defined (DEBUG_LEASE_STATE_TRANSITIONS)
4904 log_info ("processing state transition for %s: %s to %s",
4905 piaddr (lease
-> ip_addr
),
4906 binding_state_print (lease
-> binding_state
),
4907 binding_state_print (msg
-> binding_status
));
4910 /* If we're in normal state, make sure the state transition
4912 if (state
-> me
.state
== normal
) {
4914 (normal_binding_state_transition_check
4915 (lease
, state
, msg
-> binding_status
,
4916 msg
-> potential_expiry
));
4917 /* XXX if the transition the peer asked for isn't
4918 XXX allowed, maybe we should make the transition
4919 XXX into potential-conflict at this point. */
4922 (conflict_binding_state_transition_check
4923 (lease
, state
, msg
-> binding_status
,
4924 msg
-> potential_expiry
));
4926 if (new_binding_state
!= msg
-> binding_status
) {
4929 if (snprintf (outbuf
, sizeof outbuf
,
4930 "%s: invalid state transition: %s to %s",
4931 piaddr (lease
-> ip_addr
),
4932 binding_state_print (lease
-> binding_state
),
4933 binding_state_print (msg
-> binding_status
))
4935 log_fatal ("%s: impossible outbuf overflow",
4936 "dhcp_failover_process_bind_update");
4938 dhcp_failover_send_bind_ack (state
, msg
,
4943 if (new_binding_state
== FTS_EXPIRED
||
4944 new_binding_state
== FTS_RELEASED
||
4945 new_binding_state
== FTS_RESET
) {
4946 lt
-> next_binding_state
= FTS_FREE
;
4948 /* Mac address affinity. Assign the lease to
4949 * BACKUP state if we are the primary and the
4950 * peer is more likely to reallocate this lease
4951 * to a returning client.
4953 if ((state
->i_am
== primary
) &&
4954 !(lt
->flags
& (RESERVED_LEASE
| BOOTP_LEASE
)))
4955 send_to_backup
= peer_wants_lease(lt
);
4957 lt
-> next_binding_state
= new_binding_state
;
4959 msg
-> binding_status
= lt
-> next_binding_state
;
4962 /* Try to install the new information. */
4963 if (!supersede_lease (lease
, lt
, 0, 0, 0) ||
4964 !write_lease (lease
)) {
4965 message
= "database update failed";
4967 dhcp_failover_send_bind_ack (state
, msg
, reason
, message
);
4970 dhcp_failover_queue_ack (state
, msg
);
4973 /* If it is probably wise, assign lease to backup state if the peer
4974 * is not already hoarding leases.
4976 if (send_to_backup
&& secondary_not_hoarding(state
, lease
->pool
)) {
4977 lease
->next_binding_state
= FTS_BACKUP
;
4978 lease
->tstp
= cur_time
;
4979 lease
->starts
= cur_time
;
4981 if (!supersede_lease(lease
, NULL
, 0, 1, 0) ||
4982 !write_lease(lease
))
4983 log_error("can't commit lease %s for mac addr "
4984 "affinity", piaddr(lease
->ip_addr
));
4986 dhcp_failover_send_updates(state
);
4991 lease_dereference (<
, MDL
);
4993 lease_dereference (&lease
, MDL
);
4995 return ISC_R_SUCCESS
;
4998 /* This was hairy enough I didn't want to do it all in an if statement.
5000 * Returns: Truth is the secondary is allowed to get more leases based upon
5001 * MAC address affinity. False otherwise.
5004 secondary_not_hoarding(dhcp_failover_state_t
*state
, struct pool
*p
) {
5009 total
= p
->free_leases
+ p
->backup_leases
;
5011 /* How many leases is one side or the other allowed to "hold"? */
5012 hold
= ((total
* state
->max_lease_ownership
) + 50) / 100;
5014 /* If we were to send leases (or if the secondary were to send us
5015 * leases in the negative direction), how many would that be?
5017 lts
= (p
->free_leases
- p
->backup_leases
) / 2;
5019 /* The peer is not hoarding leases if we would send them more leases
5020 * (or they would take fewer leases) than the maximum they are allowed
5021 * to hold (the negative hold).
5023 return(lts
> -hold
);
5026 isc_result_t
dhcp_failover_process_bind_ack (dhcp_failover_state_t
*state
,
5027 failover_message_t
*msg
)
5029 struct lease
*lt
= (struct lease
*)0;
5030 struct lease
*lease
= (struct lease
*)0;
5032 const char *message
= "no memory";
5033 u_int32_t pot_expire
;
5034 int send_to_backup
= ISC_FALSE
;
5036 ia
.len
= sizeof msg
-> assigned_addr
;
5037 memcpy (ia
.iabuf
, &msg
-> assigned_addr
, ia
.len
);
5039 if (!find_lease_by_ip_addr (&lease
, ia
, MDL
)) {
5040 message
= "no such lease";
5044 /* XXX check for conflicts. */
5045 if (msg
-> options_present
& FTB_REJECT_REASON
) {
5046 log_error ("bind update on %s from %s rejected: %.*s",
5047 piaddr (ia
), state
-> name
,
5048 (int)((msg
-> options_present
& FTB_MESSAGE
)
5049 ? msg
-> message
.count
5050 : strlen (dhcp_failover_reject_reason_print
5051 (msg
-> reject_reason
))),
5052 (msg
-> options_present
& FTB_MESSAGE
)
5053 ? (const char *)(msg
-> message
.data
)
5054 : (dhcp_failover_reject_reason_print
5055 (msg
-> reject_reason
)));
5059 /* Silently discard acks for leases we did not update (or multiple
5062 if (!lease
->last_xid
)
5065 if (lease
->last_xid
!= msg
->xid
) {
5066 message
= "xid mismatch";
5070 /* XXX Times may need to be adjusted based on clock skew! */
5071 if (msg
->options_present
& FTO_POTENTIAL_EXPIRY
)
5072 pot_expire
= msg
->potential_expiry
;
5074 pot_expire
= lease
->tstp
;
5076 /* If the lease was desired to enter a binding state, we set
5077 * such a value upon transmitting a bndupd. We do not clear it
5078 * if we receive a bndupd in the meantime (or change the state
5079 * of the lease again ourselves), but we do set binding_state
5080 * if we get a bndupd.
5082 * So desired_binding_state tells us what we sent a bndupd for,
5083 * and binding_state tells us what we have since determined in
5086 if (lease
->desired_binding_state
== FTS_EXPIRED
||
5087 lease
->desired_binding_state
== FTS_RESET
||
5088 lease
->desired_binding_state
== FTS_RELEASED
)
5090 /* It is not a problem to do this directly as we call
5091 * supersede_lease immediately after: the lease is requeued
5092 * even if its sort order (tsfp) has changed.
5094 lease
->atsfp
= lease
->tsfp
= pot_expire
;
5095 if ((state
->i_am
== secondary
) &&
5096 (lease
->flags
& RESERVED_LEASE
))
5097 lease
->next_binding_state
= FTS_BACKUP
;
5099 lease
->next_binding_state
= FTS_FREE
;
5100 /* Clear this condition for the next go-round. */
5101 lease
->desired_binding_state
= lease
->next_binding_state
;
5102 supersede_lease(lease
, (struct lease
*)0, 0, 0, 0);
5105 /* Lease has returned to FREE state from the
5106 * transitional states. If the lease 'belongs'
5107 * to a client that would be served by the
5108 * peer, process a binding update now to send
5109 * the lease to backup state. But not if we
5110 * think we already have.
5112 if (state
->i_am
== primary
&&
5113 !(lease
->flags
& (RESERVED_LEASE
| BOOTP_LEASE
)) &&
5114 peer_wants_lease(lease
))
5115 send_to_backup
= ISC_TRUE
;
5117 if (!send_to_backup
&& state
->me
.state
== normal
)
5120 /* XXX It could be a problem to do this directly if the lease
5121 * XXX is sorted by tsfp.
5123 lease
->atsfp
= lease
->tsfp
= pot_expire
;
5124 if (lease
->desired_binding_state
!= lease
->binding_state
) {
5125 lease
->next_binding_state
=
5126 lease
->desired_binding_state
;
5127 supersede_lease(lease
,
5128 (struct lease
*)0, 0, 0, 0);
5131 /* Commit the lease only after a two-second timeout,
5132 so that if we get a bunch of acks in quick
5133 successtion (e.g., when stealing leases from the
5134 secondary), we do not do an immediate commit for
5136 add_timeout(cur_time
+ 2,
5137 commit_leases_timeout
, (void *)0, 0, 0);
5141 dhcp_failover_ack_queue_remove (state
, lease
);
5143 /* If we are supposed to send an update done after we send
5144 this lease, go ahead and send it. */
5145 if (state
-> send_update_done
== lease
) {
5146 lease_dereference (&state
-> send_update_done
, MDL
);
5147 dhcp_failover_send_update_done (state
);
5150 /* Now that the lease is off the ack queue, consider putting it
5151 * back on the update queue for mac address affinity.
5153 if (send_to_backup
&& secondary_not_hoarding(state
, lease
->pool
)) {
5154 lease
->next_binding_state
= FTS_BACKUP
;
5155 lease
->tstp
= lease
->starts
= cur_time
;
5157 if (!supersede_lease(lease
, NULL
, 0, 1, 0) ||
5158 !write_lease(lease
))
5159 log_error("can't commit lease %s for "
5160 "client affinity", piaddr(lease
->ip_addr
));
5162 if (state
->me
.state
== normal
)
5166 /* If there are updates pending, we've created space to send at
5168 dhcp_failover_send_updates (state
);
5171 lease_dereference (&lease
, MDL
);
5173 lease_dereference (<
, MDL
);
5175 return ISC_R_SUCCESS
;
5178 log_info ("bind update on %s got ack from %s: %s.",
5179 piaddr (ia
), state
-> name
, message
);
5183 isc_result_t
dhcp_failover_generate_update_queue (dhcp_failover_state_t
*state
,
5186 struct shared_network
*s
;
5190 #define FREE_LEASES 0
5191 #define ACTIVE_LEASES 1
5192 #define EXPIRED_LEASES 2
5193 #define ABANDONED_LEASES 3
5194 #define BACKUP_LEASES 4
5195 #define RESERVED_LEASES 5
5196 struct lease
**lptr
[RESERVED_LEASES
+1];
5198 /* Loop through each pool in each shared network and call the
5199 expiry routine on the pool. */
5200 for (s
= shared_networks
; s
; s
= s
-> next
) {
5201 for (p
= s
-> pools
; p
; p
= p
-> next
) {
5202 if (p
->failover_peer
!= state
)
5205 lptr
[FREE_LEASES
] = &p
->free
;
5206 lptr
[ACTIVE_LEASES
] = &p
->active
;
5207 lptr
[EXPIRED_LEASES
] = &p
->expired
;
5208 lptr
[ABANDONED_LEASES
] = &p
->abandoned
;
5209 lptr
[BACKUP_LEASES
] = &p
->backup
;
5210 lptr
[RESERVED_LEASES
] = &p
->reserved
;
5212 for (i
= FREE_LEASES
; i
<= RESERVED_LEASES
; i
++) {
5213 for (l
= *(lptr
[i
]); l
; l
= l
-> next
) {
5214 if ((l
->flags
& ON_QUEUE
) == 0 &&
5216 (l
->tstp
> l
->atsfp
) ||
5217 (i
== EXPIRED_LEASES
))) {
5218 l
-> desired_binding_state
= l
-> binding_state
;
5219 dhcp_failover_queue_update (l
, 0);
5225 return ISC_R_SUCCESS
;
5229 dhcp_failover_process_update_request (dhcp_failover_state_t
*state
,
5230 failover_message_t
*msg
)
5232 if (state
->send_update_done
) {
5233 log_info("Received update request while old update still "
5234 "flying! Silently discarding old request.");
5235 lease_dereference(&state
->send_update_done
, MDL
);
5238 /* Generate a fresh update queue. */
5239 dhcp_failover_generate_update_queue (state
, 0);
5241 state
->updxid
= msg
->xid
;
5243 /* If there's anything on the update queue (there shouldn't be
5244 anything on the ack queue), trigger an update done message
5245 when we get an ack for that lease. */
5246 if (state
-> update_queue_tail
) {
5247 lease_reference (&state
-> send_update_done
,
5248 state
-> update_queue_tail
, MDL
);
5249 dhcp_failover_send_updates (state
);
5250 log_info ("Update request from %s: sending update",
5253 /* Otherwise, there are no updates to send, so we can
5254 just send an UPDDONE message immediately. */
5255 dhcp_failover_send_update_done (state
);
5256 log_info ("Update request from %s: nothing pending",
5260 return ISC_R_SUCCESS
;
5264 dhcp_failover_process_update_request_all (dhcp_failover_state_t
*state
,
5265 failover_message_t
*msg
)
5267 if (state
->send_update_done
) {
5268 log_info("Received update request while old update still "
5269 "flying! Silently discarding old request.");
5270 lease_dereference(&state
->send_update_done
, MDL
);
5273 /* Generate a fresh update queue that includes every lease. */
5274 dhcp_failover_generate_update_queue (state
, 1);
5276 state
->updxid
= msg
->xid
;
5278 if (state
-> update_queue_tail
) {
5279 lease_reference (&state
-> send_update_done
,
5280 state
-> update_queue_tail
, MDL
);
5281 dhcp_failover_send_updates (state
);
5282 log_info ("Update request all from %s: sending update",
5285 /* This should really never happen, but it could happen
5286 on a server that currently has no leases configured. */
5287 dhcp_failover_send_update_done (state
);
5288 log_info ("Update request all from %s: nothing pending",
5292 return ISC_R_SUCCESS
;
5296 dhcp_failover_process_update_done (dhcp_failover_state_t
*state
,
5297 failover_message_t
*msg
)
5299 log_info ("failover peer %s: peer update completed.",
5302 state
-> curUPD
= 0;
5304 switch (state
-> me
.state
) {
5308 case communications_interrupted
:
5309 case resolution_interrupted
:
5315 break; /* shouldn't happen. */
5317 /* We got the UPDDONE, so we can go into normal state! */
5318 case potential_conflict
:
5319 if (state
->partner
.state
== conflict_done
) {
5320 if (state
->i_am
== secondary
) {
5321 dhcp_failover_set_state (state
, normal
);
5323 log_error("Secondary is in conflict_done "
5324 "state after conflict resolution, "
5325 "this is illegal.");
5326 dhcp_failover_set_state (state
, shut_down
);
5329 if (state
->i_am
== primary
)
5330 dhcp_failover_set_state (state
, conflict_done
);
5332 log_error("Spurious update-done message.");
5338 log_error("Spurious update-done message.");
5342 /* Wait for MCLT to expire before moving to recover_done,
5343 except that if both peers come up in recover, there is
5344 no point in waiting for MCLT to expire - this probably
5345 indicates the initial startup of a newly-configured
5347 if (state
-> me
.stos
+ state
-> mclt
> cur_time
&&
5348 state
-> partner
.state
!= recover
&&
5349 state
-> partner
.state
!= recover_done
) {
5350 dhcp_failover_set_state (state
, recover_wait
);
5351 #if defined (DEBUG_FAILOVER_TIMING)
5352 log_info ("add_timeout +%d %s",
5354 state
-> me
.stos
+ state
-> mclt
),
5355 "dhcp_failover_recover_done");
5357 add_timeout ((int)(state
-> me
.stos
+ state
-> mclt
),
5358 dhcp_failover_recover_done
,
5360 (tvref_t
)omapi_object_reference
,
5362 omapi_object_dereference
);
5364 dhcp_failover_recover_done (state
);
5367 return ISC_R_SUCCESS
;
5370 void dhcp_failover_recover_done (void *sp
)
5372 dhcp_failover_state_t
*state
= sp
;
5374 #if defined (DEBUG_FAILOVER_TIMING)
5375 log_info ("dhcp_failover_recover_done");
5378 dhcp_failover_set_state (state
, recover_done
);
5381 #if defined (DEBUG_FAILOVER_MESSAGES)
5382 /* Print hunks of failover messages, doing line breaks as appropriate.
5383 Note that this assumes syslog is being used, rather than, e.g., the
5384 Windows NT logging facility, where just dumping the whole message in
5385 one hunk would be more appropriate. */
5387 void failover_print (char *obuf
,
5388 unsigned *obufix
, unsigned obufmax
, const char *s
)
5390 int len
= strlen (s
);
5392 while (len
+ *obufix
+ 1 >= obufmax
) {
5393 log_debug ("%s", obuf
);
5395 log_debug ("%s", s
);
5401 strcpy (&obuf
[*obufix
], s
);
5404 #endif /* defined (DEBUG_FAILOVER_MESSAGES) */
5406 /* Taken from draft-ietf-dhc-loadb-01.txt: */
5407 /* A "mixing table" of 256 distinct values, in pseudo-random order. */
5408 unsigned char loadb_mx_tbl
[256] = {
5409 251, 175, 119, 215, 81, 14, 79, 191, 103, 49,
5410 181, 143, 186, 157, 0, 232, 31, 32, 55, 60,
5411 152, 58, 17, 237, 174, 70, 160, 144, 220, 90,
5412 57, 223, 59, 3, 18, 140, 111, 166, 203, 196,
5413 134, 243, 124, 95, 222, 179, 197, 65, 180, 48,
5414 36, 15, 107, 46, 233, 130, 165, 30, 123, 161,
5415 209, 23, 97, 16, 40, 91, 219, 61, 100, 10,
5416 210, 109, 250, 127, 22, 138, 29, 108, 244, 67,
5417 207, 9, 178, 204, 74, 98, 126, 249, 167, 116,
5418 34, 77, 193, 200, 121, 5, 20, 113, 71, 35,
5419 128, 13, 182, 94, 25, 226, 227, 199, 75, 27,
5420 41, 245, 230, 224, 43, 225, 177, 26, 155, 150,
5421 212, 142, 218, 115, 241, 73, 88, 105, 39, 114,
5422 62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
5423 154, 122, 12, 84, 82, 163, 44, 139, 228, 236,
5424 205, 242, 217, 11, 187, 146, 159, 64, 86, 239,
5425 195, 42, 106, 198, 118, 112, 184, 172, 87, 2,
5426 173, 117, 176, 229, 247, 253, 137, 185, 99, 164,
5427 102, 147, 45, 66, 231, 52, 141, 211, 194, 206,
5428 246, 238, 56, 110, 78, 248, 63, 240, 189, 93,
5429 92, 51, 53, 183, 19, 171, 72, 50, 33, 104,
5430 101, 69, 8, 252, 83, 120, 76, 135, 85, 54,
5431 202, 125, 188, 213, 96, 235, 136, 208, 162, 129,
5432 190, 132, 156, 38, 47, 1, 7, 254, 24, 4,
5433 216, 131, 89, 21, 28, 133, 37, 153, 149, 80,
5434 170, 68, 6, 169, 234, 151 };
5436 static unsigned char loadb_p_hash (const unsigned char *, unsigned);
5437 static unsigned char loadb_p_hash (const unsigned char *key
, unsigned len
)
5439 unsigned char hash
= len
;
5441 for(i
= len
; i
> 0; )
5442 hash
= loadb_mx_tbl
[hash
^ (key
[--i
])];
5446 int load_balance_mine (struct packet
*packet
, dhcp_failover_state_t
*state
)
5448 struct option_cache
*oc
;
5449 struct data_string ds
;
5450 unsigned char hbaix
;
5453 if (state
-> load_balance_max_secs
< ntohs (packet
-> raw
-> secs
)) {
5457 /* If we don't have a hash bucket array, we can't tell if this
5458 one's ours, so we assume it's not. */
5462 oc
= lookup_option (&dhcp_universe
, packet
-> options
,
5463 DHO_DHCP_CLIENT_IDENTIFIER
);
5464 memset (&ds
, 0, sizeof ds
);
5466 evaluate_option_cache (&ds
, packet
, (struct lease
*)0,
5467 (struct client_state
*)0,
5468 packet
-> options
, (struct option_state
*)0,
5469 &global_scope
, oc
, MDL
)) {
5470 hbaix
= loadb_p_hash (ds
.data
, ds
.len
);
5472 hbaix
= loadb_p_hash (packet
-> raw
-> chaddr
,
5473 packet
-> raw
-> hlen
);
5476 hm
= state
->hba
[(hbaix
>> 3) & 0x1F] & (1 << (hbaix
& 0x07));
5478 if (state
-> i_am
== primary
)
5484 /* The inverse of load_balance_mine ("load balance theirs"). We can't
5485 * use the regular load_balance_mine() and invert it because of the case
5486 * where there might not be an HBA, and we want to indicate false here
5487 * in this case only.
5490 peer_wants_lease(struct lease
*lp
)
5492 dhcp_failover_state_t
*state
;
5493 unsigned char hbaix
;
5499 state
= lp
->pool
->failover_peer
;
5501 if (!state
|| !state
->hba
)
5505 hbaix
= loadb_p_hash(lp
->uid
, lp
->uid_len
);
5506 else if (lp
->hardware_addr
.hlen
> 1)
5507 /* Skip the first byte, which is the hardware type, and is
5508 * not included during actual load balancing checks above
5509 * since it is separate from the packet header chaddr field.
5510 * The remainder of the hardware address should be identical
5511 * to the chaddr contents.
5513 hbaix
= loadb_p_hash(lp
->hardware_addr
.hbuf
+ 1,
5514 lp
->hardware_addr
.hlen
- 1);
5515 else /* Consistent 50/50 split */
5516 return(lp
->ip_addr
.iabuf
[lp
->ip_addr
.len
-1] & 0x01);
5518 hm
= state
->hba
[(hbaix
>> 3) & 0x1F] & (1 << (hbaix
& 0x07));
5520 if (state
->i_am
== primary
)
5526 /* This deals with what to do with bind updates when
5527 we're in the normal state
5529 Note that tsfp had better be set from the latest bind update
5530 _before_ this function is called! */
5533 normal_binding_state_transition_check (struct lease
*lease
,
5534 dhcp_failover_state_t
*state
,
5535 binding_state_t binding_state
,
5538 binding_state_t new_state
;
5540 /* If there is no transition, it's no problem. */
5541 if (binding_state
== lease
-> binding_state
)
5542 return binding_state
;
5544 switch (lease
-> binding_state
) {
5547 switch (binding_state
) {
5554 /* If the lease was free, and our peer is primary,
5555 then it can make it active, or abandoned, or
5556 backup. Abandoned is treated like free in
5558 if (state
-> i_am
== secondary
)
5559 return binding_state
;
5561 /* Otherwise, it can't legitimately do any sort of
5562 state transition. Because the lease was free,
5563 and the error has already been made, we allow the
5564 peer to change its state anyway, but log a warning
5565 message in hopes that the error will be fixed. */
5566 case FTS_FREE
: /* for compiler */
5567 new_state
= binding_state
;
5571 log_fatal ("Impossible case at %s:%d.", MDL
);
5575 /* The secondary can't change the state of an active
5577 if (state
-> i_am
== primary
) {
5578 /* Except that the client may send the DHCPRELEASE
5579 to the secondary, and we have to accept that. */
5580 if (binding_state
== FTS_RELEASED
)
5581 return binding_state
;
5582 new_state
= lease
-> binding_state
;
5586 /* So this is only for transitions made by the primary: */
5587 switch (binding_state
) {
5590 /* Can't set a lease to free or backup until the
5591 peer agrees that it's expired. */
5592 if (tsfp
> cur_time
) {
5593 new_state
= lease
-> binding_state
;
5596 return binding_state
;
5599 /* XXX 65 should be the clock skew between the peers
5600 XXX plus a fudge factor. This code will result
5601 XXX in problems if MCLT is really short or the
5602 XXX max-lease-time is really short (less than the
5603 XXX fudge factor. */
5604 if (lease
-> ends
- 65 > cur_time
) {
5605 new_state
= lease
-> binding_state
;
5613 return binding_state
;
5616 log_fatal ("Impossible case at %s:%d.", MDL
);
5621 switch (binding_state
) {
5624 /* Can't set a lease to free or backup until the
5625 peer agrees that it's expired. */
5626 if (tsfp
> cur_time
) {
5627 new_state
= lease
-> binding_state
;
5630 return binding_state
;
5637 return binding_state
;
5640 log_fatal ("Impossible case at %s:%d.", MDL
);
5644 switch (binding_state
) {
5648 /* These are invalid state transitions - should we
5655 return binding_state
;
5658 log_fatal ("Impossible case at %s:%d.", MDL
);
5662 switch (binding_state
) {
5665 /* Can't set a lease to free or backup until the
5666 peer agrees that it's expired. */
5667 if (tsfp
> cur_time
) {
5668 new_state
= lease
-> binding_state
;
5671 return binding_state
;
5678 return binding_state
;
5681 log_fatal ("Impossible case at %s:%d.", MDL
);
5685 switch (binding_state
) {
5691 /* If the lease was in backup, and our peer
5692 is secondary, then it can make it active
5694 if (state
-> i_am
== primary
)
5695 return binding_state
;
5697 /* Either the primary or the secondary can
5698 reasonably move a lease from the backup
5699 state to the free state. */
5701 return binding_state
;
5704 new_state
= lease
-> binding_state
;
5708 log_fatal ("Impossible case at %s:%d.", MDL
);
5713 log_fatal ("Impossible case at %s:%d.", MDL
);
5720 /* Determine whether the state transition is okay when we're potentially
5721 in conflict with the peer. */
5723 conflict_binding_state_transition_check (struct lease
*lease
,
5724 dhcp_failover_state_t
*state
,
5725 binding_state_t binding_state
,
5728 binding_state_t new_state
;
5730 /* If there is no transition, it's no problem. */
5731 if (binding_state
== lease
-> binding_state
)
5732 new_state
= binding_state
;
5734 switch (lease
-> binding_state
) {
5735 /* If we think the lease is not in use, then the
5736 state into which the partner put it is just fine,
5744 new_state
= binding_state
;
5747 /* If we think the lease *is* in use, then we're not
5748 going to take the partner's change if the partner
5749 thinks it's free. */
5751 switch (binding_state
) {
5754 new_state
= lease
-> binding_state
;
5758 /* If we don't agree about expiry, it's
5759 * invalid. 65 should allow for max
5760 * clock skew (60) plus some fudge.
5761 * XXX: should we refetch cur_time?
5763 if ((lease
->ends
- 65) > cur_time
)
5764 new_state
= lease
->binding_state
;
5766 new_state
= binding_state
;
5769 /* RELEASED, RESET, and ABANDONED indicate
5770 * that our partner has information about
5771 * this lease that we did not witness. Our
5777 new_state
= binding_state
;
5781 log_fatal ("Impossible case at %s:%d.", MDL
);
5787 log_fatal ("Impossible case at %s:%d.", MDL
);
5794 /* We can reallocate a lease under the following circumstances:
5796 (1) It belongs to us - it's FTS_FREE, and we're primary, or it's
5797 FTS_BACKUP, and we're secondary.
5798 (2) We're in partner_down, and the lease is not active, and we
5799 can be sure that the other server didn't make it active.
5800 We can only be sure that the server didn't make it active
5801 when we are in the partner_down state and one of the following
5802 two conditions holds:
5803 (a) in the case that the time sent from the peer is earlier than
5804 the time we entered the partner_down state, at least MCLT has
5805 gone by since we entered partner_down, or
5806 (b) in the case that the time sent from the peer is later than
5807 the time when we entered partner_down, the current time is
5808 later than the time sent from the peer by at least MCLT. */
5810 int lease_mine_to_reallocate (struct lease
*lease
)
5812 dhcp_failover_state_t
*peer
;
5814 if (lease
&& lease
->pool
&&
5815 (peer
= lease
->pool
->failover_peer
)) {
5816 switch (lease
->binding_state
) {
5818 /* ACTIVE leases may not be reallocated. */
5823 /* FREE leases may only be allocated by the primary,
5824 * unless the secondary is acting in partner_down
5825 * state and stos+mclt or tsfp+mclt has expired,
5826 * whichever is greater.
5828 * ABANDONED are treated the same as FREE for all
5829 * purposes here. Note that servers will only try
5830 * for ABANDONED leases as a last resort anyway.
5832 if (peer
-> i_am
== primary
)
5835 return(peer
->service_state
== service_partner_down
&&
5836 ((lease
->tsfp
< peer
->me
.stos
) ?
5837 (peer
->me
.stos
+ peer
->mclt
< cur_time
) :
5838 (lease
->tsfp
+ peer
->mclt
< cur_time
)));
5843 /* These three lease states go onto the 'expired'
5844 * queue. Upon entry into partner-down state, this
5845 * queue of leases has their tsfp values modified
5846 * to equal stos+mclt, the point at which the server
5847 * is allowed to remove them from these transitional
5848 * states without an acknowledgement.
5850 * Note that although tsfp has been possibly extended
5851 * past the actual tsfp we received from the peer, we
5852 * don't have to take any special action. Since tsfp
5853 * is now in the past (or now), we can guarantee that
5854 * this server will only allocate a lease time equal
5855 * to MCLT, rather than a TSFP-optimal lease, which is
5856 * the only danger for a lease in one of these states.
5858 return((peer
->service_state
== service_partner_down
) &&
5859 (lease
->tsfp
< cur_time
));
5862 /* Only the secondary may allocate BACKUP leases,
5863 * unless in partner_down state in which case at
5864 * least TSFP+MCLT or STOS+MCLT must have expired,
5865 * whichever is greater.
5867 if (peer
->i_am
== secondary
)
5870 return((peer
->service_state
== service_partner_down
) &&
5871 ((lease
->tsfp
< peer
->me
.stos
) ?
5872 (peer
->me
.stos
+ peer
->mclt
< cur_time
) :
5873 (lease
->tsfp
+ peer
->mclt
< cur_time
)));
5876 /* All lease states appear above. */
5877 log_fatal("Impossible case at %s:%d.", MDL
);
5883 return(lease
->binding_state
== FTS_FREE
||
5884 lease
->binding_state
== FTS_BACKUP
);
5889 static isc_result_t
failover_message_reference (failover_message_t
**mp
,
5890 failover_message_t
*m
,
5891 const char *file
, int line
)
5895 return ISC_R_SUCCESS
;
5898 static isc_result_t
failover_message_dereference (failover_message_t
**mp
,
5899 const char *file
, int line
)
5901 failover_message_t
*m
;
5904 if (m
-> refcnt
== 0) {
5906 failover_message_dereference (&m
-> next
,
5908 if (m
-> chaddr
.data
)
5909 dfree (m
-> chaddr
.data
, file
, line
);
5910 if (m
-> client_identifier
.data
)
5911 dfree (m
-> client_identifier
.data
, file
, line
);
5913 dfree (m
-> hba
.data
, file
, line
);
5914 if (m
-> message
.data
)
5915 dfree (m
-> message
.data
, file
, line
);
5916 if (m
-> reply_options
.data
)
5917 dfree (m
-> reply_options
.data
, file
, line
);
5918 if (m
-> request_options
.data
)
5919 dfree (m
-> request_options
.data
, file
, line
);
5920 if (m
-> vendor_class
.data
)
5921 dfree (m
-> vendor_class
.data
, file
, line
);
5922 if (m
-> vendor_options
.data
)
5923 dfree (m
-> vendor_options
.data
, file
, line
);
5925 dfree (m
-> ddns
.data
, file
, line
);
5926 dfree (*mp
, file
, line
);
5929 return ISC_R_SUCCESS
;
5932 OMAPI_OBJECT_ALLOC (dhcp_failover_state
, dhcp_failover_state_t
,
5933 dhcp_type_failover_state
)
5934 OMAPI_OBJECT_ALLOC (dhcp_failover_listener
, dhcp_failover_listener_t
,
5935 dhcp_type_failover_listener
)
5936 OMAPI_OBJECT_ALLOC (dhcp_failover_link
, dhcp_failover_link_t
,
5937 dhcp_type_failover_link
)
5938 #endif /* defined (FAILOVER_PROTOCOL) */
5940 const char *binding_state_print (enum failover_state state
)