3 Failover protocol support code... */
6 * Copyright (c) 2004-2006 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1999-2003 by Internet Software Consortium
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 * Internet Systems Consortium, Inc.
23 * Redwood City, CA 94063
27 * This software has been written for Internet Systems Consortium
28 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29 * To learn more about Internet Systems Consortium, see
30 * ``http://www.isc.org/''. To learn more about Vixie Enterprises,
31 * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
32 * ``http://www.nominum.com''.
36 static char copyright
[] =
37 "$Id: failover.c,v 1.59 2006/02/24 23:16:32 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
42 #include <omapip/omapip_p.h>
44 #if defined (FAILOVER_PROTOCOL)
45 dhcp_failover_state_t
*failover_states
;
46 static isc_result_t
do_a_failover_option (omapi_object_t
*,
47 dhcp_failover_link_t
*);
48 dhcp_failover_listener_t
*failover_listeners
;
50 static isc_result_t
failover_message_reference (failover_message_t
**,
52 const char *file
, int line
);
53 static isc_result_t
failover_message_dereference (failover_message_t
**,
54 const char *file
, int line
);
56 void dhcp_failover_startup ()
58 dhcp_failover_state_t
*state
;
60 dhcp_failover_listener_t
*l
;
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 seperate
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 omapi_value_t
*value
= (omapi_value_t
*)0;
178 dhcp_failover_state_t
*state
;
181 struct data_string ds
;
182 omapi_addr_list_t
*addrs
= (omapi_addr_list_t
*)0;
183 omapi_addr_t local_addr
;
185 /* Find the failover state in the object chain. */
186 for (o
= h
; o
-> outer
; o
= o
-> outer
)
188 for (; o
; o
= o
-> inner
) {
189 if (o
-> type
== dhcp_type_failover_state
)
193 return ISC_R_INVALIDARG
;
194 state
= (dhcp_failover_state_t
*)o
;
196 obj
= (dhcp_failover_link_t
*)0;
197 status
= dhcp_failover_link_allocate (&obj
, MDL
);
198 if (status
!= ISC_R_SUCCESS
)
200 option_cache_reference (&obj
-> peer_address
,
201 state
-> partner
.address
, MDL
);
202 obj
-> peer_port
= state
-> partner
.port
;
203 dhcp_failover_state_reference (&obj
-> state_object
, state
, MDL
);
205 memset (&ds
, 0, sizeof ds
);
206 if (!evaluate_option_cache (&ds
, (struct packet
*)0, (struct lease
*)0,
207 (struct client_state
*)0,
208 (struct option_state
*)0,
209 (struct option_state
*)0,
210 &global_scope
, obj
-> peer_address
, MDL
)) {
211 dhcp_failover_link_dereference (&obj
, MDL
);
212 return ISC_R_UNEXPECTED
;
215 /* Make an omapi address list out of a buffer containing zero or more
217 status
= omapi_addr_list_new (&addrs
, ds
.len
/ 4, MDL
);
218 if (status
!= ISC_R_SUCCESS
) {
219 dhcp_failover_link_dereference (&obj
, MDL
);
223 for (i
= 0; i
< addrs
-> count
; i
++) {
224 addrs
-> addresses
[i
].addrtype
= AF_INET
;
225 addrs
-> addresses
[i
].addrlen
= sizeof (struct in_addr
);
226 memcpy (addrs
-> addresses
[i
].address
,
227 &ds
.data
[i
* 4], sizeof (struct in_addr
));
228 addrs
-> addresses
[i
].port
= obj
-> peer_port
;
230 data_string_forget (&ds
, MDL
);
232 /* Now figure out the local address that we're supposed to use. */
233 if (!state
-> me
.address
||
234 !evaluate_option_cache (&ds
, (struct packet
*)0,
236 (struct client_state
*)0,
237 (struct option_state
*)0,
238 (struct option_state
*)0,
239 &global_scope
, state
-> me
.address
,
241 memset (&local_addr
, 0, sizeof local_addr
);
242 local_addr
.addrtype
= AF_INET
;
243 local_addr
.addrlen
= sizeof (struct in_addr
);
244 if (!state
-> server_identifier
.len
) {
245 log_fatal ("failover peer %s: no local address.",
249 if (ds
.len
!= sizeof (struct in_addr
)) {
250 data_string_forget (&ds
, MDL
);
251 dhcp_failover_link_dereference (&obj
, MDL
);
252 omapi_addr_list_dereference (&addrs
, MDL
);
253 return ISC_R_INVALIDARG
;
255 local_addr
.addrtype
= AF_INET
;
256 local_addr
.addrlen
= ds
.len
;
257 memcpy (local_addr
.address
, ds
.data
, ds
.len
);
258 if (!state
-> server_identifier
.len
)
259 data_string_copy (&state
-> server_identifier
,
261 data_string_forget (&ds
, MDL
);
262 local_addr
.port
= 0; /* Let the O.S. choose. */
265 status
= omapi_connect_list ((omapi_object_t
*)obj
,
267 omapi_addr_list_dereference (&addrs
, MDL
);
269 dhcp_failover_link_dereference (&obj
, MDL
);
273 isc_result_t
dhcp_failover_link_signal (omapi_object_t
*h
,
274 const char *name
, va_list ap
)
277 dhcp_failover_link_t
*link
;
281 dhcp_failover_state_t
*s
, *state
= (dhcp_failover_state_t
*)0;
283 if (h
-> type
!= dhcp_type_failover_link
) {
284 /* XXX shouldn't happen. Put an assert here? */
285 return ISC_R_UNEXPECTED
;
287 link
= (dhcp_failover_link_t
*)h
;
289 if (!strcmp (name
, "connect")) {
290 if (link
-> state_object
-> i_am
== primary
) {
291 status
= dhcp_failover_send_connect (h
);
292 if (status
!= ISC_R_SUCCESS
) {
293 log_info ("dhcp_failover_send_connect: %s",
294 isc_result_totext (status
));
295 omapi_disconnect (h
-> outer
, 1);
298 status
= ISC_R_SUCCESS
;
299 /* Allow the peer fifteen seconds to send us a
301 #if defined (DEBUG_FAILOVER_TIMING)
302 log_info ("add_timeout +15 %s",
303 "dhcp_failover_link_startup_timeout");
305 add_timeout (cur_time
+ 15,
306 dhcp_failover_link_startup_timeout
,
308 (tvref_t
)dhcp_failover_link_reference
,
309 (tvunref_t
)dhcp_failover_link_dereference
);
313 if (!strcmp (name
, "disconnect")) {
314 if (link
-> state_object
) {
315 dhcp_failover_state_reference (&state
,
316 link
-> state_object
, MDL
);
317 link
-> state
= dhcp_flink_disconnected
;
319 /* Make the transition. */
320 if (state
-> link_to_peer
== link
) {
321 dhcp_failover_state_transition (link
-> state_object
,
324 /* Start trying to reconnect. */
325 #if defined (DEBUG_FAILOVER_TIMING)
326 log_info ("add_timeout +5 %s",
327 "dhcp_failover_reconnect");
329 add_timeout (cur_time
+ 5, dhcp_failover_reconnect
,
331 (tvref_t
)dhcp_failover_state_reference
,
332 (tvunref_t
)dhcp_failover_state_dereference
);
334 dhcp_failover_state_dereference (&state
, MDL
);
336 return ISC_R_SUCCESS
;
339 if (!strcmp (name
, "status")) {
340 if (link
-> state_object
) {
343 status
= va_arg(ap
, isc_result_t
);
345 if ((status
== ISC_R_HOSTUNREACH
) || (status
== ISC_R_TIMEDOUT
)) {
346 dhcp_failover_state_reference (&state
,
347 link
-> state_object
, MDL
);
348 link
-> state
= dhcp_flink_disconnected
;
350 /* Make the transition. */
351 dhcp_failover_state_transition (link
-> state_object
,
354 /* Start trying to reconnect. */
355 #if defined (DEBUG_FAILOVER_TIMING)
356 log_info ("add_timeout +5 %s",
357 "dhcp_failover_reconnect");
359 add_timeout (cur_time
+ 5, dhcp_failover_reconnect
,
361 (tvref_t
)dhcp_failover_state_reference
,
362 (tvunref_t
)dhcp_failover_state_dereference
);
364 dhcp_failover_state_dereference (&state
, MDL
);
366 return ISC_R_SUCCESS
;
369 /* Not a signal we recognize? */
370 if (strcmp (name
, "ready")) {
371 if (h
-> inner
&& h
-> inner
-> type
-> signal_handler
)
372 return (*(h
-> inner
-> type
-> signal_handler
))
373 (h
-> inner
, name
, ap
);
374 return ISC_R_NOTFOUND
;
377 if (!h
-> outer
|| h
-> outer
-> type
!= omapi_type_connection
)
378 return ISC_R_INVALIDARG
;
381 /* We get here because we requested that we be woken up after
382 some number of bytes were read, and that number of bytes
383 has in fact been read. */
384 switch (link
-> state
) {
385 case dhcp_flink_start
:
386 link
-> state
= dhcp_flink_message_length_wait
;
387 if ((omapi_connection_require (c
, 2)) != ISC_R_SUCCESS
)
389 case dhcp_flink_message_length_wait
:
391 link
-> state
= dhcp_flink_message_wait
;
392 link
-> imsg
= dmalloc (sizeof (failover_message_t
), MDL
);
394 status
= ISC_R_NOMEMORY
;
397 failover_message_dereference (&link
->imsg
,
400 link
-> state
= dhcp_flink_disconnected
;
401 log_info ("message length wait: %s",
402 isc_result_totext (status
));
403 omapi_disconnect (c
, 1);
404 /* XXX just blow away the protocol state now?
405 XXX or will disconnect blow it away? */
406 return ISC_R_UNEXPECTED
;
408 memset (link
-> imsg
, 0, sizeof (failover_message_t
));
409 link
-> imsg
-> refcnt
= 1;
410 /* Get the length: */
411 omapi_connection_get_uint16 (c
, &link
-> imsg_len
);
412 link
-> imsg_count
= 0; /* Bytes read. */
414 /* Maximum of 2048 bytes in any failover message. */
415 if (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
) {
480 /* See if we can find a failover_state object that
481 matches this connection. This message should only
482 be received by a secondary from a primary. */
483 for (s
= failover_states
; s
; s
= s
-> next
) {
484 if (dhcp_failover_state_match
485 (s
, (u_int8_t
*)&link
-> imsg
-> server_addr
,
486 sizeof link
-> imsg
-> server_addr
))
490 /* If we can't find a failover protocol state
491 for this remote host, drop the connection */
493 errmsg
= "unknown server";
494 reason
= FTR_INVALID_PARTNER
;
497 /* XXX Send a refusal message first?
498 XXX Look in protocol spec for guidance. */
499 log_error ("Failover CONNECT from %u.%u.%u.%u: %s",
501 (&link
-> imsg
-> server_addr
)) [0],
503 (&link
-> imsg
-> server_addr
)) [1],
505 (&link
-> imsg
-> server_addr
)) [2],
507 (&link
-> imsg
-> server_addr
)) [3],
509 dhcp_failover_send_connectack
510 ((omapi_object_t
*)link
, state
,
512 log_info ("failover: disconnect: %s", errmsg
);
513 omapi_disconnect (c
, 0);
514 link
-> state
= dhcp_flink_disconnected
;
515 return ISC_R_SUCCESS
;
518 if ((cur_time
> link
-> imsg
-> time
&&
519 cur_time
- link
-> imsg
-> time
> 60) ||
520 (cur_time
< link
-> imsg
-> time
&&
521 link
-> imsg
-> time
- cur_time
> 60)) {
522 errmsg
= "time offset too large";
523 reason
= FTR_TIMEMISMATCH
;
527 if (!(link
-> imsg
-> options_present
& FTB_HBA
) ||
528 link
-> imsg
-> hba
.count
!= 32) {
529 errmsg
= "invalid HBA";
530 reason
= FTR_HBA_CONFLICT
; /* XXX */
534 dfree (state
-> hba
, MDL
);
535 state
-> hba
= dmalloc (32, MDL
);
537 errmsg
= "no memory";
538 reason
= FTR_MISC_REJECT
;
541 memcpy (state
-> hba
, link
-> imsg
-> hba
.data
, 32);
543 if (!link
-> state_object
)
544 dhcp_failover_state_reference
545 (&link
-> state_object
, state
, MDL
);
546 if (!link
-> peer_address
)
547 option_cache_reference
548 (&link
-> peer_address
,
549 state
-> partner
.address
, MDL
);
552 /* If we don't have a state object at this point, it's
553 some kind of bogus situation, so just drop the
555 if (!link
-> state_object
) {
556 log_info ("failover: connect: no matching state.");
557 omapi_disconnect (c
, 1);
558 link
-> state
= dhcp_flink_disconnected
;
559 return ISC_R_INVALIDARG
;
562 /* Once we have the entire message, and we've validated
563 it as best we can here, pass it to the parent. */
564 omapi_signal ((omapi_object_t
*)link
-> state_object
,
566 link
-> state
= dhcp_flink_message_length_wait
;
567 failover_message_dereference (&link
-> imsg
, MDL
);
568 /* XXX This is dangerous because we could get into a tight
569 XXX loop reading input without servicing any other stuff.
570 XXX There needs to be a way to relinquish control but
571 XXX get it back immediately if there's no other work to
573 if ((omapi_connection_require (c
, 2)) == ISC_R_SUCCESS
)
578 /* XXX should never get here. Assertion? */
581 return ISC_R_SUCCESS
;
584 static isc_result_t
do_a_failover_option (c
, link
)
586 dhcp_failover_link_t
*link
;
588 u_int16_t option_code
;
589 u_int16_t option_len
;
596 if (link
-> imsg_count
+ 2 > link
-> imsg_len
) {
597 log_error ("FAILOVER: message overflow at option code.");
598 return ISC_R_PROTOCOLERROR
;
601 /* Get option code. */
602 omapi_connection_get_uint16 (c
, &option_code
);
603 link
-> imsg_count
+= 2;
605 if (link
-> imsg_count
+ 2 > link
-> imsg_len
) {
606 log_error ("FAILOVER: message overflow at length.");
607 return ISC_R_PROTOCOLERROR
;
610 /* Get option length. */
611 omapi_connection_get_uint16 (c
, &option_len
);
612 link
-> imsg_count
+= 2;
614 if (link
-> imsg_count
+ option_len
> link
-> imsg_len
) {
615 log_error ("FAILOVER: message overflow at data.");
616 return ISC_R_PROTOCOLERROR
;
619 /* If it's an unknown code, skip over it. */
620 if (option_code
> FTO_MAX
) {
621 #if defined (DEBUG_FAILOVER_MESSAGES)
622 log_debug (" option code %d (%s) len %d (not recognized)",
624 dhcp_failover_option_name (option_code
),
627 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
628 link
-> imsg_count
+= option_len
;
629 return ISC_R_SUCCESS
;
632 /* If it's the digest, do it now. */
633 if (ft_options
[option_code
].type
== FT_DIGEST
) {
634 link
-> imsg_count
+= option_len
;
635 if (link
-> imsg_count
!= link
-> imsg_len
) {
636 log_error ("FAILOVER: digest not at end of message");
637 return ISC_R_PROTOCOLERROR
;
639 #if defined (DEBUG_FAILOVER_MESSAGES)
640 log_debug (" option %s len %d",
641 ft_options
[option_code
].name
, option_len
);
643 /* For now, just dump it. */
644 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
645 return ISC_R_SUCCESS
;
648 /* Only accept an option once. */
649 if (link
-> imsg
-> options_present
& ft_options
[option_code
].bit
) {
650 log_error ("FAILOVER: duplicate option %s",
651 ft_options
[option_code
].name
);
652 return ISC_R_PROTOCOLERROR
;
655 /* Make sure the option is appropriate for this type of message.
656 Really, any option is generally allowed for any message, and the
657 cases where this is not true are too complicated to represent in
658 this way - what this code is doing is to just avoid saving the
659 value of an option we don't have any way to use, which allows
660 us to make the failover_message structure smaller. */
661 if (ft_options
[option_code
].bit
&&
662 !(fto_allowed
[link
-> imsg
-> type
] &
663 ft_options
[option_code
].bit
)) {
664 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
665 link
-> imsg_count
+= option_len
;
666 return ISC_R_SUCCESS
;
669 /* Figure out how many elements, how big they are, and where
671 if (ft_options
[option_code
].num_present
) {
672 /* If this option takes a fixed number of elements,
673 we expect the space for them to be preallocated,
674 and we can just read the data in. */
676 op
= ((unsigned char *)link
-> imsg
) +
677 ft_options
[option_code
].offset
;
678 op_size
= ft_sizes
[ft_options
[option_code
].type
];
679 op_count
= ft_options
[option_code
].num_present
;
681 if (option_len
!= op_size
* op_count
) {
682 log_error ("FAILOVER: option size (%d:%d), option %s",
684 (ft_sizes
[ft_options
[option_code
].type
] *
685 ft_options
[option_code
].num_present
),
686 ft_options
[option_code
].name
);
687 return ISC_R_PROTOCOLERROR
;
690 failover_option_t
*fo
;
692 /* FT_DDNS* are special - one or two bytes of status
693 followed by the client FQDN. */
694 if (ft_options
[option_code
].type
== FT_DDNS1
||
695 ft_options
[option_code
].type
== FT_DDNS1
) {
698 (((char *)link
-> imsg
) +
699 ft_options
[option_code
].offset
));
701 op_count
= (ft_options
[option_code
].type
== FT_DDNS1
704 omapi_connection_copyout (&ddns
-> codes
[0],
706 link
-> imsg_count
+= op_count
;
708 ddns
-> codes
[1] = 0;
710 op_count
= option_len
- op_count
;
712 ddns
-> length
= op_count
;
713 ddns
-> data
= dmalloc (op_count
, MDL
);
715 log_error ("FAILOVER: no memory getting%s(%d)",
716 " DNS data ", op_count
);
718 /* Actually, NO_MEMORY, but if we lose here
719 we have to drop the connection. */
720 return ISC_R_PROTOCOLERROR
;
722 omapi_connection_copyout (ddns
-> data
, c
, op_count
);
726 /* A zero for num_present means that any number of
727 elements can appear, so we have to figure out how
728 many we got from the length of the option, and then
729 fill out a failover_option structure describing the
731 op_size
= ft_sizes
[ft_options
[option_code
].type
];
733 /* Make sure that option data length is a multiple of the
734 size of the data type being sent. */
735 if (op_size
> 1 && option_len
% op_size
) {
736 log_error ("FAILOVER: option_len %d not %s%d",
737 option_len
, "multiple of ", op_size
);
738 return ISC_R_PROTOCOLERROR
;
741 op_count
= option_len
/ op_size
;
743 fo
= ((failover_option_t
*)
744 (((char *)link
-> imsg
) +
745 ft_options
[option_code
].offset
));
747 fo
-> count
= op_count
;
748 fo
-> data
= dmalloc (option_len
, MDL
);
750 log_error ("FAILOVER: no memory getting %s (%d)",
751 "option data", op_count
);
753 return ISC_R_PROTOCOLERROR
;
758 /* For single-byte message values and multi-byte values that
759 don't need swapping, just read them in all at once. */
760 if (op_size
== 1 || ft_options
[option_code
].type
== FT_IPADDR
) {
761 omapi_connection_copyout ((unsigned char *)op
, c
, option_len
);
762 link
-> imsg_count
+= option_len
;
766 /* For values that require swapping, read them in one at a time
767 using routines that swap bytes. */
768 for (i
= 0; i
< op_count
; i
++) {
769 switch (ft_options
[option_code
].type
) {
771 omapi_connection_get_uint32 (c
, (u_int32_t
*)op
);
773 link
-> imsg_count
+= 4;
777 omapi_connection_get_uint16 (c
, (u_int16_t
*)op
);
779 link
-> imsg_count
+= 2;
783 /* Everything else should have been handled
785 log_error ("FAILOVER: option %s: bad type %d",
786 ft_options
[option_code
].name
,
787 ft_options
[option_code
].type
);
788 return ISC_R_PROTOCOLERROR
;
792 /* Remember that we got this option. */
793 link
-> imsg
-> options_present
|= ft_options
[option_code
].bit
;
794 return ISC_R_SUCCESS
;
797 isc_result_t
dhcp_failover_link_set_value (omapi_object_t
*h
,
799 omapi_data_string_t
*name
,
800 omapi_typed_data_t
*value
)
802 if (h
-> type
!= omapi_type_protocol
)
803 return ISC_R_INVALIDARG
;
805 /* Never valid to set these. */
806 if (!omapi_ds_strcmp (name
, "link-port") ||
807 !omapi_ds_strcmp (name
, "link-name") ||
808 !omapi_ds_strcmp (name
, "link-state"))
811 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
812 return (*(h
-> inner
-> type
-> set_value
))
813 (h
-> inner
, id
, name
, value
);
814 return ISC_R_NOTFOUND
;
817 isc_result_t
dhcp_failover_link_get_value (omapi_object_t
*h
,
819 omapi_data_string_t
*name
,
820 omapi_value_t
**value
)
822 dhcp_failover_link_t
*link
;
824 if (h
-> type
!= omapi_type_protocol
)
825 return ISC_R_INVALIDARG
;
826 link
= (dhcp_failover_link_t
*)h
;
828 if (!omapi_ds_strcmp (name
, "link-port")) {
829 return omapi_make_int_value (value
, name
,
830 (int)link
-> peer_port
, MDL
);
831 } else if (!omapi_ds_strcmp (name
, "link-state")) {
832 if (link
-> state
< 0 ||
833 link
-> state
>= dhcp_flink_state_max
)
834 return omapi_make_string_value (value
, name
,
835 "invalid link state",
837 return omapi_make_string_value
839 dhcp_flink_state_names
[link
-> state
], MDL
);
842 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
843 return (*(h
-> inner
-> type
-> get_value
))
844 (h
-> inner
, id
, name
, value
);
845 return ISC_R_NOTFOUND
;
848 isc_result_t
dhcp_failover_link_destroy (omapi_object_t
*h
,
849 const char *file
, int line
)
851 dhcp_failover_link_t
*link
;
852 if (h
-> type
!= dhcp_type_failover_link
)
853 return ISC_R_INVALIDARG
;
854 link
= (dhcp_failover_link_t
*)h
;
856 if (link
-> peer_address
)
857 option_cache_dereference (&link
-> peer_address
, file
, line
);
859 failover_message_dereference (&link
-> imsg
, file
, line
);
860 if (link
-> state_object
)
861 dhcp_failover_state_dereference (&link
-> state_object
,
863 return ISC_R_SUCCESS
;
866 /* Write all the published values associated with the object through the
867 specified connection. */
869 isc_result_t
dhcp_failover_link_stuff_values (omapi_object_t
*c
,
873 dhcp_failover_link_t
*link
;
876 if (l
-> type
!= dhcp_type_failover_link
)
877 return ISC_R_INVALIDARG
;
878 link
= (dhcp_failover_link_t
*)l
;
880 status
= omapi_connection_put_name (c
, "link-port");
881 if (status
!= ISC_R_SUCCESS
)
883 status
= omapi_connection_put_uint32 (c
, sizeof (int));
884 if (status
!= ISC_R_SUCCESS
)
886 status
= omapi_connection_put_uint32 (c
, link
-> peer_port
);
887 if (status
!= ISC_R_SUCCESS
)
890 status
= omapi_connection_put_name (c
, "link-state");
891 if (status
!= ISC_R_SUCCESS
)
893 if (link
-> state
< 0 ||
894 link
-> state
>= dhcp_flink_state_max
)
895 status
= omapi_connection_put_string (c
, "invalid link state");
897 status
= (omapi_connection_put_string
898 (c
, dhcp_flink_state_names
[link
-> state
]));
899 if (status
!= ISC_R_SUCCESS
)
902 if (link
-> inner
&& link
-> inner
-> type
-> stuff_values
)
903 return (*(link
-> inner
-> type
-> stuff_values
)) (c
, id
,
905 return ISC_R_SUCCESS
;
908 /* Set up a listener for the omapi protocol. The handle stored points to
909 a listener object, not a protocol object. */
911 isc_result_t
dhcp_failover_listen (omapi_object_t
*h
)
914 dhcp_failover_listener_t
*obj
, *l
;
915 omapi_value_t
*value
= (omapi_value_t
*)0;
916 omapi_addr_t local_addr
;
919 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
920 "local-port", &value
);
921 if (status
!= ISC_R_SUCCESS
)
923 if (!value
-> value
) {
924 omapi_value_dereference (&value
, MDL
);
925 return ISC_R_INVALIDARG
;
928 status
= omapi_get_int_value (&port
, value
-> value
);
929 omapi_value_dereference (&value
, MDL
);
930 if (status
!= ISC_R_SUCCESS
)
932 local_addr
.port
= port
;
934 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
935 "local-address", &value
);
936 if (status
!= ISC_R_SUCCESS
)
938 if (!value
-> value
) {
940 omapi_value_dereference (&value
, MDL
);
941 return ISC_R_INVALIDARG
;
944 if (value
-> value
-> type
!= omapi_datatype_data
||
945 value
-> value
-> u
.buffer
.len
!= sizeof (struct in_addr
))
948 memcpy (local_addr
.address
, value
-> value
-> u
.buffer
.value
,
949 value
-> value
-> u
.buffer
.len
);
950 local_addr
.addrlen
= value
-> value
-> u
.buffer
.len
;
951 local_addr
.addrtype
= AF_INET
;
953 omapi_value_dereference (&value
, MDL
);
955 /* Are we already listening on this port and address? */
956 for (l
= failover_listeners
; l
; l
= l
-> next
) {
957 if (l
-> address
.port
== local_addr
.port
&&
958 l
-> address
.addrtype
== local_addr
.addrtype
&&
959 l
-> address
.addrlen
== local_addr
.addrlen
&&
960 !memcmp (l
-> address
.address
, local_addr
.address
,
964 /* Already listening. */
966 return ISC_R_SUCCESS
;
968 obj
= (dhcp_failover_listener_t
*)0;
969 status
= dhcp_failover_listener_allocate (&obj
, MDL
);
970 if (status
!= ISC_R_SUCCESS
)
972 obj
-> address
= local_addr
;
974 status
= omapi_listen_addr ((omapi_object_t
*)obj
, &obj
-> address
, 1);
975 if (status
!= ISC_R_SUCCESS
)
978 status
= omapi_object_reference (&h
-> outer
,
979 (omapi_object_t
*)obj
, MDL
);
980 if (status
!= ISC_R_SUCCESS
) {
981 dhcp_failover_listener_dereference (&obj
, MDL
);
984 status
= omapi_object_reference (&obj
-> inner
, h
, MDL
);
985 if (status
!= ISC_R_SUCCESS
) {
986 dhcp_failover_listener_dereference (&obj
, MDL
);
990 /* Put this listener on the list. */
991 if (failover_listeners
) {
992 dhcp_failover_listener_reference (&obj
-> next
,
993 failover_listeners
, MDL
);
994 dhcp_failover_listener_dereference (&failover_listeners
, MDL
);
996 dhcp_failover_listener_reference (&failover_listeners
, obj
, MDL
);
998 return dhcp_failover_listener_dereference (&obj
, MDL
);
1001 /* Signal handler for protocol listener - if we get a connect signal,
1002 create a new protocol connection, otherwise pass the signal down. */
1004 isc_result_t
dhcp_failover_listener_signal (omapi_object_t
*o
,
1005 const char *name
, va_list ap
)
1007 isc_result_t status
;
1008 omapi_connection_object_t
*c
;
1009 dhcp_failover_link_t
*obj
;
1010 dhcp_failover_listener_t
*p
;
1011 dhcp_failover_state_t
*s
, *state
= (dhcp_failover_state_t
*)0;
1013 if (!o
|| o
-> type
!= dhcp_type_failover_listener
)
1014 return ISC_R_INVALIDARG
;
1015 p
= (dhcp_failover_listener_t
*)o
;
1017 /* Not a signal we recognize? */
1018 if (strcmp (name
, "connect")) {
1019 if (p
-> inner
&& p
-> inner
-> type
-> signal_handler
)
1020 return (*(p
-> inner
-> type
-> signal_handler
))
1021 (p
-> inner
, name
, ap
);
1022 return ISC_R_NOTFOUND
;
1025 c
= va_arg (ap
, omapi_connection_object_t
*);
1026 if (!c
|| c
-> type
!= omapi_type_connection
)
1027 return ISC_R_INVALIDARG
;
1029 /* See if we can find a failover_state object that
1030 matches this connection. */
1031 for (s
= failover_states
; s
; s
= s
-> next
) {
1032 if (dhcp_failover_state_match
1033 (s
, (u_int8_t
*)&c
-> remote_addr
.sin_addr
,
1034 sizeof c
-> remote_addr
.sin_addr
)) {
1040 log_info ("failover: listener: no matching state");
1041 return omapi_disconnect ((omapi_object_t
*)c
, 1);
1044 obj
= (dhcp_failover_link_t
*)0;
1045 status
= dhcp_failover_link_allocate (&obj
, MDL
);
1046 if (status
!= ISC_R_SUCCESS
)
1048 obj
-> peer_port
= ntohs (c
-> remote_addr
.sin_port
);
1050 status
= omapi_object_reference (&obj
-> outer
,
1051 (omapi_object_t
*)c
, MDL
);
1052 if (status
!= ISC_R_SUCCESS
) {
1054 dhcp_failover_link_dereference (&obj
, MDL
);
1055 log_info ("failover: listener: picayune failure.");
1056 omapi_disconnect ((omapi_object_t
*)c
, 1);
1060 status
= omapi_object_reference (&c
-> inner
,
1061 (omapi_object_t
*)obj
, MDL
);
1062 if (status
!= ISC_R_SUCCESS
)
1065 status
= dhcp_failover_state_reference (&obj
-> state_object
,
1067 if (status
!= ISC_R_SUCCESS
)
1070 omapi_signal_in ((omapi_object_t
*)obj
, "connect");
1072 return dhcp_failover_link_dereference (&obj
, MDL
);
1075 isc_result_t
dhcp_failover_listener_set_value (omapi_object_t
*h
,
1077 omapi_data_string_t
*name
,
1078 omapi_typed_data_t
*value
)
1080 if (h
-> type
!= dhcp_type_failover_listener
)
1081 return ISC_R_INVALIDARG
;
1083 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
1084 return (*(h
-> inner
-> type
-> set_value
))
1085 (h
-> inner
, id
, name
, value
);
1086 return ISC_R_NOTFOUND
;
1089 isc_result_t
dhcp_failover_listener_get_value (omapi_object_t
*h
,
1091 omapi_data_string_t
*name
,
1092 omapi_value_t
**value
)
1094 if (h
-> type
!= dhcp_type_failover_listener
)
1095 return ISC_R_INVALIDARG
;
1097 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
1098 return (*(h
-> inner
-> type
-> get_value
))
1099 (h
-> inner
, id
, name
, value
);
1100 return ISC_R_NOTFOUND
;
1103 isc_result_t
dhcp_failover_listener_destroy (omapi_object_t
*h
,
1104 const char *file
, int line
)
1106 dhcp_failover_listener_t
*l
;
1108 if (h
-> type
!= dhcp_type_failover_listener
)
1109 return ISC_R_INVALIDARG
;
1110 l
= (dhcp_failover_listener_t
*)h
;
1112 dhcp_failover_listener_dereference (&l
-> next
, file
, line
);
1114 return ISC_R_SUCCESS
;
1117 /* Write all the published values associated with the object through the
1118 specified connection. */
1120 isc_result_t
dhcp_failover_listener_stuff (omapi_object_t
*c
,
1126 if (p
-> type
!= dhcp_type_failover_listener
)
1127 return ISC_R_INVALIDARG
;
1129 if (p
-> inner
&& p
-> inner
-> type
-> stuff_values
)
1130 return (*(p
-> inner
-> type
-> stuff_values
)) (c
, id
,
1132 return ISC_R_SUCCESS
;
1135 /* Set up master state machine for the failover protocol. */
1137 isc_result_t
dhcp_failover_register (omapi_object_t
*h
)
1139 isc_result_t status
;
1140 dhcp_failover_state_t
*obj
;
1142 omapi_value_t
*value
= (omapi_value_t
*)0;
1144 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
1145 "local-port", &value
);
1146 if (status
!= ISC_R_SUCCESS
)
1148 if (!value
-> value
) {
1149 omapi_value_dereference (&value
, MDL
);
1150 return ISC_R_INVALIDARG
;
1153 status
= omapi_get_int_value (&port
, value
-> value
);
1154 omapi_value_dereference (&value
, MDL
);
1155 if (status
!= ISC_R_SUCCESS
)
1158 obj
= (dhcp_failover_state_t
*)0;
1159 dhcp_failover_state_allocate (&obj
, MDL
);
1160 obj
-> me
.port
= port
;
1162 status
= omapi_listen ((omapi_object_t
*)obj
, port
, 1);
1163 if (status
!= ISC_R_SUCCESS
) {
1164 dhcp_failover_state_dereference (&obj
, MDL
);
1168 status
= omapi_object_reference (&h
-> outer
, (omapi_object_t
*)obj
,
1170 if (status
!= ISC_R_SUCCESS
) {
1171 dhcp_failover_state_dereference (&obj
, MDL
);
1174 status
= omapi_object_reference (&obj
-> inner
, h
, MDL
);
1175 dhcp_failover_state_dereference (&obj
, MDL
);
1179 /* Signal handler for protocol state machine. */
1181 isc_result_t
dhcp_failover_state_signal (omapi_object_t
*o
,
1182 const char *name
, va_list ap
)
1184 isc_result_t status
;
1185 omapi_connection_object_t
*c
;
1186 omapi_protocol_object_t
*obj
;
1187 dhcp_failover_state_t
*state
;
1188 dhcp_failover_link_t
*link
;
1191 if (!o
|| o
-> type
!= dhcp_type_failover_state
)
1192 return ISC_R_INVALIDARG
;
1193 state
= (dhcp_failover_state_t
*)o
;
1195 /* Not a signal we recognize? */
1196 if (strcmp (name
, "disconnect") &&
1197 strcmp (name
, "message")) {
1198 if (state
-> inner
&& state
-> inner
-> type
-> signal_handler
)
1199 return (*(state
-> inner
-> type
-> signal_handler
))
1200 (state
-> inner
, name
, ap
);
1201 return ISC_R_NOTFOUND
;
1204 /* Handle connect signals by seeing what state we're in
1205 and potentially doing a state transition. */
1206 if (!strcmp (name
, "disconnect")) {
1207 link
= va_arg (ap
, dhcp_failover_link_t
*);
1209 dhcp_failover_link_dereference (&state
-> link_to_peer
, MDL
);
1210 dhcp_failover_state_transition (state
, "disconnect");
1211 if (state
-> i_am
== primary
) {
1212 #if defined (DEBUG_FAILOVER_TIMING)
1213 log_info ("add_timeout +90 %s",
1214 "dhcp_failover_reconnect");
1216 add_timeout (cur_time
+ 90, dhcp_failover_reconnect
,
1218 (tvref_t
)dhcp_failover_state_reference
,
1220 dhcp_failover_state_dereference
);
1222 } else if (!strcmp (name
, "message")) {
1223 link
= va_arg (ap
, dhcp_failover_link_t
*);
1225 if (link
-> imsg
-> type
== FTM_CONNECT
) {
1226 /* If we already have a link to the peer, it must be
1228 XXX Is this the right thing to do?
1229 XXX Probably not - what if both peers start at
1230 XXX the same time? */
1231 if (state
-> link_to_peer
) {
1232 dhcp_failover_send_connectack
1233 ((omapi_object_t
*)link
, state
,
1235 "already connected");
1236 omapi_disconnect (link
-> outer
, 1);
1237 return ISC_R_SUCCESS
;
1239 if (!(link
-> imsg
-> options_present
& FTB_MCLT
)) {
1240 dhcp_failover_send_connectack
1241 ((omapi_object_t
*)link
, state
,
1243 "no MCLT provided");
1244 omapi_disconnect (link
-> outer
, 1);
1245 return ISC_R_SUCCESS
;
1248 dhcp_failover_link_reference (&state
-> link_to_peer
,
1250 status
= (dhcp_failover_send_connectack
1251 ((omapi_object_t
*)link
, state
, 0, 0));
1252 if (status
!= ISC_R_SUCCESS
) {
1253 dhcp_failover_link_dereference
1254 (&state
-> link_to_peer
, MDL
);
1255 log_info ("dhcp_failover_send_connectack: %s",
1256 isc_result_totext (status
));
1257 omapi_disconnect (link
-> outer
, 1);
1258 return ISC_R_SUCCESS
;
1260 if (link
-> imsg
-> options_present
& FTB_MAX_UNACKED
)
1261 state
-> partner
.max_flying_updates
=
1262 link
-> imsg
-> max_unacked
;
1263 if (link
-> imsg
-> options_present
&
1265 state
-> partner
.max_response_delay
=
1266 link
-> imsg
-> receive_timer
;
1267 state
-> mclt
= link
-> imsg
-> mclt
;
1268 dhcp_failover_send_state (state
);
1269 cancel_timeout (dhcp_failover_link_startup_timeout
,
1271 } else if (link
-> imsg
-> type
== FTM_CONNECTACK
) {
1275 cancel_timeout (dhcp_failover_link_startup_timeout
,
1278 if (link
-> imsg
-> reject_reason
) {
1279 log_error ("Failover CONNECT to %u.%u.%u.%u%s%s",
1281 (&link
-> imsg
-> server_addr
)) [0],
1283 (&link
-> imsg
-> server_addr
)) [1],
1285 (&link
-> imsg
-> server_addr
)) [2],
1287 (&link
-> imsg
-> server_addr
)) [3],
1289 (dhcp_failover_reject_reason_print
1290 (link
-> imsg
-> reject_reason
)));
1291 /* XXX print message from peer if peer sent message. */
1292 omapi_disconnect (link
-> outer
, 1);
1293 return ISC_R_SUCCESS
;
1296 if (!dhcp_failover_state_match
1298 (u_int8_t
*)&link
-> imsg
-> server_addr
,
1299 sizeof link
-> imsg
-> server_addr
)) {
1300 errmsg
= "unknown server";
1301 reason
= FTR_INVALID_PARTNER
;
1303 log_error ("Failover CONNECTACK from %u.%u.%u.%u: %s",
1305 (&link
-> imsg
-> server_addr
)) [0],
1307 (&link
-> imsg
-> server_addr
)) [1],
1309 (&link
-> imsg
-> server_addr
)) [2],
1311 (&link
-> imsg
-> server_addr
)) [3],
1313 dhcp_failover_send_disconnect ((omapi_object_t
*)link
,
1315 omapi_disconnect (link
-> outer
, 0);
1316 return ISC_R_SUCCESS
;
1319 if (state
-> link_to_peer
) {
1320 errmsg
= "already connected";
1321 reason
= FTR_DUP_CONNECTION
;
1325 if ((cur_time
> link
-> imsg
-> time
&&
1326 cur_time
- link
-> imsg
-> time
> 60) ||
1327 (cur_time
< link
-> imsg
-> time
&&
1328 link
-> imsg
-> time
- cur_time
> 60)) {
1329 errmsg
= "time offset too large";
1330 reason
= FTR_TIMEMISMATCH
;
1334 dhcp_failover_link_reference (&state
-> link_to_peer
,
1337 /* XXX This is probably the right thing to do, but
1338 XXX for release three, to make the smallest possible
1339 XXX change, we are doing this when the peer state
1340 XXX changes instead. */
1341 if (state
-> me
.state
== startup
)
1342 dhcp_failover_set_state (state
,
1343 state
-> saved_state
);
1346 dhcp_failover_send_state (state
);
1348 if (link
-> imsg
-> options_present
& FTB_MAX_UNACKED
)
1349 state
-> partner
.max_flying_updates
=
1350 link
-> imsg
-> max_unacked
;
1351 if (link
-> imsg
-> options_present
& FTB_RECEIVE_TIMER
)
1352 state
-> partner
.max_response_delay
=
1353 link
-> imsg
-> receive_timer
;
1354 #if defined (DEBUG_FAILOVER_TIMING)
1355 log_info ("add_timeout +%d %s",
1356 (int)state
-> partner
.max_response_delay
/ 3,
1357 "dhcp_failover_send_contact");
1359 add_timeout (cur_time
+
1360 (int)state
-> partner
.max_response_delay
/ 3,
1361 dhcp_failover_send_contact
, state
,
1362 (tvref_t
)dhcp_failover_state_reference
,
1363 (tvunref_t
)dhcp_failover_state_dereference
);
1364 #if defined (DEBUG_FAILOVER_TIMING)
1365 log_info ("add_timeout +%d %s",
1366 (int)state
-> me
.max_response_delay
,
1367 "dhcp_failover_timeout");
1369 add_timeout (cur_time
+
1370 (int)state
-> me
.max_response_delay
,
1371 dhcp_failover_timeout
, state
,
1372 (tvref_t
)dhcp_failover_state_reference
,
1373 (tvunref_t
)dhcp_failover_state_dereference
);
1374 } else if (link
-> imsg
-> type
== FTM_DISCONNECT
) {
1375 if (link
-> imsg
-> reject_reason
) {
1376 log_error ("Failover DISCONNECT from %u.%u.%u.%u%s%s",
1378 (&link
-> imsg
-> server_addr
)) [0],
1380 (&link
-> imsg
-> server_addr
)) [1],
1382 (&link
-> imsg
-> server_addr
)) [2],
1384 (&link
-> imsg
-> server_addr
)) [3],
1386 (dhcp_failover_reject_reason_print
1387 (link
-> imsg
-> reject_reason
)));
1389 omapi_disconnect (link
-> outer
, 1);
1390 } else if (link
-> imsg
-> type
== FTM_BNDUPD
) {
1391 dhcp_failover_process_bind_update (state
,
1393 } else if (link
-> imsg
-> type
== FTM_BNDACK
) {
1394 dhcp_failover_process_bind_ack (state
, link
-> imsg
);
1395 } else if (link
-> imsg
-> type
== FTM_UPDREQ
) {
1396 dhcp_failover_process_update_request (state
,
1398 } else if (link
-> imsg
-> type
== FTM_UPDREQALL
) {
1399 dhcp_failover_process_update_request_all
1400 (state
, link
-> imsg
);
1401 } else if (link
-> imsg
-> type
== FTM_UPDDONE
) {
1402 dhcp_failover_process_update_done (state
,
1404 } else if (link
-> imsg
-> type
== FTM_POOLREQ
) {
1405 dhcp_failover_pool_rebalance (state
);
1406 } else if (link
-> imsg
-> type
== FTM_POOLRESP
) {
1407 log_info ("pool response: %ld leases",
1409 link
-> imsg
-> addresses_transferred
);
1410 } else if (link
-> imsg
-> type
== FTM_STATE
) {
1411 dhcp_failover_peer_state_changed (state
,
1415 /* Add a timeout so that if the partner doesn't send
1416 another message for the maximum transmit idle time
1417 plus a grace of one second, we close the
1419 if (state
-> link_to_peer
&&
1420 state
-> link_to_peer
== link
&&
1421 state
-> link_to_peer
-> state
!= dhcp_flink_disconnected
)
1423 #if defined (DEBUG_FAILOVER_TIMING)
1424 log_info ("add_timeout +%d %s",
1425 (int)state
-> me
.max_response_delay
,
1426 "dhcp_failover_timeout");
1428 add_timeout (cur_time
+
1429 (int)state
-> me
.max_response_delay
,
1430 dhcp_failover_timeout
, state
,
1431 (tvref_t
)dhcp_failover_state_reference
,
1432 (tvunref_t
)dhcp_failover_state_dereference
);
1437 /* Handle all the events we care about... */
1438 return ISC_R_SUCCESS
;
1441 isc_result_t
dhcp_failover_state_transition (dhcp_failover_state_t
*state
,
1444 isc_result_t status
;
1446 /* XXX Check these state transitions against the spec! */
1447 if (!strcmp (name
, "disconnect")) {
1448 if (state
-> link_to_peer
) {
1449 log_info ("peer %s: disconnected", state
-> name
);
1450 if (state
-> link_to_peer
-> state_object
)
1451 dhcp_failover_state_dereference
1452 (&state
-> link_to_peer
-> state_object
, MDL
);
1453 dhcp_failover_link_dereference (&state
-> link_to_peer
,
1456 cancel_timeout (dhcp_failover_send_contact
, state
);
1457 cancel_timeout (dhcp_failover_timeout
, state
);
1458 cancel_timeout (dhcp_failover_startup_timeout
, state
);
1460 switch (state
-> me
.state
== startup
?
1461 state
-> saved_state
: state
-> me
.state
) {
1462 case resolution_interrupted
:
1464 case communications_interrupted
:
1466 /* Already in the right state? */
1467 if (state
-> me
.state
== startup
)
1468 return (dhcp_failover_set_state
1469 (state
, state
-> saved_state
));
1470 return ISC_R_SUCCESS
;
1472 case potential_conflict
:
1473 return dhcp_failover_set_state
1474 (state
, resolution_interrupted
);
1477 return dhcp_failover_set_state
1478 (state
, communications_interrupted
);
1481 return dhcp_failover_set_state
1482 (state
, resolution_interrupted
);
1484 break; /* can't happen. */
1486 } else if (!strcmp (name
, "connect")) {
1487 switch (state
-> me
.state
) {
1488 case communications_interrupted
:
1489 status
= dhcp_failover_set_state (state
, normal
);
1490 dhcp_failover_send_updates (state
);
1493 case resolution_interrupted
:
1494 return dhcp_failover_set_state (state
,
1495 potential_conflict
);
1498 case potential_conflict
:
1507 return dhcp_failover_send_state (state
);
1509 } else if (!strcmp (name
, "startup")) {
1510 dhcp_failover_set_state (state
, startup
);
1511 return ISC_R_SUCCESS
;
1512 } else if (!strcmp (name
, "connect-timeout")) {
1513 switch (state
-> me
.state
) {
1514 case communications_interrupted
:
1516 case resolution_interrupted
:
1517 return ISC_R_SUCCESS
;
1521 return dhcp_failover_set_state
1522 (state
, communications_interrupted
);
1524 case potential_conflict
:
1525 return dhcp_failover_set_state
1526 (state
, resolution_interrupted
);
1529 return dhcp_failover_set_state
1530 (state
, communications_interrupted
);
1533 return dhcp_failover_set_state
1534 (state
, resolution_interrupted
);
1537 return ISC_R_INVALIDARG
;
1540 isc_result_t
dhcp_failover_set_service_state (dhcp_failover_state_t
*state
)
1542 switch (state
-> me
.state
) {
1544 state
-> service_state
= not_responding
;
1545 state
-> nrr
= " (my state unknown)";
1549 state
-> service_state
= service_partner_down
;
1554 state
-> service_state
= cooperating
;
1558 case communications_interrupted
:
1559 state
-> service_state
= not_cooperating
;
1563 case resolution_interrupted
:
1564 case potential_conflict
:
1565 state
-> service_state
= not_responding
;
1566 state
-> nrr
= " (resolving conflicts)";
1570 state
-> service_state
= not_responding
;
1571 state
-> nrr
= " (recovering)";
1575 state
-> service_state
= not_responding
;
1576 state
-> nrr
= " (shut down)";
1580 state
-> service_state
= not_responding
;
1581 state
-> nrr
= " (paused)";
1585 state
-> service_state
= not_responding
;
1586 state
-> nrr
= " (recover wait)";
1590 state
-> service_state
= not_responding
;
1591 state
-> nrr
= " (recover done)";
1595 state
-> service_state
= service_startup
;
1596 state
-> nrr
= " (startup)";
1600 /* Some peer states can require us not to respond, even if our
1602 /* XXX hm. I suspect this isn't true anymore. */
1603 if (state
-> service_state
!= not_responding
) {
1604 switch (state
-> partner
.state
) {
1606 state
-> service_state
= not_responding
;
1607 state
-> nrr
= " (recovering)";
1610 case potential_conflict
:
1611 state
-> service_state
= not_responding
;
1612 state
-> nrr
= " (resolving conflicts)";
1615 /* Other peer states don't affect our behaviour. */
1621 return ISC_R_SUCCESS
;
1624 isc_result_t
dhcp_failover_set_state (dhcp_failover_state_t
*state
,
1625 enum failover_state new_state
)
1627 enum failover_state saved_state
;
1630 struct shared_network
*s
;
1633 /* First make the transition out of the current state. */
1634 switch (state
-> me
.state
) {
1636 /* Any updates that haven't been acked yet, we have to
1637 resend, just in case. */
1638 if (state
-> ack_queue_tail
) {
1641 /* Zap the flags. */
1642 for (lp
= state
-> ack_queue_head
; lp
; lp
= lp
-> next_pending
)
1643 lp
-> flags
= ((lp
-> flags
& ~ON_ACK_QUEUE
) |
1646 /* Now hook the ack queue to the beginning of the update
1648 if (state
-> update_queue_head
) {
1649 lease_reference (&state
-> ack_queue_tail
-> next_pending
,
1650 state
-> update_queue_head
, MDL
);
1651 lease_dereference (&state
-> update_queue_head
, MDL
);
1653 lease_reference (&state
-> update_queue_head
,
1654 state
-> ack_queue_head
, MDL
);
1655 if (!state
-> update_queue_tail
) {
1656 #if defined (POINTER_DEBUG)
1657 if (state
-> ack_queue_tail
-> next_pending
) {
1658 log_error ("next pending on ack queue tail.");
1662 lease_reference (&state
-> update_queue_tail
,
1663 state
-> ack_queue_tail
, MDL
);
1665 lease_dereference (&state
-> ack_queue_tail
, MDL
);
1666 lease_dereference (&state
-> ack_queue_head
, MDL
);
1667 state
-> cur_unacked_updates
= 0;
1669 cancel_timeout (dhcp_failover_keepalive
, state
);
1675 case potential_conflict
:
1677 case communications_interrupted
:
1678 case resolution_interrupted
:
1684 /* Tentatively make the transition. */
1685 saved_state
= state
-> me
.state
;
1686 saved_stos
= state
-> me
.stos
;
1688 /* Keep the old stos if we're going into recover_wait or if we're
1689 coming into or out of startup. */
1690 if (new_state
!= recover_wait
&& new_state
!= startup
&&
1691 saved_state
!= startup
)
1692 state
-> me
.stos
= cur_time
;
1694 /* If we're in shutdown, peer is in partner_down, and we're moving
1695 to recover, we can skip waiting for MCLT to expire. This happens
1696 when a server is moved administratively into shutdown prior to
1697 actually shutting down. Of course, if there are any updates
1698 pending we can't actually do this. */
1699 if (new_state
== recover
&& saved_state
== shut_down
&&
1700 state
-> partner
.state
== partner_down
&&
1701 !state
-> update_queue_head
&& !state
-> ack_queue_head
)
1702 state
-> me
.stos
= cur_time
- state
-> mclt
;
1704 state
-> me
.state
= new_state
;
1705 if (new_state
== startup
&& saved_state
!= startup
)
1706 state
-> saved_state
= saved_state
;
1708 /* If we can't record the new state, we can't make a state transition. */
1709 if (!write_failover_state (state
) || !commit_leases ()) {
1710 log_error ("Unable to record current failover state for %s",
1712 state
-> me
.state
= saved_state
;
1713 state
-> me
.stos
= saved_stos
;
1714 return ISC_R_IOERROR
;
1717 log_info ("failover peer %s: I move from %s to %s",
1718 state
-> name
, dhcp_failover_state_name_print (saved_state
),
1719 dhcp_failover_state_name_print (state
-> me
.state
));
1721 /* If we were in startup and we just left it, cancel the timeout. */
1722 if (new_state
!= startup
&& saved_state
== startup
)
1723 cancel_timeout (dhcp_failover_startup_timeout
, state
);
1725 /* Set our service state. */
1726 dhcp_failover_set_service_state (state
);
1728 /* Tell the peer about it. */
1729 if (state
-> link_to_peer
)
1730 dhcp_failover_send_state (state
);
1732 switch (new_state
) {
1734 if (state
-> partner
.state
== normal
)
1735 dhcp_failover_state_pool_check (state
);
1738 case potential_conflict
:
1739 if (state
-> i_am
== primary
)
1740 dhcp_failover_send_update_request (state
);
1744 #if defined (DEBUG_FAILOVER_TIMING)
1745 log_info ("add_timeout +15 %s",
1746 "dhcp_failover_startup_timeout");
1748 add_timeout (cur_time
+ 15,
1749 dhcp_failover_startup_timeout
,
1751 (tvref_t
)omapi_object_reference
,
1753 omapi_object_dereference
);
1756 /* If we come back in recover_wait and there's still waiting
1757 to do, set a timeout. */
1759 if (state
-> me
.stos
+ state
-> mclt
> cur_time
) {
1760 #if defined (DEBUG_FAILOVER_TIMING)
1761 log_info ("add_timeout +%d %s",
1763 state
-> me
.stos
+ state
-> mclt
),
1764 "dhcp_failover_startup_timeout");
1766 add_timeout ((int)(state
-> me
.stos
+ state
-> mclt
),
1767 dhcp_failover_recover_done
,
1769 (tvref_t
)omapi_object_reference
,
1771 omapi_object_dereference
);
1773 dhcp_failover_recover_done (state
);
1777 if (state
-> link_to_peer
)
1778 dhcp_failover_send_update_request_all (state
);
1782 /* For every expired lease, set a timeout for it to become free. */
1783 for (s
= shared_networks
; s
; s
= s
-> next
) {
1784 for (p
= s
-> pools
; p
; p
= p
-> next
) {
1785 if (p
-> failover_peer
== state
) {
1786 for (l
= p
->expired
; l
; l
= l
->next
) {
1787 l
->tsfp
= state
->me
.stos
+ state
->mclt
;
1788 l
->sort_time
= (l
->tsfp
> l
->ends
) ?
1792 (p
->expired
->sort_time
< p
->next_event_time
)) {
1794 p
->next_event_time
= p
->expired
->sort_time
;
1795 #if defined (DEBUG_FAILOVER_TIMING)
1796 log_info ("add_timeout +%d %s",
1797 (int)(cur_time
- p
->next_event_time
),
1800 add_timeout(p
->next_event_time
, pool_timer
, p
,
1801 (tvref_t
)pool_reference
,
1802 (tvunref_t
)pool_dereference
);
1814 return ISC_R_SUCCESS
;
1817 isc_result_t
dhcp_failover_peer_state_changed (dhcp_failover_state_t
*state
,
1818 failover_message_t
*msg
)
1820 enum failover_state previous_state
= state
-> partner
.state
;
1821 enum failover_state new_state
;
1823 isc_result_t status
;
1825 new_state
= msg
-> server_state
;
1826 startupp
= (msg
-> server_flags
& FTF_STARTUP
) ? 1 : 0;
1828 if (state
-> partner
.state
== new_state
&& state
-> me
.state
) {
1829 switch (state
-> me
.state
) {
1831 dhcp_failover_set_state (state
, state
-> saved_state
);
1832 return ISC_R_SUCCESS
;
1836 case potential_conflict
:
1841 return ISC_R_SUCCESS
;
1843 /* If we get a peer state change when we're
1844 disconnected, we always process it. */
1846 case communications_interrupted
:
1847 case resolution_interrupted
:
1853 state
-> partner
.state
= new_state
;
1855 log_info ("failover peer %s: peer moves from %s to %s",
1857 dhcp_failover_state_name_print (previous_state
),
1858 dhcp_failover_state_name_print (state
-> partner
.state
));
1860 if (!write_failover_state (state
) || !commit_leases ()) {
1861 /* This is bad, but it's not fatal. Of course, if we
1862 can't write to the lease database, we're not going to
1863 get much done anyway. */
1864 log_error ("Unable to record current failover state for %s",
1868 /* Do any state transitions that are required as a result of the
1869 peer's state transition. */
1871 switch (state
-> me
.state
== startup
?
1872 state
-> saved_state
: state
-> me
.state
) {
1873 case startup
: /* can't happen. */
1877 switch (new_state
) {
1879 dhcp_failover_state_pool_check (state
);
1882 case communications_interrupted
:
1886 if (state
-> me
.state
== startup
)
1887 dhcp_failover_set_state (state
, recover
);
1889 dhcp_failover_set_state (state
,
1890 potential_conflict
);
1893 case potential_conflict
:
1894 case resolution_interrupted
:
1895 /* None of these transitions should ever occur. */
1896 dhcp_failover_set_state (state
, shut_down
);
1900 dhcp_failover_set_state (state
, partner_down
);
1904 /* XXX This one is specified, but it's specified in
1905 XXX the documentation for the shut_down state,
1906 XXX not the normal state. */
1907 dhcp_failover_set_state (state
, partner_down
);
1911 dhcp_failover_set_state (state
,
1912 communications_interrupted
);
1917 /* We probably don't need to do anything here. */
1927 switch (new_state
) {
1929 log_info ("failover peer %s: requesting %s",
1930 state
-> name
, "full update from peer");
1931 /* Don't send updreqall if we're really in the
1932 startup state, because that will result in two
1934 if (state
-> me
.state
== recover
)
1935 dhcp_failover_send_update_request_all (state
);
1938 case potential_conflict
:
1939 case resolution_interrupted
:
1941 dhcp_failover_set_state (state
, potential_conflict
);
1945 case communications_interrupted
:
1946 /* We're supposed to send an update request at this
1948 /* XXX we don't currently have code here to do any
1949 XXX clever detection of when we should send an
1950 XXX UPDREQALL message rather than an UPDREQ
1951 XXX message. What to do, what to do? */
1952 /* Currently when we enter recover state, no matter
1953 * the reason, we send an UPDREQALL. So, it makes
1954 * the most sense to stick to that until something
1956 * Furthermore, we only went to send the update
1957 * request if we are not in startup state.
1959 if (state
-> me
.state
== recover
)
1960 dhcp_failover_send_update_request_all (state
);
1964 /* XXX We're not explicitly told what to do in this
1965 XXX case, but this transition is consistent with
1966 XXX what is elsewhere in the draft. */
1967 dhcp_failover_set_state (state
, partner_down
);
1970 /* We can't really do anything in this case. */
1974 /* We should have asked for an update already. */
1985 case potential_conflict
:
1986 switch (new_state
) {
1988 if (previous_state
== potential_conflict
&&
1989 state
-> i_am
== secondary
)
1990 dhcp_failover_send_update_request (state
);
1995 case potential_conflict
:
1997 case communications_interrupted
:
1998 case resolution_interrupted
:
2003 dhcp_failover_set_state (state
, recover
);
2007 dhcp_failover_set_state (state
, partner_down
);
2017 /* Take no action if other server is starting up. */
2021 switch (new_state
) {
2022 /* This is where we should be. */
2028 dhcp_failover_set_state (state
, normal
);
2032 case potential_conflict
:
2034 case communications_interrupted
:
2035 case resolution_interrupted
:
2036 dhcp_failover_set_state (state
, potential_conflict
);
2039 /* These don't change anything. */
2050 case communications_interrupted
:
2051 switch (new_state
) {
2053 /* Stick with the status quo. */
2056 /* If we're in communications-interrupted and an
2057 amnesiac peer connects, go to the partner_down
2058 state immediately. */
2060 dhcp_failover_set_state (state
, partner_down
);
2064 case communications_interrupted
:
2067 /* XXX so we don't need to do this specially in
2068 XXX the CONNECT and CONNECTACK handlers. */
2069 dhcp_failover_send_updates (state
);
2070 dhcp_failover_set_state (state
, normal
);
2073 case potential_conflict
:
2075 case resolution_interrupted
:
2076 dhcp_failover_set_state (state
, potential_conflict
);
2080 dhcp_failover_set_state (state
, partner_down
);
2089 case resolution_interrupted
:
2090 switch (new_state
) {
2093 case potential_conflict
:
2095 case communications_interrupted
:
2096 case resolution_interrupted
:
2099 dhcp_failover_set_state (state
, potential_conflict
);
2103 dhcp_failover_set_state (state
, partner_down
);
2116 switch (new_state
) {
2119 dhcp_failover_set_state (state
, normal
);
2122 case potential_conflict
:
2124 case communications_interrupted
:
2125 case resolution_interrupted
:
2132 dhcp_failover_set_state (state
, partner_down
);
2141 /* We are essentially dead in the water when we're in
2142 either shut_down or paused states, and do not do any
2143 automatic state transitions. */
2148 /* We still have to wait... */
2156 /* If we didn't make a transition out of startup as a result of
2157 the peer's state change, do it now as a result of the fact that
2158 we got a state change from the peer. */
2159 if (state
-> me
.state
== startup
&& state
-> saved_state
!= startup
)
2160 dhcp_failover_set_state (state
, state
-> saved_state
);
2162 /* For now, just set the service state based on the peer's state
2164 dhcp_failover_set_service_state (state
);
2166 return ISC_R_SUCCESS
;
2169 int dhcp_failover_pool_rebalance (dhcp_failover_state_t
*state
)
2172 int leases_queued
= 0;
2173 struct lease
*lp
= (struct lease
*)0;
2174 struct lease
*next
= (struct lease
*)0;
2175 struct shared_network
*s
;
2178 binding_state_t peer_lease_state
;
2179 binding_state_t my_lease_state
;
2183 if (state
-> me
.state
!= normal
|| state
-> i_am
== secondary
)
2186 for (s
= shared_networks
; s
; s
= s
-> next
) {
2187 for (p
= s
-> pools
; p
; p
= p
-> next
) {
2188 if (p
-> failover_peer
!= state
)
2191 /* Right now we're giving the peer half of the free leases.
2192 If we have more leases than the peer (i.e., more than
2193 half), then the number of leases we have, less the number
2194 of leases the peer has, will be how many more leases we
2195 have than the peer has. So if we send half that number
2196 to the peer, we should be even. */
2197 if (p
-> failover_peer
-> i_am
== primary
) {
2198 lts
= (p
-> free_leases
- p
-> backup_leases
) / 2;
2199 peer_lease_state
= FTS_BACKUP
;
2200 my_lease_state
= FTS_FREE
;
2203 lts
= (p
-> backup_leases
- p
-> free_leases
) / 2;
2204 peer_lease_state
= FTS_FREE
;
2205 my_lease_state
= FTS_BACKUP
;
2209 tenper
= (p
-> backup_leases
+ p
-> free_leases
) / 10;
2213 log_info ("pool %lx %s total %d free %d %s %d lts %d",
2215 (p
-> shared_network
?
2216 p
-> shared_network
-> name
: ""), p
-> lease_count
,
2217 p
-> free_leases
, "backup", p
-> backup_leases
, lts
);
2219 lease_reference (&lp
, *lq
, MDL
);
2222 /* Remember the next lease in the list. */
2224 lease_dereference (&next
, MDL
);
2226 lease_reference (&next
, lp
-> next
, MDL
);
2230 lp
-> next_binding_state
= peer_lease_state
;
2231 lp
-> tstp
= cur_time
;
2232 lp
-> starts
= cur_time
;
2234 if (!supersede_lease (lp
, (struct lease
*)0, 0, 1, 0)
2235 || !write_lease (lp
))
2237 log_info ("can't commit lease %s on giveaway",
2238 piaddr (lp
-> ip_addr
));
2241 lease_dereference (&lp
, MDL
);
2243 lease_reference (&lp
, next
, MDL
);
2246 lease_dereference (&next
, MDL
);
2248 lease_dereference (&lp
, MDL
);
2252 log_info ("lease imbalance - lts = %d", lts
);
2257 dhcp_failover_send_poolresp (state
, leases_queued
);
2258 dhcp_failover_send_updates (state
);
2259 return leases_queued
;
2262 int dhcp_failover_pool_check (struct pool
*pool
)
2268 if (!pool
-> failover_peer
||
2269 pool
-> failover_peer
-> me
.state
!= normal
)
2272 if (pool
-> failover_peer
-> i_am
== primary
)
2273 lts
= (pool
-> backup_leases
- pool
-> free_leases
) / 2;
2275 lts
= (pool
-> free_leases
- pool
-> backup_leases
) / 2;
2277 log_info ("pool %lx %s total %d free %d backup %d lts %d",
2278 (unsigned long)pool
,
2279 pool
-> shared_network
? pool
-> shared_network
-> name
: "",
2280 pool
-> lease_count
,
2281 pool
-> free_leases
, pool
-> backup_leases
, lts
);
2283 tenper
= (pool
-> backup_leases
+ pool
-> free_leases
) / 10;
2287 /* XXX What about multiple pools? */
2288 if (pool
-> failover_peer
-> i_am
== secondary
) {
2289 /* Ask the primary to send us leases. */
2290 dhcp_failover_send_poolreq (pool
-> failover_peer
);
2293 /* Figure out how many leases to skip on the backup
2294 list. We skip the earliest leases on the list
2295 to reduce the chance of trying to steal a lease
2296 that the secondary is about to allocate. */
2297 int i
= pool
-> backup_leases
- lts
;
2298 log_info ("Taking %d leases from secondary.", lts
);
2299 for (lp
= pool
-> backup
; lp
; lp
= lp
-> next
) {
2300 /* Skip to the last leases on the free
2301 list, because they are less likely
2302 to already have been allocated. */
2306 lp
-> desired_binding_state
= FTS_FREE
;
2307 dhcp_failover_queue_update (lp
, 1);
2312 log_info ("failed to take %d leases.", lts
);
2318 int dhcp_failover_state_pool_check (dhcp_failover_state_t
*state
)
2321 struct shared_network
*s
;
2324 for (s
= shared_networks
; s
; s
= s
-> next
) {
2325 for (p
= s
-> pools
; p
; p
= p
-> next
) {
2326 if (p
-> failover_peer
!= state
)
2328 /* Only need to request rebalance on one pool. */
2329 if (dhcp_failover_pool_check (p
))
2336 isc_result_t
dhcp_failover_send_updates (dhcp_failover_state_t
*state
)
2338 struct lease
*lp
= (struct lease
*)0;
2339 isc_result_t status
;
2341 /* Can't update peer if we're not talking to it! */
2342 if (!state
-> link_to_peer
)
2343 return ISC_R_SUCCESS
;
2345 while ((state
-> partner
.max_flying_updates
>
2346 state
-> cur_unacked_updates
) && state
-> update_queue_head
) {
2347 /* Grab the head of the update queue. */
2348 lease_reference (&lp
, state
-> update_queue_head
, MDL
);
2350 /* Send the update to the peer. */
2351 status
= dhcp_failover_send_bind_update (state
, lp
);
2352 if (status
!= ISC_R_SUCCESS
) {
2353 lease_dereference (&lp
, MDL
);
2356 lp
-> flags
&= ~ON_UPDATE_QUEUE
;
2358 /* Take it off the head of the update queue and put the next
2359 item in the update queue at the head. */
2360 lease_dereference (&state
-> update_queue_head
, MDL
);
2361 if (lp
-> next_pending
) {
2362 lease_reference (&state
-> update_queue_head
,
2363 lp
-> next_pending
, MDL
);
2364 lease_dereference (&lp
-> next_pending
, MDL
);
2366 lease_dereference (&state
-> update_queue_tail
, MDL
);
2369 if (state
-> ack_queue_head
) {
2371 (&state
-> ack_queue_tail
-> next_pending
,
2373 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2375 lease_reference (&state
-> ack_queue_head
, lp
, MDL
);
2377 #if defined (POINTER_DEBUG)
2378 if (lp
-> next_pending
) {
2379 log_error ("ack_queue_tail: lp -> next_pending");
2383 lease_reference (&state
-> ack_queue_tail
, lp
, MDL
);
2384 lp
-> flags
|= ON_ACK_QUEUE
;
2385 lease_dereference (&lp
, MDL
);
2387 /* Count the object as an unacked update. */
2388 state
-> cur_unacked_updates
++;
2390 return ISC_R_SUCCESS
;
2393 /* Queue an update for a lease. Always returns 1 at this point - it's
2394 not an error for this to be called on a lease for which there's no
2397 int dhcp_failover_queue_update (struct lease
*lease
, int immediate
)
2399 dhcp_failover_state_t
*state
;
2401 if (!lease
-> pool
||
2402 !lease
-> pool
-> failover_peer
)
2405 /* If it's already on the update queue, leave it there. */
2406 if (lease
-> flags
& ON_UPDATE_QUEUE
)
2409 /* Get the failover state structure for this lease. */
2410 state
= lease
-> pool
-> failover_peer
;
2412 /* If it's on the ack queue, take it off. */
2413 if (lease
-> flags
& ON_ACK_QUEUE
)
2414 dhcp_failover_ack_queue_remove (state
, lease
);
2416 if (state
-> update_queue_head
) {
2417 lease_reference (&state
-> update_queue_tail
-> next_pending
,
2419 lease_dereference (&state
-> update_queue_tail
, MDL
);
2421 lease_reference (&state
-> update_queue_head
, lease
, MDL
);
2423 #if defined (POINTER_DEBUG)
2424 if (lease
-> next_pending
) {
2425 log_error ("next pending on update queue lease.");
2426 #if defined (DEBUG_RC_HISTORY)
2427 dump_rc_history (lease
);
2432 lease_reference (&state
-> update_queue_tail
, lease
, MDL
);
2433 lease
-> flags
|= ON_UPDATE_QUEUE
;
2435 dhcp_failover_send_updates (state
);
2439 int dhcp_failover_send_acks (dhcp_failover_state_t
*state
)
2441 failover_message_t
*msg
= (failover_message_t
*)0;
2443 /* Must commit all leases prior to acking them. */
2444 if (!commit_leases ())
2447 while (state
-> toack_queue_head
) {
2448 failover_message_reference
2449 (&msg
, state
-> toack_queue_head
, MDL
);
2450 failover_message_dereference
2451 (&state
-> toack_queue_head
, MDL
);
2453 failover_message_reference
2454 (&state
-> toack_queue_head
, msg
-> next
, MDL
);
2457 dhcp_failover_send_bind_ack (state
, msg
, 0, (const char *)0);
2459 failover_message_dereference (&msg
, MDL
);
2462 if (state
-> toack_queue_tail
)
2463 failover_message_dereference (&state
-> toack_queue_tail
, MDL
);
2464 state
-> pending_acks
= 0;
2469 void dhcp_failover_toack_queue_timeout (void *vs
)
2471 dhcp_failover_state_t
*state
= vs
;
2473 #if defined (DEBUG_FAILOVER_TIMING)
2474 log_info ("dhcp_failover_toack_queue_timeout");
2477 dhcp_failover_send_acks (state
);
2480 /* Queue an ack for a message. There is currently no way to queue a
2481 negative ack -- these need to be sent directly. */
2483 int dhcp_failover_queue_ack (dhcp_failover_state_t
*state
,
2484 failover_message_t
*msg
)
2486 if (state
-> toack_queue_head
) {
2487 failover_message_reference
2488 (&state
-> toack_queue_tail
-> next
, msg
, MDL
);
2489 failover_message_dereference (&state
-> toack_queue_tail
, MDL
);
2491 failover_message_reference (&state
-> toack_queue_head
,
2494 failover_message_reference (&state
-> toack_queue_tail
, msg
, MDL
);
2496 state
-> pending_acks
++;
2498 /* Flush the toack queue whenever we exceed half the number of
2499 allowed unacked updates. */
2500 if (state
-> pending_acks
>= state
-> partner
.max_flying_updates
/ 2) {
2501 dhcp_failover_send_acks (state
);
2504 /* Schedule a timeout to flush the ack queue. */
2505 if (state
-> pending_acks
> 0) {
2506 #if defined (DEBUG_FAILOVER_TIMING)
2507 log_info ("add_timeout +2 %s",
2508 "dhcp_failover_toack_queue_timeout");
2510 add_timeout (cur_time
+ 2,
2511 dhcp_failover_toack_queue_timeout
, state
,
2512 (tvref_t
)dhcp_failover_state_reference
,
2513 (tvunref_t
)dhcp_failover_state_dereference
);
2519 void dhcp_failover_ack_queue_remove (dhcp_failover_state_t
*state
,
2520 struct lease
*lease
)
2524 if (!(lease
-> flags
& ON_ACK_QUEUE
))
2527 if (state
-> ack_queue_head
== lease
) {
2528 lease_dereference (&state
-> ack_queue_head
, MDL
);
2529 if (lease
-> next_pending
) {
2530 lease_reference (&state
-> ack_queue_head
,
2531 lease
-> next_pending
, MDL
);
2532 lease_dereference (&lease
-> next_pending
, MDL
);
2534 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2537 for (lp
= state
-> ack_queue_head
;
2538 lp
&& lp
-> next_pending
!= lease
;
2539 lp
= lp
-> next_pending
)
2545 lease_dereference (&lp
-> next_pending
, MDL
);
2546 if (lease
-> next_pending
) {
2547 lease_reference (&lp
-> next_pending
,
2548 lease
-> next_pending
, MDL
);
2549 lease_dereference (&lease
-> next_pending
, MDL
);
2551 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2552 if (lp
-> next_pending
) {
2553 log_error ("state -> ack_queue_tail");
2556 lease_reference (&state
-> ack_queue_tail
, lp
, MDL
);
2560 lease
-> flags
&= ~ON_ACK_QUEUE
;
2561 state
-> cur_unacked_updates
--;
2564 * When updating leases as a result of an ack, we defer the commit
2565 * for performance reasons. When there are no more acks pending,
2568 if (state
-> cur_unacked_updates
== 0) {
2573 isc_result_t
dhcp_failover_state_set_value (omapi_object_t
*h
,
2575 omapi_data_string_t
*name
,
2576 omapi_typed_data_t
*value
)
2578 isc_result_t status
;
2580 if (h
-> type
!= dhcp_type_failover_state
)
2581 return ISC_R_INVALIDARG
;
2583 /* This list of successful returns is completely wrong, but the
2584 fastest way to make dhcpctl do something vaguely sane when
2585 you try to change the local state. */
2587 if (!omapi_ds_strcmp (name
, "name")) {
2588 return ISC_R_SUCCESS
;
2589 } else if (!omapi_ds_strcmp (name
, "partner-address")) {
2590 return ISC_R_SUCCESS
;
2591 } else if (!omapi_ds_strcmp (name
, "local-address")) {
2592 return ISC_R_SUCCESS
;
2593 } else if (!omapi_ds_strcmp (name
, "partner-port")) {
2594 return ISC_R_SUCCESS
;
2595 } else if (!omapi_ds_strcmp (name
, "local-port")) {
2596 return ISC_R_SUCCESS
;
2597 } else if (!omapi_ds_strcmp (name
, "max-outstanding-updates")) {
2598 return ISC_R_SUCCESS
;
2599 } else if (!omapi_ds_strcmp (name
, "mclt")) {
2600 return ISC_R_SUCCESS
;
2601 } else if (!omapi_ds_strcmp (name
, "load-balance-max-secs")) {
2602 return ISC_R_SUCCESS
;
2603 } else if (!omapi_ds_strcmp (name
, "load-balance-hba")) {
2604 return ISC_R_SUCCESS
;
2605 } else if (!omapi_ds_strcmp (name
, "partner-state")) {
2606 return ISC_R_SUCCESS
;
2607 } else if (!omapi_ds_strcmp (name
, "local-state")) {
2609 status
= omapi_get_int_value (&l
, value
);
2610 if (status
!= ISC_R_SUCCESS
)
2612 return dhcp_failover_set_state ((dhcp_failover_state_t
*)h
, l
);
2613 } else if (!omapi_ds_strcmp (name
, "partner-stos")) {
2614 return ISC_R_SUCCESS
;
2615 } else if (!omapi_ds_strcmp (name
, "local-stos")) {
2616 return ISC_R_SUCCESS
;
2617 } else if (!omapi_ds_strcmp (name
, "hierarchy")) {
2618 return ISC_R_SUCCESS
;
2619 } else if (!omapi_ds_strcmp (name
, "last-packet-sent")) {
2620 return ISC_R_SUCCESS
;
2621 } else if (!omapi_ds_strcmp (name
, "last-timestamp-received")) {
2622 return ISC_R_SUCCESS
;
2623 } else if (!omapi_ds_strcmp (name
, "skew")) {
2624 return ISC_R_SUCCESS
;
2625 } else if (!omapi_ds_strcmp (name
, "max-response-delay")) {
2626 return ISC_R_SUCCESS
;
2627 } else if (!omapi_ds_strcmp (name
, "cur-unacked-updates")) {
2628 return ISC_R_SUCCESS
;
2631 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
2632 return (*(h
-> inner
-> type
-> set_value
))
2633 (h
-> inner
, id
, name
, value
);
2634 return ISC_R_NOTFOUND
;
2637 void dhcp_failover_keepalive (void *vs
)
2639 dhcp_failover_state_t
*state
= vs
;
2642 void dhcp_failover_reconnect (void *vs
)
2644 dhcp_failover_state_t
*state
= vs
;
2645 isc_result_t status
;
2647 #if defined (DEBUG_FAILOVER_TIMING)
2648 log_info ("dhcp_failover_reconnect");
2650 /* If we already connected the other way, let the connection
2651 recovery code initiate any retry that may be required. */
2652 if (state
-> link_to_peer
)
2655 status
= dhcp_failover_link_initiate ((omapi_object_t
*)state
);
2656 if (status
!= ISC_R_SUCCESS
&& status
!= ISC_R_INCOMPLETE
) {
2657 log_info ("failover peer %s: %s", state
-> name
,
2658 isc_result_totext (status
));
2659 #if defined (DEBUG_FAILOVER_TIMING)
2660 log_info ("add_timeout +90 %s",
2661 "dhcp_failover_listener_restart");
2663 add_timeout (cur_time
+ 90,
2664 dhcp_failover_listener_restart
, state
,
2665 (tvref_t
)dhcp_failover_state_reference
,
2666 (tvunref_t
)dhcp_failover_state_dereference
);
2670 void dhcp_failover_startup_timeout (void *vs
)
2672 dhcp_failover_state_t
*state
= vs
;
2673 isc_result_t status
;
2675 #if defined (DEBUG_FAILOVER_TIMING)
2676 log_info ("dhcp_failover_startup_timeout");
2679 dhcp_failover_state_transition (state
, "disconnect");
2682 void dhcp_failover_link_startup_timeout (void *vl
)
2684 dhcp_failover_link_t
*link
= vl
;
2685 isc_result_t status
;
2688 for (p
= (omapi_object_t
*)link
; p
-> inner
; p
= p
-> inner
)
2690 for (; p
; p
= p
-> outer
)
2691 if (p
-> type
== omapi_type_connection
)
2694 log_info ("failover: link startup timeout");
2695 omapi_disconnect (p
, 1);
2699 void dhcp_failover_listener_restart (void *vs
)
2701 dhcp_failover_state_t
*state
= vs
;
2702 isc_result_t status
;
2704 #if defined (DEBUG_FAILOVER_TIMING)
2705 log_info ("dhcp_failover_listener_restart");
2708 status
= dhcp_failover_listen ((omapi_object_t
*)state
);
2709 if (status
!= ISC_R_SUCCESS
) {
2710 log_info ("failover peer %s: %s", state
-> name
,
2711 isc_result_totext (status
));
2712 #if defined (DEBUG_FAILOVER_TIMING)
2713 log_info ("add_timeout +90 %s",
2714 "dhcp_failover_listener_restart");
2716 add_timeout (cur_time
+ 90,
2717 dhcp_failover_listener_restart
, state
,
2718 (tvref_t
)dhcp_failover_state_reference
,
2719 (tvunref_t
)dhcp_failover_state_dereference
);
2723 isc_result_t
dhcp_failover_state_get_value (omapi_object_t
*h
,
2725 omapi_data_string_t
*name
,
2726 omapi_value_t
**value
)
2728 dhcp_failover_state_t
*s
;
2729 struct option_cache
*oc
;
2730 struct data_string ds
;
2731 isc_result_t status
;
2733 if (h
-> type
!= dhcp_type_failover_state
)
2734 return ISC_R_INVALIDARG
;
2735 s
= (dhcp_failover_state_t
*)h
;
2737 if (!omapi_ds_strcmp (name
, "name")) {
2739 return omapi_make_string_value (value
,
2740 name
, s
-> name
, MDL
);
2741 return ISC_R_NOTFOUND
;
2742 } else if (!omapi_ds_strcmp (name
, "partner-address")) {
2743 oc
= s
-> partner
.address
;
2745 memset (&ds
, 0, sizeof ds
);
2746 if (!evaluate_option_cache (&ds
, (struct packet
*)0,
2748 (struct client_state
*)0,
2749 (struct option_state
*)0,
2750 (struct option_state
*)0,
2751 &global_scope
, oc
, MDL
)) {
2752 return ISC_R_NOTFOUND
;
2754 status
= omapi_make_const_value (value
,
2755 name
, ds
.data
, ds
.len
, MDL
);
2756 /* Disgusting kludge: */
2757 if (oc
== s
-> me
.address
&& !s
-> server_identifier
.len
)
2758 data_string_copy (&s
-> server_identifier
, &ds
, MDL
);
2759 data_string_forget (&ds
, MDL
);
2761 } else if (!omapi_ds_strcmp (name
, "local-address")) {
2762 oc
= s
-> me
.address
;
2764 } else if (!omapi_ds_strcmp (name
, "partner-port")) {
2765 return omapi_make_int_value (value
, name
,
2766 s
-> partner
.port
, MDL
);
2767 } else if (!omapi_ds_strcmp (name
, "local-port")) {
2768 return omapi_make_int_value (value
,
2769 name
, s
-> me
.port
, MDL
);
2770 } else if (!omapi_ds_strcmp (name
, "max-outstanding-updates")) {
2771 return omapi_make_uint_value (value
, name
,
2772 s
-> me
.max_flying_updates
,
2774 } else if (!omapi_ds_strcmp (name
, "mclt")) {
2775 return omapi_make_uint_value (value
, name
, s
-> mclt
, MDL
);
2776 } else if (!omapi_ds_strcmp (name
, "load-balance-max-secs")) {
2777 return omapi_make_int_value (value
, name
,
2778 s
-> load_balance_max_secs
, MDL
);
2779 } else if (!omapi_ds_strcmp (name
, "load-balance-hba")) {
2781 return omapi_make_const_value (value
, name
,
2783 return ISC_R_NOTFOUND
;
2784 } else if (!omapi_ds_strcmp (name
, "partner-state")) {
2785 return omapi_make_uint_value (value
, name
,
2786 s
-> partner
.state
, MDL
);
2787 } else if (!omapi_ds_strcmp (name
, "local-state")) {
2788 return omapi_make_uint_value (value
, name
,
2789 s
-> me
.state
, MDL
);
2790 } else if (!omapi_ds_strcmp (name
, "partner-stos")) {
2791 return omapi_make_int_value (value
, name
,
2792 s
-> partner
.stos
, MDL
);
2793 } else if (!omapi_ds_strcmp (name
, "local-stos")) {
2794 return omapi_make_int_value (value
, name
,
2796 } else if (!omapi_ds_strcmp (name
, "hierarchy")) {
2797 return omapi_make_uint_value (value
, name
, s
-> i_am
, MDL
);
2798 } else if (!omapi_ds_strcmp (name
, "last-packet-sent")) {
2799 return omapi_make_int_value (value
, name
,
2800 s
-> last_packet_sent
, MDL
);
2801 } else if (!omapi_ds_strcmp (name
, "last-timestamp-received")) {
2802 return omapi_make_int_value (value
, name
,
2803 s
-> last_timestamp_received
,
2805 } else if (!omapi_ds_strcmp (name
, "skew")) {
2806 return omapi_make_int_value (value
, name
, s
-> skew
, MDL
);
2807 } else if (!omapi_ds_strcmp (name
, "max-response-delay")) {
2808 return omapi_make_uint_value (value
, name
,
2809 s
-> me
.max_response_delay
,
2811 } else if (!omapi_ds_strcmp (name
, "cur-unacked-updates")) {
2812 return omapi_make_int_value (value
, name
,
2813 s
-> cur_unacked_updates
, MDL
);
2816 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
2817 return (*(h
-> inner
-> type
-> get_value
))
2818 (h
-> inner
, id
, name
, value
);
2819 return ISC_R_NOTFOUND
;
2822 isc_result_t
dhcp_failover_state_destroy (omapi_object_t
*h
,
2823 const char *file
, int line
)
2825 dhcp_failover_state_t
*s
;
2827 if (h
-> type
!= dhcp_type_failover_state
)
2828 return ISC_R_INVALIDARG
;
2829 s
= (dhcp_failover_state_t
*)h
;
2831 if (s
-> link_to_peer
)
2832 dhcp_failover_link_dereference (&s
-> link_to_peer
, file
, line
);
2834 dfree (s
-> name
, MDL
);
2835 s
-> name
= (char *)0;
2837 if (s
-> partner
.address
)
2838 option_cache_dereference (&s
-> partner
.address
, file
, line
);
2839 if (s
-> me
.address
)
2840 option_cache_dereference (&s
-> me
.address
, file
, line
);
2842 dfree (s
-> hba
, file
, line
);
2843 s
-> hba
= (u_int8_t
*)0;
2845 if (s
-> update_queue_head
)
2846 lease_dereference (&s
-> update_queue_head
, file
, line
);
2847 if (s
-> update_queue_tail
)
2848 lease_dereference (&s
-> update_queue_tail
, file
, line
);
2849 if (s
-> ack_queue_head
)
2850 lease_dereference (&s
-> ack_queue_head
, file
, line
);
2851 if (s
-> ack_queue_tail
)
2852 lease_dereference (&s
-> ack_queue_tail
, file
, line
);
2853 if (s
-> send_update_done
)
2854 lease_dereference (&s
-> send_update_done
, file
, line
);
2855 if (s
-> toack_queue_head
)
2856 failover_message_dereference (&s
-> toack_queue_head
,
2858 if (s
-> toack_queue_tail
)
2859 failover_message_dereference (&s
-> toack_queue_tail
,
2861 return ISC_R_SUCCESS
;
2864 /* Write all the published values associated with the object through the
2865 specified connection. */
2867 isc_result_t
dhcp_failover_state_stuff (omapi_object_t
*c
,
2871 dhcp_failover_state_t
*s
;
2872 omapi_connection_object_t
*conn
;
2873 isc_result_t status
;
2875 if (c
-> type
!= omapi_type_connection
)
2876 return ISC_R_INVALIDARG
;
2877 conn
= (omapi_connection_object_t
*)c
;
2879 if (h
-> type
!= dhcp_type_failover_state
)
2880 return ISC_R_INVALIDARG
;
2881 s
= (dhcp_failover_state_t
*)h
;
2883 status
= omapi_connection_put_name (c
, "name");
2884 if (status
!= ISC_R_SUCCESS
)
2886 status
= omapi_connection_put_string (c
, s
-> name
);
2887 if (status
!= ISC_R_SUCCESS
)
2890 status
= omapi_connection_put_name (c
, "partner-address");
2891 if (status
!= ISC_R_SUCCESS
)
2893 status
= omapi_connection_put_uint32 (c
, sizeof s
-> partner
.address
);
2894 if (status
!= ISC_R_SUCCESS
)
2896 status
= omapi_connection_copyin (c
, (u_int8_t
*)&s
-> partner
.address
,
2897 sizeof s
-> partner
.address
);
2898 if (status
!= ISC_R_SUCCESS
)
2901 status
= omapi_connection_put_name (c
, "partner-port");
2902 if (status
!= ISC_R_SUCCESS
)
2904 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
2905 if (status
!= ISC_R_SUCCESS
)
2907 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> partner
.port
);
2908 if (status
!= ISC_R_SUCCESS
)
2911 status
= omapi_connection_put_name (c
, "local-address");
2912 if (status
!= ISC_R_SUCCESS
)
2914 status
= omapi_connection_put_uint32 (c
, sizeof s
-> me
.address
);
2915 if (status
!= ISC_R_SUCCESS
)
2917 status
= omapi_connection_copyin (c
, (u_int8_t
*)&s
-> me
.address
,
2918 sizeof s
-> me
.address
);
2919 if (status
!= ISC_R_SUCCESS
)
2922 status
= omapi_connection_put_name (c
, "local-port");
2923 if (status
!= ISC_R_SUCCESS
)
2925 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
2926 if (status
!= ISC_R_SUCCESS
)
2928 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> me
.port
);
2929 if (status
!= ISC_R_SUCCESS
)
2932 status
= omapi_connection_put_name (c
, "max-outstanding-updates");
2933 if (status
!= ISC_R_SUCCESS
)
2935 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
2936 if (status
!= ISC_R_SUCCESS
)
2938 status
= omapi_connection_put_uint32 (c
,
2939 s
-> me
.max_flying_updates
);
2940 if (status
!= ISC_R_SUCCESS
)
2943 status
= omapi_connection_put_name (c
, "mclt");
2944 if (status
!= ISC_R_SUCCESS
)
2946 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
2947 if (status
!= ISC_R_SUCCESS
)
2949 status
= omapi_connection_put_uint32 (c
, s
-> mclt
);
2950 if (status
!= ISC_R_SUCCESS
)
2953 status
= omapi_connection_put_name (c
, "load-balance-max-secs");
2954 if (status
!= ISC_R_SUCCESS
)
2956 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
2957 if (status
!= ISC_R_SUCCESS
)
2959 status
= (omapi_connection_put_uint32
2960 (c
, (u_int32_t
)s
-> load_balance_max_secs
));
2961 if (status
!= ISC_R_SUCCESS
)
2966 status
= omapi_connection_put_name (c
, "load-balance-hba");
2967 if (status
!= ISC_R_SUCCESS
)
2969 status
= omapi_connection_put_uint32 (c
, 32);
2970 if (status
!= ISC_R_SUCCESS
)
2972 status
= omapi_connection_copyin (c
, s
-> hba
, 32);
2973 if (status
!= ISC_R_SUCCESS
)
2977 status
= omapi_connection_put_name (c
, "partner-state");
2978 if (status
!= ISC_R_SUCCESS
)
2980 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
2981 if (status
!= ISC_R_SUCCESS
)
2983 status
= omapi_connection_put_uint32 (c
, s
-> partner
.state
);
2984 if (status
!= ISC_R_SUCCESS
)
2987 status
= omapi_connection_put_name (c
, "local-state");
2988 if (status
!= ISC_R_SUCCESS
)
2990 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
2991 if (status
!= ISC_R_SUCCESS
)
2993 status
= omapi_connection_put_uint32 (c
, s
-> me
.state
);
2994 if (status
!= ISC_R_SUCCESS
)
2997 status
= omapi_connection_put_name (c
, "partner-stos");
2998 if (status
!= ISC_R_SUCCESS
)
3000 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3001 if (status
!= ISC_R_SUCCESS
)
3003 status
= omapi_connection_put_uint32 (c
,
3004 (u_int32_t
)s
-> partner
.stos
);
3005 if (status
!= ISC_R_SUCCESS
)
3008 status
= omapi_connection_put_name (c
, "local-stos");
3009 if (status
!= ISC_R_SUCCESS
)
3011 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3012 if (status
!= ISC_R_SUCCESS
)
3014 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> me
.stos
);
3015 if (status
!= ISC_R_SUCCESS
)
3018 status
= omapi_connection_put_name (c
, "hierarchy");
3019 if (status
!= ISC_R_SUCCESS
)
3021 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3022 if (status
!= ISC_R_SUCCESS
)
3024 status
= omapi_connection_put_uint32 (c
, s
-> i_am
);
3025 if (status
!= ISC_R_SUCCESS
)
3028 status
= omapi_connection_put_name (c
, "last-packet-sent");
3029 if (status
!= ISC_R_SUCCESS
)
3031 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3032 if (status
!= ISC_R_SUCCESS
)
3034 status
= (omapi_connection_put_uint32
3035 (c
, (u_int32_t
)s
-> last_packet_sent
));
3036 if (status
!= ISC_R_SUCCESS
)
3039 status
= omapi_connection_put_name (c
, "last-timestamp-received");
3040 if (status
!= ISC_R_SUCCESS
)
3042 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3043 if (status
!= ISC_R_SUCCESS
)
3045 status
= (omapi_connection_put_uint32
3046 (c
, (u_int32_t
)s
-> last_timestamp_received
));
3047 if (status
!= ISC_R_SUCCESS
)
3050 status
= omapi_connection_put_name (c
, "skew");
3051 if (status
!= ISC_R_SUCCESS
)
3053 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3054 if (status
!= ISC_R_SUCCESS
)
3056 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> skew
);
3057 if (status
!= ISC_R_SUCCESS
)
3060 status
= omapi_connection_put_name (c
, "max-response-delay");
3061 if (status
!= ISC_R_SUCCESS
)
3063 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3064 if (status
!= ISC_R_SUCCESS
)
3066 status
= (omapi_connection_put_uint32
3067 (c
, (u_int32_t
)s
-> me
.max_response_delay
));
3068 if (status
!= ISC_R_SUCCESS
)
3071 status
= omapi_connection_put_name (c
, "cur-unacked-updates");
3072 if (status
!= ISC_R_SUCCESS
)
3074 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3075 if (status
!= ISC_R_SUCCESS
)
3077 status
= (omapi_connection_put_uint32
3078 (c
, (u_int32_t
)s
-> cur_unacked_updates
));
3079 if (status
!= ISC_R_SUCCESS
)
3082 if (h
-> inner
&& h
-> inner
-> type
-> stuff_values
)
3083 return (*(h
-> inner
-> type
-> stuff_values
)) (c
, id
,
3085 return ISC_R_SUCCESS
;
3088 isc_result_t
dhcp_failover_state_lookup (omapi_object_t
**sp
,
3090 omapi_object_t
*ref
)
3092 omapi_value_t
*tv
= (omapi_value_t
*)0;
3093 isc_result_t status
;
3094 dhcp_failover_state_t
*s
;
3097 return ISC_R_NOKEYS
;
3099 /* First see if we were sent a handle. */
3100 status
= omapi_get_value_str (ref
, id
, "handle", &tv
);
3101 if (status
== ISC_R_SUCCESS
) {
3102 status
= omapi_handle_td_lookup (sp
, tv
-> value
);
3104 omapi_value_dereference (&tv
, MDL
);
3105 if (status
!= ISC_R_SUCCESS
)
3108 /* Don't return the object if the type is wrong. */
3109 if ((*sp
) -> type
!= dhcp_type_failover_state
) {
3110 omapi_object_dereference (sp
, MDL
);
3111 return ISC_R_INVALIDARG
;
3115 /* Look the failover state up by peer name. */
3116 status
= omapi_get_value_str (ref
, id
, "name", &tv
);
3117 if (status
== ISC_R_SUCCESS
) {
3118 for (s
= failover_states
; s
; s
= s
-> next
) {
3119 unsigned l
= strlen (s
-> name
);
3120 if (l
== tv
-> value
-> u
.buffer
.len
&&
3122 tv
-> value
-> u
.buffer
.value
, l
))
3125 omapi_value_dereference (&tv
, MDL
);
3127 /* If we already have a lease, and it's not the same one,
3128 then the query was invalid. */
3129 if (*sp
&& *sp
!= (omapi_object_t
*)s
) {
3130 omapi_object_dereference (sp
, MDL
);
3131 return ISC_R_KEYCONFLICT
;
3134 omapi_object_dereference (sp
, MDL
);
3135 return ISC_R_NOTFOUND
;
3137 /* XXX fix so that hash lookup itself creates
3138 XXX the reference. */
3139 omapi_object_reference (sp
, (omapi_object_t
*)s
, MDL
);
3142 /* If we get to here without finding a lease, no valid key was
3145 return ISC_R_NOKEYS
;
3146 return ISC_R_SUCCESS
;
3149 isc_result_t
dhcp_failover_state_create (omapi_object_t
**sp
,
3152 return ISC_R_NOTIMPLEMENTED
;
3155 isc_result_t
dhcp_failover_state_remove (omapi_object_t
*sp
,
3158 return ISC_R_NOTIMPLEMENTED
;
3161 int dhcp_failover_state_match (dhcp_failover_state_t
*state
,
3162 u_int8_t
*addr
, unsigned addrlen
)
3164 struct option_cache
*oc
;
3165 struct data_string ds
;
3168 memset (&ds
, 0, sizeof ds
);
3169 if (evaluate_option_cache (&ds
, (struct packet
*)0,
3171 (struct client_state
*)0,
3172 (struct option_state
*)0,
3173 (struct option_state
*)0,
3175 state
-> partner
.address
, MDL
)) {
3176 for (i
= 0; i
+ addrlen
- 1 < ds
.len
; i
+= addrlen
) {
3177 if (!memcmp (&ds
.data
[i
],
3179 data_string_forget (&ds
, MDL
);
3183 data_string_forget (&ds
, MDL
);
3188 const char *dhcp_failover_reject_reason_print (int reason
)
3191 case FTR_ILLEGAL_IP_ADDR
:
3192 return "Illegal IP address (not part of any address pool).";
3194 case FTR_FATAL_CONFLICT
:
3195 return "Fatal conflict exists: address in use by other client.";
3197 case FTR_MISSING_BINDINFO
:
3198 return "Missing binding information.";
3200 case FTR_TIMEMISMATCH
:
3201 return "Connection rejected, time mismatch too great.";
3203 case FTR_INVALID_MCLT
:
3204 return "Connection rejected, invalid MCLT.";
3206 case FTR_MISC_REJECT
:
3207 return "Connection rejected, unknown reason.";
3209 case FTR_DUP_CONNECTION
:
3210 return "Connection rejected, duplicate connection.";
3212 case FTR_INVALID_PARTNER
:
3213 return "Connection rejected, invalid failover partner.";
3215 case FTR_TLS_UNSUPPORTED
:
3216 return "TLS not supported.";
3218 case FTR_TLS_UNCONFIGURED
:
3219 return "TLS supported but not configured.";
3221 case FTR_TLS_REQUIRED
:
3222 return "TLS required but not supported by partner.";
3224 case FTR_DIGEST_UNSUPPORTED
:
3225 return "Message digest not supported.";
3227 case FTR_DIGEST_UNCONFIGURED
:
3228 return "Message digest not configured.";
3230 case FTR_VERSION_MISMATCH
:
3231 return "Protocol version mismatch.";
3233 case FTR_MISSING_BIND_INFO
:
3234 return "Missing binding information.";
3236 case FTR_OUTDATED_BIND_INFO
:
3237 return "Outdated binding information.";
3239 case FTR_LESS_CRIT_BIND_INFO
:
3240 return "Less critical binding information.";
3242 case FTR_NO_TRAFFIC
:
3243 return "No traffic within sufficient time.";
3245 case FTR_HBA_CONFLICT
:
3246 return "Hash bucket assignment conflict.";
3250 return "Unknown: Error occurred but does not match any reason code.";
3254 const char *dhcp_failover_state_name_print (enum failover_state state
)
3259 return "unknown-state";
3262 return "partner-down";
3267 case communications_interrupted
:
3268 return "communications-interrupted";
3270 case resolution_interrupted
:
3271 return "resolution-interrupted";
3273 case potential_conflict
:
3274 return "potential-conflict";
3280 return "recover-done";
3283 return "recover-wait";
3296 const char *dhcp_failover_message_name (unsigned type
)
3300 return "pool-request";
3303 return "pool-response";
3306 return "bind-update";
3314 case FTM_CONNECTACK
:
3315 return "connect-ack";
3318 return "update-request";
3321 return "update-done";
3324 return "update-request-all";
3332 case FTM_DISCONNECT
:
3333 return "disconnect";
3336 return "<unknown message type>";
3340 const char *dhcp_failover_option_name (unsigned type
)
3343 case FTO_BINDING_STATUS
:
3344 return "binding-status";
3346 case FTO_ASSIGNED_IP_ADDRESS
:
3347 return "assigned-ip-address";
3349 case FTO_SERVER_ADDR
:
3350 return "server-addr";
3352 case FTO_ADDRESSES_TRANSFERRED
:
3353 return "addresses-transferred";
3355 case FTO_CLIENT_IDENTIFIER
:
3356 return "client-identifier";
3364 case FTO_REJECT_REASON
:
3365 return "reject-reason";
3373 case FTO_VENDOR_CLASS
:
3374 return "vendor-class";
3376 case FTO_LEASE_EXPIRY
:
3377 return "lease-expiry";
3379 case FTO_POTENTIAL_EXPIRY
:
3380 return "potential-expiry";
3382 case FTO_GRACE_EXPIRY
:
3383 return "grace-expiry";
3391 case FTO_SERVER_STATE
:
3392 return "server-state";
3394 case FTO_SERVER_FLAGS
:
3395 return "server-flags";
3397 case FTO_VENDOR_OPTIONS
:
3398 return "vendor-options";
3400 case FTO_MAX_UNACKED
:
3401 return "max-unacked";
3403 case FTO_RECEIVE_TIMER
:
3404 return "receive-timer";
3409 case FTO_MESSAGE_DIGEST
:
3410 return "message-digest";
3412 case FTO_PROTOCOL_VERSION
:
3413 return "protocol-version";
3415 case FTO_TLS_REQUEST
:
3416 return "tls-request";
3421 case FTO_REQUEST_OPTIONS
:
3422 return "request-options";
3424 case FTO_REPLY_OPTIONS
:
3425 return "reply-options";
3428 return "<unknown option>";
3432 failover_option_t
*dhcp_failover_option_printf (unsigned code
,
3436 const char *fmt
, ...)
3441 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3442 * It is unclear what the effects of truncation here are, or
3443 * how that condition should be handled. It seems that this
3444 * function is used for formatting messages in the failover
3445 * command channel. For now the safest thing is for
3446 * overflow-truncation to cause a fatal log.
3449 if (vsnprintf (tbuf
, sizeof tbuf
, fmt
, va
) >= sizeof tbuf
)
3450 log_fatal ("%s: vsnprintf would truncate",
3451 "dhcp_failover_make_option");
3454 return dhcp_failover_make_option (code
, obuf
, obufix
, obufmax
,
3455 strlen (tbuf
), tbuf
);
3458 failover_option_t
*dhcp_failover_make_option (unsigned code
,
3459 char *obuf
, unsigned *obufix
,
3460 unsigned obufmax
, ...)
3463 struct failover_option_info
*info
;
3465 unsigned size
, count
;
3471 #if defined (DEBUG_FAILOVER_MESSAGES)
3475 /* Note that the failover_option structure is used differently on
3476 input than on output - on input, count is an element count, and
3477 on output it's the number of bytes total in the option, including
3478 the option code and option length. */
3479 failover_option_t option
, *op
;
3482 /* Bogus option code? */
3483 if (code
< 1 || code
> FTO_MAX
|| ft_options
[code
].type
== FT_UNDEF
) {
3484 return &null_failover_option
;
3486 info
= &ft_options
[code
];
3488 va_start (va
, obufmax
);
3490 /* Get the number of elements and the size of the buffer we need
3492 if (info
-> type
== FT_DDNS
|| info
-> type
== FT_DDNS1
) {
3493 count
= info
-> type
== FT_DDNS
? 1 : 2;
3494 size
= va_arg (va
, int) + count
;
3496 /* Find out how many items in this list. */
3497 if (info
-> num_present
)
3498 count
= info
-> num_present
;
3500 count
= va_arg (va
, int);
3502 /* Figure out size. */
3503 switch (info
-> type
) {
3510 case FT_TEXT_OR_BYTES
:
3512 txt
= va_arg (va
, char *);
3517 ilen
= va_arg (va
, unsigned);
3518 size
= count
* ilen
;
3530 /* shouldn't get here. */
3531 log_fatal ("bogus type in failover_make_option: %d",
3533 return &null_failover_option
;
3539 /* Allocate a buffer for the option. */
3540 option
.count
= size
;
3541 option
.data
= dmalloc (option
.count
, MDL
);
3544 return &null_failover_option
;
3547 /* Put in the option code and option length. */
3548 putUShort (option
.data
, code
);
3549 putUShort (&option
.data
[2], size
- 4);
3551 #if defined (DEBUG_FAILOVER_MESSAGES)
3552 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3553 * It is unclear what the effects of truncation here are, or
3554 * how that condition should be handled. It seems that this
3555 * message may be sent over the failover command channel.
3556 * For now the safest thing is for overflow-truncation to cause
3559 if (snprintf (tbuf
, sizeof tbuf
, " (%s<%d>", info
-> name
,
3560 option
.count
) >= sizeof tbuf
)
3561 log_fatal ("dhcp_failover_make_option: tbuf overflow");
3562 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3565 /* Now put in the data. */
3566 switch (info
-> type
) {
3568 for (i
= 0; i
< count
; i
++) {
3569 val
= va_arg (va
, unsigned);
3570 #if defined (DEBUG_FAILOVER_MESSAGES)
3571 /* %Audit% Cannot exceed 24 bytes. %2004.06.17,Safe% */
3572 sprintf (tbuf
, " %d", val
);
3573 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3575 option
.data
[i
+ 4] = val
;
3580 for (i
= 0; i
< count
; i
++) {
3581 iaddr
= va_arg (va
, u_int8_t
*);
3583 dfree (option
.data
, MDL
);
3584 log_error ("IP addrlen=%d, should be 4.",
3587 return &null_failover_option
;
3590 #if defined (DEBUG_FAILOVER_MESSAGES)
3591 /*%Audit% Cannot exceed 17 bytes. %2004.06.17,Safe%*/
3592 sprintf (tbuf
, " %u.%u.%u.%u",
3593 iaddr
[0], iaddr
[1], iaddr
[2], iaddr
[3]);
3594 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3596 memcpy (&option
.data
[4 + i
* ilen
], iaddr
, ilen
);
3601 for (i
= 0; i
< count
; i
++) {
3602 val
= va_arg (va
, unsigned);
3603 #if defined (DEBUG_FAILOVER_MESSAGES)
3604 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3605 sprintf (tbuf
, " %d", val
);
3606 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3608 putULong (&option
.data
[4 + i
* 4], val
);
3614 bval
= va_arg (va
, u_int8_t
*);
3615 #if defined (DEBUG_FAILOVER_MESSAGES)
3616 for (i
= 0; i
< count
; i
++) {
3617 /* 23 bytes plus nul, safe. */
3618 sprintf (tbuf
, " %d", bval
[i
]);
3619 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3622 memcpy (&option
.data
[4], bval
, count
);
3625 /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
3626 terminated. Note that the caller should be careful not
3627 to provide a format and data that amount to more than 256
3628 bytes of data, since it will cause a fatal error. */
3629 case FT_TEXT_OR_BYTES
:
3631 #if defined (DEBUG_FAILOVER_MESSAGES)
3632 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3633 * It is unclear what the effects of truncation here are, or
3634 * how that condition should be handled. It seems that this
3635 * function is used for formatting messages in the failover
3636 * command channel. For now the safest thing is for
3637 * overflow-truncation to cause a fatal log.
3639 if (snprintf (tbuf
, sizeof tbuf
, "\"%s\"", txt
) >= sizeof tbuf
)
3640 log_fatal ("dhcp_failover_make_option: tbuf overflow");
3641 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3643 memcpy (&option
.data
[4], txt
, count
);
3648 option
.data
[4] = va_arg (va
, unsigned);
3650 option
.data
[5] = va_arg (va
, unsigned);
3651 bval
= va_arg (va
, u_int8_t
*);
3652 memcpy (&option
.data
[4 + count
], bval
, size
- count
- 4);
3653 #if defined (DEBUG_FAILOVER_MESSAGES)
3654 for (i
= 4; i
< size
; i
++) {
3655 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3656 sprintf (tbuf
, " %d", option
.data
[i
]);
3657 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3663 for (i
= 0; i
< count
; i
++) {
3664 val
= va_arg (va
, u_int32_t
);
3665 #if defined (DEBUG_FAILOVER_MESSAGES)
3666 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3667 sprintf (tbuf
, " %d", val
);
3668 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3670 putUShort (&option
.data
[4 + i
* 2], val
);
3679 #if defined DEBUG_FAILOVER_MESSAGES
3680 failover_print (obuf
, obufix
, obufmax
, ")");
3684 /* Now allocate a place to store what we just set up. */
3685 op
= dmalloc (sizeof (failover_option_t
), MDL
);
3687 dfree (option
.data
, MDL
);
3688 return &null_failover_option
;
3695 /* Send a failover message header. */
3697 isc_result_t
dhcp_failover_put_message (dhcp_failover_link_t
*link
,
3698 omapi_object_t
*connection
,
3706 failover_option_t
*option
;
3707 unsigned char *opbuf
;
3708 isc_result_t status
= ISC_R_SUCCESS
;
3711 /* Run through the argument list once to compute the length of
3712 the option portion of the message. */
3713 va_start (list
, msg_type
);
3714 while ((option
= va_arg (list
, failover_option_t
*))) {
3715 if (option
!= &skip_failover_option
)
3716 size
+= option
-> count
;
3717 if (option
== &null_failover_option
)
3722 /* Allocate an option buffer, unless we got an error. */
3723 if (!bad_option
&& size
) {
3724 opbuf
= dmalloc (size
, MDL
);
3726 status
= ISC_R_NOMEMORY
;
3728 opbuf
= (unsigned char *)0;
3730 va_start (list
, msg_type
);
3731 while ((option
= va_arg (list
, failover_option_t
*))) {
3732 if (option
== &skip_failover_option
)
3734 if (!bad_option
&& opbuf
)
3735 memcpy (&opbuf
[opix
],
3736 option
-> data
, option
-> count
);
3737 if (option
!= &null_failover_option
&&
3738 option
!= &skip_failover_option
) {
3739 opix
+= option
-> count
;
3740 dfree (option
-> data
, MDL
);
3741 dfree (option
, MDL
);
3746 return ISC_R_INVALIDARG
;
3748 /* Now send the message header. */
3750 /* Message length. */
3751 status
= omapi_connection_put_uint16 (connection
, size
+ 12);
3752 if (status
!= ISC_R_SUCCESS
)
3757 status
= omapi_connection_copyin (connection
, &cbuf
, 1);
3758 if (status
!= ISC_R_SUCCESS
)
3761 /* Payload offset. */
3763 status
= omapi_connection_copyin (connection
, &cbuf
, 1);
3764 if (status
!= ISC_R_SUCCESS
)
3768 status
= omapi_connection_put_uint32 (connection
, (u_int32_t
)cur_time
);
3769 if (status
!= ISC_R_SUCCESS
)
3772 /* Transaction ID. */
3773 status
= omapi_connection_put_uint32 (connection
, link
-> xid
++);
3774 if (status
!= ISC_R_SUCCESS
)
3780 status
= omapi_connection_copyin (connection
, opbuf
, size
);
3781 if (status
!= ISC_R_SUCCESS
)
3785 if (link
-> state_object
&&
3786 link
-> state_object
-> link_to_peer
== link
) {
3787 #if defined (DEBUG_FAILOVER_TIMING)
3788 log_info ("add_timeout +%d %s",
3789 (int)(link
-> state_object
->
3790 partner
.max_response_delay
) / 3,
3791 "dhcp_failover_send_contact");
3793 add_timeout (cur_time
+
3794 (int)(link
-> state_object
->
3795 partner
.max_response_delay
) / 3,
3796 dhcp_failover_send_contact
, link
-> state_object
,
3797 (tvref_t
)dhcp_failover_state_reference
,
3798 (tvunref_t
)dhcp_failover_state_dereference
);
3805 log_info ("dhcp_failover_put_message: something went wrong.");
3806 omapi_disconnect (connection
, 1);
3810 void dhcp_failover_timeout (void *vstate
)
3812 dhcp_failover_state_t
*state
= vstate
;
3813 dhcp_failover_link_t
*link
;
3814 isc_result_t status
;
3816 #if defined (DEBUG_FAILOVER_TIMING)
3817 log_info ("dhcp_failover_timeout");
3820 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
3822 link
= state
-> link_to_peer
;
3825 link
-> outer
-> type
!= omapi_type_connection
)
3828 log_error ("timeout waiting for failover peer %s", state
-> name
);
3830 /* If we haven't gotten a timely response, blow away the connection.
3831 This will cause the state to change automatically. */
3832 omapi_disconnect (link
-> outer
, 1);
3835 void dhcp_failover_send_contact (void *vstate
)
3837 dhcp_failover_state_t
*state
= vstate
;
3838 dhcp_failover_link_t
*link
;
3839 isc_result_t status
;
3841 #if defined (DEBUG_FAILOVER_MESSAGES)
3843 unsigned obufix
= 0;
3845 # define FMA obuf, &obufix, sizeof obuf
3846 failover_print (FMA
, "(contact");
3848 # define FMA (char *)0, (unsigned *)0, 0
3851 #if defined (DEBUG_FAILOVER_TIMING)
3852 log_info ("dhcp_failover_send_contact");
3855 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
3857 link
= state
-> link_to_peer
;
3860 link
-> outer
-> type
!= omapi_type_connection
)
3863 status
= (dhcp_failover_put_message
3864 (link
, link
-> outer
,
3866 (failover_option_t
*)0));
3868 #if defined (DEBUG_FAILOVER_MESSAGES)
3869 if (status
!= ISC_R_SUCCESS
)
3870 failover_print (FMA
, " (failed)");
3871 failover_print (FMA
, ")");
3873 log_debug ("%s", obuf
);
3879 isc_result_t
dhcp_failover_send_state (dhcp_failover_state_t
*state
)
3881 dhcp_failover_link_t
*link
;
3882 isc_result_t status
;
3884 #if defined (DEBUG_FAILOVER_MESSAGES)
3886 unsigned obufix
= 0;
3888 # define FMA obuf, &obufix, sizeof obuf
3889 failover_print (FMA
, "(state");
3891 # define FMA (char *)0, (unsigned *)0, 0
3894 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
3895 return ISC_R_INVALIDARG
;
3896 link
= state
-> link_to_peer
;
3899 link
-> outer
-> type
!= omapi_type_connection
)
3900 return ISC_R_INVALIDARG
;
3902 status
= (dhcp_failover_put_message
3903 (link
, link
-> outer
,
3905 dhcp_failover_make_option (FTO_SERVER_STATE
, FMA
,
3906 (state
-> me
.state
== startup
3907 ? state
-> saved_state
3908 : state
-> me
.state
)),
3909 dhcp_failover_make_option
3910 (FTO_SERVER_FLAGS
, FMA
,
3911 (state
-> service_state
== service_startup
3912 ? FTF_STARTUP
: 0)),
3913 dhcp_failover_make_option (FTO_STOS
, FMA
, state
-> me
.stos
),
3914 (failover_option_t
*)0));
3916 #if defined (DEBUG_FAILOVER_MESSAGES)
3917 if (status
!= ISC_R_SUCCESS
)
3918 failover_print (FMA
, " (failed)");
3919 failover_print (FMA
, ")");
3921 log_debug ("%s", obuf
);
3924 return ISC_R_SUCCESS
;
3927 /* Send a connect message. */
3929 isc_result_t
dhcp_failover_send_connect (omapi_object_t
*l
)
3931 dhcp_failover_link_t
*link
;
3932 dhcp_failover_state_t
*state
;
3933 isc_result_t status
;
3935 #if defined (DEBUG_FAILOVER_MESSAGES)
3937 unsigned obufix
= 0;
3939 # define FMA obuf, &obufix, sizeof obuf
3940 failover_print (FMA
, "(connect");
3942 # define FMA (char *)0, (unsigned *)0, 0
3945 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
3946 return ISC_R_INVALIDARG
;
3947 link
= (dhcp_failover_link_t
*)l
;
3948 state
= link
-> state_object
;
3949 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
3950 return ISC_R_INVALIDARG
;
3953 (dhcp_failover_put_message
3956 dhcp_failover_make_option (FTO_SERVER_ADDR
, FMA
,
3957 state
-> server_identifier
.len
,
3958 state
-> server_identifier
.data
),
3959 dhcp_failover_make_option (FTO_MAX_UNACKED
, FMA
,
3960 state
-> me
.max_flying_updates
),
3961 dhcp_failover_make_option (FTO_RECEIVE_TIMER
, FMA
,
3962 state
-> me
.max_response_delay
),
3963 dhcp_failover_option_printf (FTO_VENDOR_CLASS
, FMA
,
3964 "isc-%s", DHCP_VERSION
),
3965 dhcp_failover_make_option (FTO_PROTOCOL_VERSION
, FMA
,
3966 DHCP_FAILOVER_VERSION
),
3967 dhcp_failover_make_option (FTO_TLS_REQUEST
, FMA
,
3969 dhcp_failover_make_option (FTO_MCLT
, FMA
,
3972 ? dhcp_failover_make_option (FTO_HBA
, FMA
, 32, state
-> hba
)
3973 : &skip_failover_option
),
3974 (failover_option_t
*)0));
3976 #if defined (DEBUG_FAILOVER_MESSAGES)
3977 if (status
!= ISC_R_SUCCESS
)
3978 failover_print (FMA
, " (failed)");
3979 failover_print (FMA
, ")");
3981 log_debug ("%s", obuf
);
3987 isc_result_t
dhcp_failover_send_connectack (omapi_object_t
*l
,
3988 dhcp_failover_state_t
*state
,
3989 int reason
, const char *errmsg
)
3991 dhcp_failover_link_t
*link
;
3992 isc_result_t status
;
3993 #if defined (DEBUG_FAILOVER_MESSAGES)
3995 unsigned obufix
= 0;
3997 # define FMA obuf, &obufix, sizeof obuf
3998 failover_print (FMA
, "(connectack");
4000 # define FMA (char *)0, (unsigned *)0, 0
4003 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
4004 return ISC_R_INVALIDARG
;
4005 link
= (dhcp_failover_link_t
*)l
;
4006 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
4007 return ISC_R_INVALIDARG
;
4010 (dhcp_failover_put_message
4014 ? (dhcp_failover_make_option
4015 (FTO_SERVER_ADDR
, FMA
,
4016 state
-> server_identifier
.len
,
4017 state
-> server_identifier
.data
))
4018 : &skip_failover_option
),
4020 ? dhcp_failover_make_option (FTO_MAX_UNACKED
, FMA
,
4021 state
-> me
.max_flying_updates
)
4022 : &skip_failover_option
),
4024 ? dhcp_failover_make_option (FTO_RECEIVE_TIMER
, FMA
,
4025 state
-> me
.max_response_delay
)
4026 : &skip_failover_option
),
4027 dhcp_failover_option_printf (FTO_VENDOR_CLASS
, FMA
,
4028 "isc-%s", DHCP_VERSION
),
4029 dhcp_failover_make_option (FTO_PROTOCOL_VERSION
, FMA
,
4030 DHCP_FAILOVER_VERSION
),
4031 dhcp_failover_make_option (FTO_TLS_REQUEST
, FMA
,
4034 ? dhcp_failover_make_option (FTO_REJECT_REASON
,
4036 : &skip_failover_option
),
4038 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4039 strlen (errmsg
), errmsg
)
4040 : &skip_failover_option
),
4041 (failover_option_t
*)0));
4043 #if defined (DEBUG_FAILOVER_MESSAGES)
4044 if (status
!= ISC_R_SUCCESS
)
4045 failover_print (FMA
, " (failed)");
4046 failover_print (FMA
, ")");
4048 log_debug ("%s", obuf
);
4054 isc_result_t
dhcp_failover_send_disconnect (omapi_object_t
*l
,
4056 const char *message
)
4058 dhcp_failover_link_t
*link
;
4059 dhcp_failover_state_t
*state
;
4060 isc_result_t status
;
4061 #if defined (DEBUG_FAILOVER_MESSAGES)
4063 unsigned obufix
= 0;
4065 # define FMA obuf, &obufix, sizeof obuf
4066 failover_print (FMA
, "(disconnect");
4068 # define FMA (char *)0, (unsigned *)0, 0
4071 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
4072 return ISC_R_INVALIDARG
;
4073 link
= (dhcp_failover_link_t
*)l
;
4074 state
= link
-> state_object
;
4075 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
4076 return ISC_R_INVALIDARG
;
4078 if (!message
&& reason
)
4079 message
= dhcp_failover_reject_reason_print (reason
);
4081 status
= (dhcp_failover_put_message
4084 dhcp_failover_make_option (FTO_REJECT_REASON
,
4087 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4088 strlen (message
), message
)
4089 : &skip_failover_option
),
4090 (failover_option_t
*)0));
4092 #if defined (DEBUG_FAILOVER_MESSAGES)
4093 if (status
!= ISC_R_SUCCESS
)
4094 failover_print (FMA
, " (failed)");
4095 failover_print (FMA
, ")");
4097 log_debug ("%s", obuf
);
4103 /* Send a Bind Update message. */
4105 isc_result_t
dhcp_failover_send_bind_update (dhcp_failover_state_t
*state
,
4106 struct lease
*lease
)
4108 dhcp_failover_link_t
*link
;
4109 isc_result_t status
;
4110 #if defined (DEBUG_FAILOVER_MESSAGES)
4112 unsigned obufix
= 0;
4114 # define FMA obuf, &obufix, sizeof obuf
4115 failover_print (FMA
, "(bndupd");
4117 # define FMA (char *)0, (unsigned *)0, 0
4120 if (!state
-> link_to_peer
||
4121 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4122 return ISC_R_INVALIDARG
;
4123 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4125 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4126 return ISC_R_INVALIDARG
;
4128 /* Send the update. */
4129 status
= (dhcp_failover_put_message
4130 (link
, link
-> outer
,
4132 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS
, FMA
,
4133 lease
-> ip_addr
.len
,
4134 lease
-> ip_addr
.iabuf
),
4135 dhcp_failover_make_option (FTO_BINDING_STATUS
, FMA
,
4136 lease
-> desired_binding_state
),
4138 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER
, FMA
,
4141 : &skip_failover_option
,
4142 lease
-> hardware_addr
.hlen
4143 ? dhcp_failover_make_option (FTO_CHADDR
, FMA
,
4144 lease
-> hardware_addr
.hlen
,
4145 lease
-> hardware_addr
.hbuf
)
4146 : &skip_failover_option
,
4147 dhcp_failover_make_option (FTO_LEASE_EXPIRY
, FMA
,
4149 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY
, FMA
,
4151 dhcp_failover_make_option (FTO_STOS
, FMA
,
4153 dhcp_failover_make_option (FTO_CLTT
, FMA
,
4155 &skip_failover_option
, /* XXX DDNS */
4156 &skip_failover_option
, /* XXX request options */
4157 &skip_failover_option
, /* XXX reply options */
4158 (failover_option_t
*)0));
4160 #if defined (DEBUG_FAILOVER_MESSAGES)
4161 if (status
!= ISC_R_SUCCESS
)
4162 failover_print (FMA
, " (failed)");
4163 failover_print (FMA
, ")");
4165 log_debug ("%s", obuf
);
4171 /* Send a Bind ACK message. */
4173 isc_result_t
dhcp_failover_send_bind_ack (dhcp_failover_state_t
*state
,
4174 failover_message_t
*msg
,
4175 int reason
, const char *message
)
4177 dhcp_failover_link_t
*link
;
4178 isc_result_t status
;
4179 #if defined (DEBUG_FAILOVER_MESSAGES)
4181 unsigned obufix
= 0;
4183 # define FMA obuf, &obufix, sizeof obuf
4184 failover_print (FMA
, "(bndack");
4186 # define FMA (char *)0, (unsigned *)0, 0
4189 if (!state
-> link_to_peer
||
4190 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4191 return ISC_R_INVALIDARG
;
4192 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4194 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4195 return ISC_R_INVALIDARG
;
4197 if (!message
&& reason
)
4198 message
= dhcp_failover_reject_reason_print (reason
);
4200 /* Send the update. */
4201 status
= (dhcp_failover_put_message
4202 (link
, link
-> outer
,
4204 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS
, FMA
,
4205 sizeof msg
-> assigned_addr
,
4206 &msg
-> assigned_addr
),
4207 dhcp_failover_make_option (FTO_BINDING_STATUS
, FMA
,
4208 msg
-> binding_status
),
4209 (msg
-> options_present
& FTB_CLIENT_IDENTIFIER
)
4210 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER
, FMA
,
4211 msg
-> client_identifier
.count
,
4212 msg
-> client_identifier
.data
)
4213 : &skip_failover_option
,
4214 (msg
-> options_present
& FTB_CHADDR
)
4215 ? dhcp_failover_make_option (FTO_CHADDR
, FMA
,
4216 msg
-> chaddr
.count
,
4218 : &skip_failover_option
,
4219 dhcp_failover_make_option (FTO_LEASE_EXPIRY
, FMA
,
4221 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY
, FMA
,
4222 msg
-> potential_expiry
),
4223 dhcp_failover_make_option (FTO_STOS
, FMA
,
4225 dhcp_failover_make_option (FTO_CLTT
, FMA
,
4228 ? dhcp_failover_make_option (FTO_REJECT_REASON
,
4230 : &skip_failover_option
,
4232 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4233 strlen (message
), message
)
4234 : &skip_failover_option
),
4235 &skip_failover_option
, /* XXX DDNS */
4236 &skip_failover_option
, /* XXX request options */
4237 &skip_failover_option
, /* XXX reply options */
4238 (failover_option_t
*)0));
4240 #if defined (DEBUG_FAILOVER_MESSAGES)
4241 if (status
!= ISC_R_SUCCESS
)
4242 failover_print (FMA
, " (failed)");
4243 failover_print (FMA
, ")");
4245 log_debug ("%s", obuf
);
4251 isc_result_t
dhcp_failover_send_poolreq (dhcp_failover_state_t
*state
)
4253 dhcp_failover_link_t
*link
;
4254 isc_result_t status
;
4255 #if defined (DEBUG_FAILOVER_MESSAGES)
4257 unsigned obufix
= 0;
4259 # define FMA obuf, &obufix, sizeof obuf
4260 failover_print (FMA
, "(poolreq");
4262 # define FMA (char *)0, (unsigned *)0, 0
4265 if (!state
-> link_to_peer
||
4266 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4267 return ISC_R_INVALIDARG
;
4268 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4270 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4271 return ISC_R_INVALIDARG
;
4273 status
= (dhcp_failover_put_message
4274 (link
, link
-> outer
,
4276 (failover_option_t
*)0));
4278 #if defined (DEBUG_FAILOVER_MESSAGES)
4279 if (status
!= ISC_R_SUCCESS
)
4280 failover_print (FMA
, " (failed)");
4281 failover_print (FMA
, ")");
4283 log_debug ("%s", obuf
);
4289 isc_result_t
dhcp_failover_send_poolresp (dhcp_failover_state_t
*state
,
4292 dhcp_failover_link_t
*link
;
4293 isc_result_t status
;
4294 #if defined (DEBUG_FAILOVER_MESSAGES)
4296 unsigned obufix
= 0;
4298 # define FMA obuf, &obufix, sizeof obuf
4299 failover_print (FMA
, "(poolresp");
4301 # define FMA (char *)0, (unsigned *)0, 0
4304 if (!state
-> link_to_peer
||
4305 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4306 return ISC_R_INVALIDARG
;
4307 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4309 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4310 return ISC_R_INVALIDARG
;
4312 status
= (dhcp_failover_put_message
4313 (link
, link
-> outer
,
4315 dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED
, FMA
,
4317 (failover_option_t
*)0));
4319 #if defined (DEBUG_FAILOVER_MESSAGES)
4320 if (status
!= ISC_R_SUCCESS
)
4321 failover_print (FMA
, " (failed)");
4322 failover_print (FMA
, ")");
4324 log_debug ("%s", obuf
);
4330 isc_result_t
dhcp_failover_send_update_request (dhcp_failover_state_t
*state
)
4332 dhcp_failover_link_t
*link
;
4333 isc_result_t status
;
4334 #if defined (DEBUG_FAILOVER_MESSAGES)
4336 unsigned obufix
= 0;
4338 # define FMA obuf, &obufix, sizeof obuf
4339 failover_print (FMA
, "(updreq");
4341 # define FMA (char *)0, (unsigned *)0, 0
4344 if (!state
-> link_to_peer
||
4345 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4346 return ISC_R_INVALIDARG
;
4347 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4349 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4350 return ISC_R_INVALIDARG
;
4352 if (state
-> curUPD
)
4353 return ISC_R_ALREADYRUNNING
;
4355 status
= (dhcp_failover_put_message
4356 (link
, link
-> outer
,
4358 (failover_option_t
*)0));
4360 if (status
== ISC_R_SUCCESS
)
4361 state
-> curUPD
= FTM_UPDREQ
;
4363 #if defined (DEBUG_FAILOVER_MESSAGES)
4364 if (status
!= ISC_R_SUCCESS
)
4365 failover_print (FMA
, " (failed)");
4366 failover_print (FMA
, ")");
4368 log_debug ("%s", obuf
);
4371 log_info ("Sent update request message to %s", state
-> name
);
4375 isc_result_t
dhcp_failover_send_update_request_all (dhcp_failover_state_t
4378 dhcp_failover_link_t
*link
;
4379 isc_result_t status
;
4380 #if defined (DEBUG_FAILOVER_MESSAGES)
4382 unsigned obufix
= 0;
4384 # define FMA obuf, &obufix, sizeof obuf
4385 failover_print (FMA
, "(updreqall");
4387 # define FMA (char *)0, (unsigned *)0, 0
4390 if (!state
-> link_to_peer
||
4391 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4392 return ISC_R_INVALIDARG
;
4393 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4395 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4396 return ISC_R_INVALIDARG
;
4398 /* If there is an UPDREQ in progress, then upgrade to UPDREQALL. */
4399 if (state
-> curUPD
&& (state
-> curUPD
!= FTM_UPDREQ
))
4400 return ISC_R_ALREADYRUNNING
;
4402 status
= (dhcp_failover_put_message
4403 (link
, link
-> outer
,
4405 (failover_option_t
*)0));
4407 if (status
== ISC_R_SUCCESS
)
4408 state
-> curUPD
= FTM_UPDREQALL
;
4410 #if defined (DEBUG_FAILOVER_MESSAGES)
4411 if (status
!= ISC_R_SUCCESS
)
4412 failover_print (FMA
, " (failed)");
4413 failover_print (FMA
, ")");
4415 log_debug ("%s", obuf
);
4418 log_info ("Sent update request all message to %s", state
-> name
);
4422 isc_result_t
dhcp_failover_send_update_done (dhcp_failover_state_t
*state
)
4424 dhcp_failover_link_t
*link
;
4425 isc_result_t status
;
4426 #if defined (DEBUG_FAILOVER_MESSAGES)
4428 unsigned obufix
= 0;
4430 # define FMA obuf, &obufix, sizeof obuf
4431 failover_print (FMA
, "(upddone");
4433 # define FMA (char *)0, (unsigned *)0, 0
4436 if (!state
-> link_to_peer
||
4437 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4438 return ISC_R_INVALIDARG
;
4439 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4441 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4442 return ISC_R_INVALIDARG
;
4444 status
= (dhcp_failover_put_message
4445 (link
, link
-> outer
,
4447 (failover_option_t
*)0));
4449 #if defined (DEBUG_FAILOVER_MESSAGES)
4450 if (status
!= ISC_R_SUCCESS
)
4451 failover_print (FMA
, " (failed)");
4452 failover_print (FMA
, ")");
4454 log_debug ("%s", obuf
);
4458 log_info ("Sent update done message to %s", state
-> name
);
4460 /* There may be uncommitted leases at this point (since
4461 dhcp_failover_process_bind_ack() doesn't commit leases);
4462 commit the lease file. */
4468 isc_result_t
dhcp_failover_process_bind_update (dhcp_failover_state_t
*state
,
4469 failover_message_t
*msg
)
4471 struct lease
*lt
, *lease
;
4473 int reason
= FTR_MISC_REJECT
;
4474 const char *message
;
4475 int new_binding_state
;
4477 ia
.len
= sizeof msg
-> assigned_addr
;
4478 memcpy (ia
.iabuf
, &msg
-> assigned_addr
, ia
.len
);
4480 lease
= (struct lease
*)0;
4481 lt
= (struct lease
*)0;
4482 if (!find_lease_by_ip_addr (&lease
, ia
, MDL
)) {
4483 message
= "unknown IP address";
4484 reason
= FTR_ILLEGAL_IP_ADDR
;
4488 /* XXX check for conflicts. */
4490 /* Install the new info. */
4491 if (!lease_copy (<
, lease
, MDL
)) {
4492 message
= "no memory";
4496 if (msg
-> options_present
& FTB_CHADDR
) {
4497 if (msg
->binding_status
== FTS_ABANDONED
) {
4498 message
= "BNDUPD to ABANDONED with a CHADDR";
4501 if (msg
-> chaddr
.count
> sizeof lt
-> hardware_addr
.hbuf
) {
4502 message
= "chaddr too long";
4505 lt
-> hardware_addr
.hlen
= msg
-> chaddr
.count
;
4506 memcpy (lt
-> hardware_addr
.hbuf
, msg
-> chaddr
.data
,
4507 msg
-> chaddr
.count
);
4508 } else if (msg
->binding_status
== FTS_ACTIVE
||
4509 msg
->binding_status
== FTS_EXPIRED
||
4510 msg
->binding_status
== FTS_RELEASED
) {
4511 message
= "BNDUPD without CHADDR";
4513 } else if (msg
->binding_status
== FTS_ABANDONED
) {
4514 lt
->hardware_addr
.hlen
= 0;
4516 binding_scope_dereference(<
->scope
, MDL
);
4519 /* There is no explicit message content to indicate that the client
4520 * supplied no client-identifier. So if we don't hear of a value,
4521 * we discard the last one.
4523 if (msg
->options_present
& FTB_CLIENT_IDENTIFIER
) {
4524 if (msg
->binding_status
== FTS_ABANDONED
) {
4525 message
= "BNDUPD to ABANDONED with client-id";
4529 lt
->uid_len
= msg
->client_identifier
.count
;
4531 /* Allocate the lt->uid buffer if we haven't already, or
4532 * re-allocate the lt-uid buffer if we have one that is not
4533 * large enough. Otherwise, just use the extant buffer.
4535 if (!lt
->uid
|| lt
->uid
== lt
->uid_buf
||
4536 lt
->uid_len
> lt
->uid_max
) {
4537 if (lt
->uid
&& lt
->uid
!= lt
->uid_buf
)
4538 dfree(lt
->uid
, MDL
);
4540 if (lt
->uid_len
> sizeof(lt
->uid_buf
)) {
4541 lt
->uid_max
= lt
->uid_len
;
4542 lt
->uid
= dmalloc(lt
->uid_len
, MDL
);
4544 message
= "no memory";
4548 lt
->uid_max
= sizeof(lt
->uid_buf
);
4549 lt
->uid
= lt
->uid_buf
;
4553 msg
-> client_identifier
.data
, lt
-> uid_len
);
4554 } else if (lt
->uid
&& msg
->binding_status
!= FTS_RESET
&&
4555 msg
->binding_status
!= FTS_FREE
&&
4556 msg
->binding_status
!= FTS_BACKUP
) {
4557 if (lt
->uid
!= lt
->uid_buf
)
4558 dfree (lt
->uid
, MDL
);
4560 lt
->uid_max
= lt
->uid_len
= 0;
4563 /* If the lease was expired, also remove the stale binding scope. */
4564 if (lt
->scope
&& lt
->ends
< cur_time
)
4565 binding_scope_dereference(<
->scope
, MDL
);
4567 /* XXX Times may need to be adjusted based on clock skew! */
4568 if (msg
-> options_present
& FTB_STOS
) {
4569 lt
-> starts
= msg
-> stos
;
4571 if (msg
-> options_present
& FTB_LEASE_EXPIRY
) {
4572 lt
-> ends
= msg
-> expiry
;
4574 if (msg
-> options_present
& FTB_CLTT
) {
4575 lt
-> cltt
= msg
-> client_ltt
;
4577 if (msg
->options_present
& FTB_POTENTIAL_EXPIRY
) {
4578 lt
->atsfp
= lt
->tsfp
= msg
->potential_expiry
;
4581 if (msg
-> options_present
& FTB_BINDING_STATUS
) {
4582 #if defined (DEBUG_LEASE_STATE_TRANSITIONS)
4583 log_info ("processing state transition for %s: %s to %s",
4584 piaddr (lease
-> ip_addr
),
4585 binding_state_print (lease
-> binding_state
),
4586 binding_state_print (msg
-> binding_status
));
4589 /* If we're in normal state, make sure the state transition
4591 if (state
-> me
.state
== normal
) {
4593 (normal_binding_state_transition_check
4594 (lease
, state
, msg
-> binding_status
,
4595 msg
-> potential_expiry
));
4596 /* XXX if the transition the peer asked for isn't
4597 XXX allowed, maybe we should make the transition
4598 XXX into potential-conflict at this point. */
4601 (conflict_binding_state_transition_check
4602 (lease
, state
, msg
-> binding_status
,
4603 msg
-> potential_expiry
));
4605 if (new_binding_state
!= msg
-> binding_status
) {
4608 if (snprintf (outbuf
, sizeof outbuf
,
4609 "%s: invalid state transition: %s to %s",
4610 piaddr (lease
-> ip_addr
),
4611 binding_state_print (lease
-> binding_state
),
4612 binding_state_print (msg
-> binding_status
))
4614 log_fatal ("%s: impossible outbuf overflow",
4615 "dhcp_failover_process_bind_update");
4617 dhcp_failover_send_bind_ack (state
, msg
,
4622 if (new_binding_state
== FTS_EXPIRED
||
4623 new_binding_state
== FTS_RELEASED
||
4624 new_binding_state
== FTS_RESET
)
4625 lt
-> next_binding_state
= FTS_FREE
;
4627 lt
-> next_binding_state
= new_binding_state
;
4628 msg
-> binding_status
= lt
-> next_binding_state
;
4631 /* Try to install the new information. */
4632 if (!supersede_lease (lease
, lt
, 0, 0, 0) ||
4633 !write_lease (lease
)) {
4634 message
= "database update failed";
4636 dhcp_failover_send_bind_ack (state
, msg
, reason
, message
);
4639 dhcp_failover_queue_ack (state
, msg
);
4644 lease_dereference (<
, MDL
);
4646 lease_dereference (&lease
, MDL
);
4648 return ISC_R_SUCCESS
;
4651 isc_result_t
dhcp_failover_process_bind_ack (dhcp_failover_state_t
*state
,
4652 failover_message_t
*msg
)
4654 struct lease
*lt
= (struct lease
*)0;
4655 struct lease
*lease
= (struct lease
*)0;
4657 const char *message
= "no memory";
4659 ia
.len
= sizeof msg
-> assigned_addr
;
4660 memcpy (ia
.iabuf
, &msg
-> assigned_addr
, ia
.len
);
4662 if (!find_lease_by_ip_addr (&lease
, ia
, MDL
)) {
4663 message
= "no such lease";
4667 /* XXX check for conflicts. */
4668 if (msg
-> options_present
& FTB_REJECT_REASON
) {
4669 log_error ("bind update on %s from %s rejected: %.*s",
4670 piaddr (ia
), state
-> name
,
4671 (int)((msg
-> options_present
& FTB_MESSAGE
)
4672 ? msg
-> message
.count
4673 : strlen (dhcp_failover_reject_reason_print
4674 (msg
-> reject_reason
))),
4675 (msg
-> options_present
& FTB_MESSAGE
)
4676 ? (const char *)(msg
-> message
.data
)
4677 : (dhcp_failover_reject_reason_print
4678 (msg
-> reject_reason
)));
4682 /* XXX Times may need to be adjusted based on clock skew! */
4683 if (msg
-> options_present
& FTB_POTENTIAL_EXPIRY
) {
4684 /* XXX it could be a problem to do this directly if the
4685 XXX lease is sorted by tsfp. */
4686 lease
->atsfp
= lease
->tsfp
= msg
->potential_expiry
;
4688 if ((lease
-> binding_state
== FTS_EXPIRED
||
4689 lease
-> binding_state
== FTS_RESET
||
4690 lease
-> binding_state
== FTS_RELEASED
) &&
4691 (msg
-> options_present
& FTB_BINDING_STATUS
) &&
4692 msg
-> binding_status
== FTS_FREE
)
4694 lease
-> next_binding_state
= FTS_FREE
;
4695 supersede_lease (lease
, (struct lease
*)0, 0, 0, 0);
4696 write_lease (lease
);
4697 if (state
-> me
.state
== normal
)
4700 if ((lease
-> desired_binding_state
!=
4701 lease
-> binding_state
) &&
4702 (msg
-> options_present
& FTB_BINDING_STATUS
) &&
4703 (msg
-> binding_status
==
4704 lease
-> desired_binding_state
)) {
4705 lease
-> next_binding_state
=
4706 lease
-> desired_binding_state
;
4707 supersede_lease (lease
,
4708 (struct lease
*)0, 0, 0, 0);
4710 write_lease (lease
);
4711 /* Commit the lease only after a two-second timeout,
4712 so that if we get a bunch of acks in quick
4713 successtion (e.g., when stealing leases from the
4714 secondary), we do not do an immediate commit for
4716 add_timeout (cur_time
+ 2,
4717 commit_leases_timeout
, (void *)0, 0, 0);
4719 } else if (lease
-> desired_binding_state
!= lease
-> binding_state
&&
4720 (msg
-> options_present
& FTB_BINDING_STATUS
) &&
4721 msg
-> binding_status
== lease
-> desired_binding_state
) {
4722 lease
-> next_binding_state
= lease
-> desired_binding_state
;
4723 supersede_lease (lease
, (struct lease
*)0, 0, 0, 0);
4724 write_lease (lease
);
4725 add_timeout (cur_time
+ 2, commit_leases_timeout
,
4730 dhcp_failover_ack_queue_remove (state
, lease
);
4732 /* If we are supposed to send an update done after we send
4733 this lease, go ahead and send it. */
4734 if (state
-> send_update_done
== lease
) {
4735 lease_dereference (&state
-> send_update_done
, MDL
);
4736 dhcp_failover_send_update_done (state
);
4739 /* If there are updates pending, we've created space to send at
4741 dhcp_failover_send_updates (state
);
4744 lease_dereference (&lease
, MDL
);
4746 lease_dereference (<
, MDL
);
4748 return ISC_R_SUCCESS
;
4751 log_info ("bind update on %s from %s: %s.",
4752 piaddr (ia
), state
-> name
, message
);
4756 isc_result_t
dhcp_failover_generate_update_queue (dhcp_failover_state_t
*state
,
4759 struct shared_network
*s
;
4761 struct lease
*l
, *n
;
4763 struct lease
**lptr
[5];
4764 #define FREE_LEASES 0
4765 #define ACTIVE_LEASES 1
4766 #define EXPIRED_LEASES 2
4767 #define ABANDONED_LEASES 3
4768 #define BACKUP_LEASES 4
4770 /* First remove everything from the update and ack queues. */
4771 l
= n
= (struct lease
*)0;
4772 if (state
-> update_queue_head
) {
4773 lease_reference (&l
, state
-> update_queue_head
, MDL
);
4774 lease_dereference (&state
-> update_queue_head
, MDL
);
4776 l
-> flags
&= ~ON_UPDATE_QUEUE
;
4777 if (l
-> next_pending
) {
4778 lease_reference (&n
,
4779 l
-> next_pending
, MDL
);
4780 lease_dereference (&l
-> next_pending
, MDL
);
4782 lease_dereference (&l
, MDL
);
4784 lease_reference (&l
, n
, MDL
);
4785 lease_dereference (&n
, MDL
);
4788 lease_dereference (&state
-> update_queue_tail
, MDL
);
4791 if (state
-> ack_queue_head
) {
4792 lease_reference (&l
, state
-> ack_queue_head
, MDL
);
4793 lease_dereference (&state
-> ack_queue_head
, MDL
);
4795 l
-> flags
&= ~ON_ACK_QUEUE
;
4796 if (l
-> next_pending
) {
4797 lease_reference (&n
,
4798 l
-> next_pending
, MDL
);
4799 lease_dereference (&l
-> next_pending
, MDL
);
4801 lease_dereference (&l
, MDL
);
4803 lease_reference (&l
, n
, MDL
);
4804 lease_dereference (&n
, MDL
);
4807 lease_dereference (&state
-> ack_queue_tail
, MDL
);
4809 if (state
-> send_update_done
)
4810 lease_dereference (&state
-> send_update_done
, MDL
);
4811 state
-> cur_unacked_updates
= 0;
4813 /* Loop through each pool in each shared network and call the
4814 expiry routine on the pool. */
4815 for (s
= shared_networks
; s
; s
= s
-> next
) {
4816 for (p
= s
-> pools
; p
; p
= p
-> next
) {
4817 if (p
->failover_peer
!= state
)
4820 lptr
[FREE_LEASES
] = &p
-> free
;
4821 lptr
[ACTIVE_LEASES
] = &p
-> active
;
4822 lptr
[EXPIRED_LEASES
] = &p
-> expired
;
4823 lptr
[ABANDONED_LEASES
] = &p
-> abandoned
;
4824 lptr
[BACKUP_LEASES
] = &p
-> backup
;
4826 for (i
= FREE_LEASES
; i
<= BACKUP_LEASES
; i
++) {
4827 for (l
= *(lptr
[i
]); l
; l
= l
-> next
) {
4829 (l
->starts
!= MIN_TIME
||
4830 l
->ends
!= MIN_TIME
)) ||
4831 (l
->tstp
> l
->atsfp
) ||
4832 (i
== EXPIRED_LEASES
)) {
4833 l
-> desired_binding_state
= l
-> binding_state
;
4834 dhcp_failover_queue_update (l
, 0);
4840 return ISC_R_SUCCESS
;
4844 dhcp_failover_process_update_request (dhcp_failover_state_t
*state
,
4845 failover_message_t
*msg
)
4847 /* Generate a fresh update queue. */
4848 dhcp_failover_generate_update_queue (state
, 0);
4850 /* If there's anything on the update queue (there shouldn't be
4851 anything on the ack queue), trigger an update done message
4852 when we get an ack for that lease. */
4853 if (state
-> update_queue_tail
) {
4854 lease_reference (&state
-> send_update_done
,
4855 state
-> update_queue_tail
, MDL
);
4856 dhcp_failover_send_updates (state
);
4857 log_info ("Update request from %s: sending update",
4860 /* Otherwise, there are no updates to send, so we can
4861 just send an UPDDONE message immediately. */
4862 dhcp_failover_send_update_done (state
);
4863 log_info ("Update request from %s: nothing pending",
4867 return ISC_R_SUCCESS
;
4871 dhcp_failover_process_update_request_all (dhcp_failover_state_t
*state
,
4872 failover_message_t
*msg
)
4874 /* Generate a fresh update queue that includes every lease. */
4875 dhcp_failover_generate_update_queue (state
, 1);
4877 if (state
-> update_queue_tail
) {
4878 lease_reference (&state
-> send_update_done
,
4879 state
-> update_queue_tail
, MDL
);
4880 dhcp_failover_send_updates (state
);
4881 log_info ("Update request all from %s: sending update",
4884 /* This should really never happen, but it could happen
4885 on a server that currently has no leases configured. */
4886 dhcp_failover_send_update_done (state
);
4887 log_info ("Update request all from %s: nothing pending",
4891 return ISC_R_SUCCESS
;
4895 dhcp_failover_process_update_done (dhcp_failover_state_t
*state
,
4896 failover_message_t
*msg
)
4898 log_info ("failover peer %s: peer update completed.",
4901 state
-> curUPD
= 0;
4903 switch (state
-> me
.state
) {
4907 case communications_interrupted
:
4908 case resolution_interrupted
:
4914 break; /* shouldn't happen. */
4916 /* We got the UPDDONE, so we can go into normal state! */
4917 case potential_conflict
:
4918 dhcp_failover_set_state (state
, normal
);
4922 /* Wait for MCLT to expire before moving to recover_done,
4923 except that if both peers come up in recover, there is
4924 no point in waiting for MCLT to expire - this probably
4925 indicates the initial startup of a newly-configured
4927 if (state
-> me
.stos
+ state
-> mclt
> cur_time
&&
4928 state
-> partner
.state
!= recover
&&
4929 state
-> partner
.state
!= recover_done
) {
4930 dhcp_failover_set_state (state
, recover_wait
);
4931 #if defined (DEBUG_FAILOVER_TIMING)
4932 log_info ("add_timeout +%d %s",
4934 state
-> me
.stos
+ state
-> mclt
),
4935 "dhcp_failover_recover_done");
4937 add_timeout ((int)(state
-> me
.stos
+ state
-> mclt
),
4938 dhcp_failover_recover_done
,
4940 (tvref_t
)omapi_object_reference
,
4942 omapi_object_dereference
);
4944 dhcp_failover_recover_done (state
);
4947 return ISC_R_SUCCESS
;
4950 void dhcp_failover_recover_done (void *sp
)
4952 dhcp_failover_state_t
*state
= sp
;
4954 #if defined (DEBUG_FAILOVER_TIMING)
4955 log_info ("dhcp_failover_recover_done");
4958 dhcp_failover_set_state (state
, recover_done
);
4961 #if defined (DEBUG_FAILOVER_MESSAGES)
4962 /* Print hunks of failover messages, doing line breaks as appropriate.
4963 Note that this assumes syslog is being used, rather than, e.g., the
4964 Windows NT logging facility, where just dumping the whole message in
4965 one hunk would be more appropriate. */
4967 void failover_print (char *obuf
,
4968 unsigned *obufix
, unsigned obufmax
, const char *s
)
4970 int len
= strlen (s
);
4972 while (len
+ *obufix
+ 1 >= obufmax
) {
4973 log_debug ("%s", obuf
);
4975 log_debug ("%s", s
);
4981 strcpy (&obuf
[*obufix
], s
);
4984 #endif /* defined (DEBUG_FAILOVER_MESSAGES) */
4986 /* Taken from draft-ietf-dhc-loadb-01.txt: */
4987 /* A "mixing table" of 256 distinct values, in pseudo-random order. */
4988 unsigned char loadb_mx_tbl
[256] = {
4989 251, 175, 119, 215, 81, 14, 79, 191, 103, 49,
4990 181, 143, 186, 157, 0, 232, 31, 32, 55, 60,
4991 152, 58, 17, 237, 174, 70, 160, 144, 220, 90,
4992 57, 223, 59, 3, 18, 140, 111, 166, 203, 196,
4993 134, 243, 124, 95, 222, 179, 197, 65, 180, 48,
4994 36, 15, 107, 46, 233, 130, 165, 30, 123, 161,
4995 209, 23, 97, 16, 40, 91, 219, 61, 100, 10,
4996 210, 109, 250, 127, 22, 138, 29, 108, 244, 67,
4997 207, 9, 178, 204, 74, 98, 126, 249, 167, 116,
4998 34, 77, 193, 200, 121, 5, 20, 113, 71, 35,
4999 128, 13, 182, 94, 25, 226, 227, 199, 75, 27,
5000 41, 245, 230, 224, 43, 225, 177, 26, 155, 150,
5001 212, 142, 218, 115, 241, 73, 88, 105, 39, 114,
5002 62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
5003 154, 122, 12, 84, 82, 163, 44, 139, 228, 236,
5004 205, 242, 217, 11, 187, 146, 159, 64, 86, 239,
5005 195, 42, 106, 198, 118, 112, 184, 172, 87, 2,
5006 173, 117, 176, 229, 247, 253, 137, 185, 99, 164,
5007 102, 147, 45, 66, 231, 52, 141, 211, 194, 206,
5008 246, 238, 56, 110, 78, 248, 63, 240, 189, 93,
5009 92, 51, 53, 183, 19, 171, 72, 50, 33, 104,
5010 101, 69, 8, 252, 83, 120, 76, 135, 85, 54,
5011 202, 125, 188, 213, 96, 235, 136, 208, 162, 129,
5012 190, 132, 156, 38, 47, 1, 7, 254, 24, 4,
5013 216, 131, 89, 21, 28, 133, 37, 153, 149, 80,
5014 170, 68, 6, 169, 234, 151 };
5016 static unsigned char loadb_p_hash (const unsigned char *, unsigned);
5017 static unsigned char loadb_p_hash (const unsigned char *key
, unsigned len
)
5019 unsigned char hash
= len
;
5021 for(i
= len
; i
> 0; )
5022 hash
= loadb_mx_tbl
[hash
^ (key
[--i
])];
5026 int load_balance_mine (struct packet
*packet
, dhcp_failover_state_t
*state
)
5028 struct option_cache
*oc
;
5029 struct data_string ds
;
5030 unsigned char hbaix
;
5033 if (state
-> load_balance_max_secs
< ntohs (packet
-> raw
-> secs
)) {
5037 /* If we don't have a hash bucket array, we can't tell if this
5038 one's ours, so we assume it's not. */
5042 oc
= lookup_option (&dhcp_universe
, packet
-> options
,
5043 DHO_DHCP_CLIENT_IDENTIFIER
);
5044 memset (&ds
, 0, sizeof ds
);
5046 evaluate_option_cache (&ds
, packet
, (struct lease
*)0,
5047 (struct client_state
*)0,
5048 packet
-> options
, (struct option_state
*)0,
5049 &global_scope
, oc
, MDL
)) {
5050 hbaix
= loadb_p_hash (ds
.data
, ds
.len
);
5052 hbaix
= loadb_p_hash (packet
-> raw
-> chaddr
,
5053 packet
-> raw
-> hlen
);
5055 hm
= (state
-> hba
[hbaix
/ 8] & (1 << (hbaix
& 3)));
5056 if (state
-> i_am
== primary
)
5062 /* This deals with what to do with bind updates when
5063 we're in the normal state
5065 Note that tsfp had better be set from the latest bind update
5066 _before_ this function is called! */
5069 normal_binding_state_transition_check (struct lease
*lease
,
5070 dhcp_failover_state_t
*state
,
5071 binding_state_t binding_state
,
5074 binding_state_t new_state
;
5076 /* If there is no transition, it's no problem. */
5077 if (binding_state
== lease
-> binding_state
)
5078 return binding_state
;
5080 switch (lease
-> binding_state
) {
5083 switch (binding_state
) {
5090 /* If the lease was free, and our peer is primary,
5091 then it can make it active, or abandoned, or
5092 backup. Abandoned is treated like free in
5094 if (state
-> i_am
== secondary
)
5095 return binding_state
;
5097 /* Otherwise, it can't legitimately do any sort of
5098 state transition. Because the lease was free,
5099 and the error has already been made, we allow the
5100 peer to change its state anyway, but log a warning
5101 message in hopes that the error will be fixed. */
5102 case FTS_FREE
: /* for compiler */
5103 new_state
= binding_state
;
5107 log_fatal ("Impossible case at %s:%d.", MDL
);
5111 /* The secondary can't change the state of an active
5113 if (state
-> i_am
== primary
) {
5114 /* Except that the client may send the DHCPRELEASE
5115 to the secondary, and we have to accept that. */
5116 if (binding_state
== FTS_RELEASED
)
5117 return binding_state
;
5118 new_state
= lease
-> binding_state
;
5122 /* So this is only for transitions made by the primary: */
5123 switch (binding_state
) {
5126 /* Can't set a lease to free or backup until the
5127 peer agrees that it's expired. */
5128 if (tsfp
> cur_time
) {
5129 new_state
= lease
-> binding_state
;
5132 return binding_state
;
5135 /* XXX 65 should be the clock skew between the peers
5136 XXX plus a fudge factor. This code will result
5137 XXX in problems if MCLT is really short or the
5138 XXX max-lease-time is really short (less than the
5139 XXX fudge factor. */
5140 if (lease
-> ends
- 65 > cur_time
) {
5141 new_state
= lease
-> binding_state
;
5149 return binding_state
;
5152 log_fatal ("Impossible case at %s:%d.", MDL
);
5157 switch (binding_state
) {
5160 /* Can't set a lease to free or backup until the
5161 peer agrees that it's expired. */
5162 if (tsfp
> cur_time
) {
5163 new_state
= lease
-> binding_state
;
5166 return binding_state
;
5173 return binding_state
;
5176 log_fatal ("Impossible case at %s:%d.", MDL
);
5180 switch (binding_state
) {
5184 /* These are invalid state transitions - should we
5191 return binding_state
;
5194 log_fatal ("Impossible case at %s:%d.", MDL
);
5198 switch (binding_state
) {
5201 /* Can't set a lease to free or backup until the
5202 peer agrees that it's expired. */
5203 if (tsfp
> cur_time
) {
5204 new_state
= lease
-> binding_state
;
5207 return binding_state
;
5214 return binding_state
;
5217 log_fatal ("Impossible case at %s:%d.", MDL
);
5221 switch (binding_state
) {
5227 /* If the lease was in backup, and our peer
5228 is secondary, then it can make it active
5230 if (state
-> i_am
== primary
)
5231 return binding_state
;
5233 /* Either the primary or the secondary can
5234 reasonably move a lease from the backup
5235 state to the free state. */
5237 return binding_state
;
5240 new_state
= lease
-> binding_state
;
5244 log_fatal ("Impossible case at %s:%d.", MDL
);
5249 log_fatal ("Impossible case at %s:%d.", MDL
);
5256 /* Determine whether the state transition is okay when we're potentially
5257 in conflict with the peer. */
5259 conflict_binding_state_transition_check (struct lease
*lease
,
5260 dhcp_failover_state_t
*state
,
5261 binding_state_t binding_state
,
5264 binding_state_t new_state
;
5266 /* If there is no transition, it's no problem. */
5267 if (binding_state
== lease
-> binding_state
)
5268 new_state
= binding_state
;
5270 switch (lease
-> binding_state
) {
5271 /* If we think the lease is not in use, then the
5272 state into which the partner put it is just fine,
5280 new_state
= binding_state
;
5283 /* If we think the lease *is* in use, then we're not
5284 going to take the partner's change if the partner
5285 thinks it's free. */
5287 switch (binding_state
) {
5290 new_state
= lease
-> binding_state
;
5294 /* If we don't agree about expiry, it's
5295 * invalid. 65 should allow for max
5296 * clock skew (60) plus some fudge.
5297 * XXX: should we refetch cur_time?
5299 if ((lease
->ends
- 65) > cur_time
)
5300 new_state
= lease
->binding_state
;
5302 new_state
= binding_state
;
5305 /* RELEASED, RESET, and ABANDONED indicate
5306 * that our partner has information about
5307 * this lease that we did not witness. Our
5313 new_state
= binding_state
;
5317 log_fatal ("Impossible case at %s:%d.", MDL
);
5323 log_fatal ("Impossible case at %s:%d.", MDL
);
5330 /* We can reallocate a lease under the following circumstances:
5332 (1) It belongs to us - it's FTS_FREE, and we're primary, or it's
5333 FTS_BACKUP, and we're secondary.
5334 (2) We're in partner_down, and the lease is not active, and we
5335 can be sure that the other server didn't make it active.
5336 We can only be sure that the server didn't make it active
5337 when we are in the partner_down state and one of the following
5338 two conditions holds:
5339 (a) in the case that the time sent from the peer is earlier than
5340 the time we entered the partner_down state, at least MCLT has
5341 gone by since we entered partner_down, or
5342 (b) in the case that the time sent from the peer is later than
5343 the time when we entered partner_down, the current time is
5344 later than the time sent from the peer by at least MCLT. */
5346 int lease_mine_to_reallocate (struct lease
*lease
)
5348 dhcp_failover_state_t
*peer
;
5350 if (lease
&& lease
->pool
&&
5351 (peer
= lease
->pool
->failover_peer
)) {
5352 switch (lease
->binding_state
) {
5354 /* ACTIVE leases may not be reallocated. */
5359 /* FREE leases may only be allocated by the primary,
5360 * unless the secondary is acting in partner_down
5361 * state and stos+mclt or tsfp+mclt has expired,
5362 * whichever is greater.
5364 * ABANDONED are treated the same as FREE for all
5365 * purposes here. Note that servers will only try
5366 * for ABANDONED leases as a last resort anyway.
5368 if (peer
-> i_am
== primary
)
5371 return(peer
->service_state
== service_partner_down
&&
5372 ((lease
->tsfp
< peer
->me
.stos
) ?
5373 (peer
->me
.stos
+ peer
->mclt
< cur_time
) :
5374 (lease
->tsfp
+ peer
->mclt
< cur_time
)));
5379 /* These three lease states go onto the 'expired'
5380 * queue. Upon entry into partner-down state, this
5381 * queue of leases has their tsfp values modified
5382 * to equal stos+mclt, the point at which the server
5383 * is allowed to remove them from these transitional
5384 * states without an acknowledgement.
5386 * Note that although tsfp has been possibly extended
5387 * past the actual tsfp we received from the peer, we
5388 * don't have to take any special action. Since tsfp
5389 * is now in the past (or now), we can guarantee that
5390 * this server will only allocate a lease time equal
5391 * to MCLT, rather than a TSFP-optimal lease, which is
5392 * the only danger for a lease in one of these states.
5394 return((peer
->service_state
== service_partner_down
) &&
5395 (lease
->tsfp
< cur_time
));
5398 /* Only the secondary may allocate BACKUP leases,
5399 * unless in partner_down state in which case at
5400 * least TSFP+MCLT or STOS+MCLT must have expired,
5401 * whichever is greater.
5403 if (peer
->i_am
== secondary
)
5406 return((peer
->service_state
== service_partner_down
) &&
5407 ((lease
->tsfp
< peer
->me
.stos
) ?
5408 (peer
->me
.stos
+ peer
->mclt
< cur_time
) :
5409 (lease
->tsfp
+ peer
->mclt
< cur_time
)));
5412 /* All lease states appear above. */
5413 log_fatal("Impossible case at %s:%d.", MDL
);
5419 return(lease
->binding_state
== FTS_FREE
||
5420 lease
->binding_state
== FTS_BACKUP
);
5425 static isc_result_t
failover_message_reference (failover_message_t
**mp
,
5426 failover_message_t
*m
,
5427 const char *file
, int line
)
5431 return ISC_R_SUCCESS
;
5434 static isc_result_t
failover_message_dereference (failover_message_t
**mp
,
5435 const char *file
, int line
)
5437 failover_message_t
*m
;
5440 if (m
-> refcnt
== 0) {
5442 failover_message_dereference (&m
-> next
,
5444 if (m
-> chaddr
.data
)
5445 dfree (m
-> chaddr
.data
, file
, line
);
5446 if (m
-> client_identifier
.data
)
5447 dfree (m
-> client_identifier
.data
, file
, line
);
5449 dfree (m
-> hba
.data
, file
, line
);
5450 if (m
-> message
.data
)
5451 dfree (m
-> message
.data
, file
, line
);
5452 if (m
-> reply_options
.data
)
5453 dfree (m
-> reply_options
.data
, file
, line
);
5454 if (m
-> request_options
.data
)
5455 dfree (m
-> request_options
.data
, file
, line
);
5456 if (m
-> vendor_class
.data
)
5457 dfree (m
-> vendor_class
.data
, file
, line
);
5458 if (m
-> vendor_options
.data
)
5459 dfree (m
-> vendor_options
.data
, file
, line
);
5461 dfree (m
-> ddns
.data
, file
, line
);
5462 dfree (*mp
, file
, line
);
5465 return ISC_R_SUCCESS
;
5468 OMAPI_OBJECT_ALLOC (dhcp_failover_state
, dhcp_failover_state_t
,
5469 dhcp_type_failover_state
)
5470 OMAPI_OBJECT_ALLOC (dhcp_failover_listener
, dhcp_failover_listener_t
,
5471 dhcp_type_failover_listener
)
5472 OMAPI_OBJECT_ALLOC (dhcp_failover_link
, dhcp_failover_link_t
,
5473 dhcp_type_failover_link
)
5474 #endif /* defined (FAILOVER_PROTOCOL) */
5476 const char *binding_state_print (enum failover_state state
)