3 Failover protocol support code... */
6 * Copyright (c) 2004 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.53.2.35 2004/11/24 17:39:19 dhankins Exp $ Copyright (c) 2004 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 if (p
-> next_event_time
>
1789 state
-> me
.stos
+ state
-> mclt
) {
1790 p
-> next_event_time
=
1791 state
-> me
.stos
+ state
-> mclt
;
1792 #if defined (DEBUG_FAILOVER_TIMING)
1793 log_info ("add_timeout +%d %s",
1794 (int)(cur_time
- p
-> next_event_time
),
1797 add_timeout (p
-> next_event_time
, pool_timer
, p
,
1798 (tvref_t
)pool_reference
,
1799 (tvunref_t
)pool_dereference
);
1811 return ISC_R_SUCCESS
;
1814 isc_result_t
dhcp_failover_peer_state_changed (dhcp_failover_state_t
*state
,
1815 failover_message_t
*msg
)
1817 enum failover_state previous_state
= state
-> partner
.state
;
1818 enum failover_state new_state
;
1820 isc_result_t status
;
1822 new_state
= msg
-> server_state
;
1823 startupp
= (msg
-> server_flags
& FTF_STARTUP
) ? 1 : 0;
1825 if (state
-> partner
.state
== new_state
&& state
-> me
.state
) {
1826 switch (state
-> me
.state
) {
1828 dhcp_failover_set_state (state
, state
-> saved_state
);
1829 return ISC_R_SUCCESS
;
1833 case potential_conflict
:
1838 return ISC_R_SUCCESS
;
1840 /* If we get a peer state change when we're
1841 disconnected, we always process it. */
1843 case communications_interrupted
:
1844 case resolution_interrupted
:
1850 state
-> partner
.state
= new_state
;
1852 log_info ("failover peer %s: peer moves from %s to %s",
1854 dhcp_failover_state_name_print (previous_state
),
1855 dhcp_failover_state_name_print (state
-> partner
.state
));
1857 if (!write_failover_state (state
) || !commit_leases ()) {
1858 /* This is bad, but it's not fatal. Of course, if we
1859 can't write to the lease database, we're not going to
1860 get much done anyway. */
1861 log_error ("Unable to record current failover state for %s",
1865 /* Do any state transitions that are required as a result of the
1866 peer's state transition. */
1868 switch (state
-> me
.state
== startup
?
1869 state
-> saved_state
: state
-> me
.state
) {
1870 case startup
: /* can't happen. */
1874 switch (new_state
) {
1876 dhcp_failover_state_pool_check (state
);
1879 case communications_interrupted
:
1883 if (state
-> me
.state
== startup
)
1884 dhcp_failover_set_state (state
, recover
);
1886 dhcp_failover_set_state (state
,
1887 potential_conflict
);
1890 case potential_conflict
:
1891 case resolution_interrupted
:
1892 /* None of these transitions should ever occur. */
1893 dhcp_failover_set_state (state
, shut_down
);
1897 dhcp_failover_set_state (state
, partner_down
);
1901 /* XXX This one is specified, but it's specified in
1902 XXX the documentation for the shut_down state,
1903 XXX not the normal state. */
1904 dhcp_failover_set_state (state
, partner_down
);
1908 dhcp_failover_set_state (state
,
1909 communications_interrupted
);
1914 /* We probably don't need to do anything here. */
1924 switch (new_state
) {
1926 log_info ("failover peer %s: requesting %s",
1927 state
-> name
, "full update from peer");
1928 /* Don't send updreqall if we're really in the
1929 startup state, because that will result in two
1931 if (state
-> me
.state
== recover
)
1932 dhcp_failover_send_update_request_all (state
);
1935 case potential_conflict
:
1936 case resolution_interrupted
:
1938 dhcp_failover_set_state (state
, potential_conflict
);
1942 case communications_interrupted
:
1943 /* We're supposed to send an update request at this
1945 /* XXX we don't currently have code here to do any
1946 XXX clever detection of when we should send an
1947 XXX UPDREQALL message rather than an UPDREQ
1948 XXX message. What to do, what to do? */
1949 dhcp_failover_send_update_request (state
);
1953 /* XXX We're not explicitly told what to do in this
1954 XXX case, but this transition is consistent with
1955 XXX what is elsewhere in the draft. */
1956 dhcp_failover_set_state (state
, partner_down
);
1959 /* We can't really do anything in this case. */
1963 /* We should have asked for an update already. */
1974 case potential_conflict
:
1975 switch (new_state
) {
1977 if (previous_state
== potential_conflict
&&
1978 state
-> i_am
== secondary
)
1979 dhcp_failover_send_update_request (state
);
1984 case potential_conflict
:
1986 case communications_interrupted
:
1987 case resolution_interrupted
:
1992 dhcp_failover_set_state (state
, recover
);
1996 dhcp_failover_set_state (state
, partner_down
);
2006 /* Take no action if other server is starting up. */
2010 switch (new_state
) {
2011 /* This is where we should be. */
2017 dhcp_failover_set_state (state
, normal
);
2021 case potential_conflict
:
2023 case communications_interrupted
:
2024 case resolution_interrupted
:
2025 dhcp_failover_set_state (state
, potential_conflict
);
2028 /* These don't change anything. */
2039 case communications_interrupted
:
2040 switch (new_state
) {
2042 /* Stick with the status quo. */
2045 /* If we're in communications-interrupted and an
2046 amnesiac peer connects, go to the partner_down
2047 state immediately. */
2049 dhcp_failover_set_state (state
, partner_down
);
2053 case communications_interrupted
:
2056 /* XXX so we don't need to do this specially in
2057 XXX the CONNECT and CONNECTACK handlers. */
2058 dhcp_failover_send_updates (state
);
2059 dhcp_failover_set_state (state
, normal
);
2062 case potential_conflict
:
2064 case resolution_interrupted
:
2065 dhcp_failover_set_state (state
, potential_conflict
);
2069 dhcp_failover_set_state (state
, partner_down
);
2078 case resolution_interrupted
:
2079 switch (new_state
) {
2082 case potential_conflict
:
2084 case communications_interrupted
:
2085 case resolution_interrupted
:
2088 dhcp_failover_set_state (state
, potential_conflict
);
2092 dhcp_failover_set_state (state
, partner_down
);
2105 switch (new_state
) {
2108 dhcp_failover_set_state (state
, normal
);
2111 case potential_conflict
:
2113 case communications_interrupted
:
2114 case resolution_interrupted
:
2121 dhcp_failover_set_state (state
, partner_down
);
2130 /* We are essentially dead in the water when we're in
2131 either shut_down or paused states, and do not do any
2132 automatic state transitions. */
2137 /* We still have to wait... */
2145 /* If we didn't make a transition out of startup as a result of
2146 the peer's state change, do it now as a result of the fact that
2147 we got a state change from the peer. */
2148 if (state
-> me
.state
== startup
&& state
-> saved_state
!= startup
)
2149 dhcp_failover_set_state (state
, state
-> saved_state
);
2151 /* For now, just set the service state based on the peer's state
2153 dhcp_failover_set_service_state (state
);
2155 return ISC_R_SUCCESS
;
2158 int dhcp_failover_pool_rebalance (dhcp_failover_state_t
*state
)
2161 int leases_queued
= 0;
2162 struct lease
*lp
= (struct lease
*)0;
2163 struct lease
*next
= (struct lease
*)0;
2164 struct shared_network
*s
;
2167 binding_state_t peer_lease_state
;
2168 binding_state_t my_lease_state
;
2172 if (state
-> me
.state
!= normal
|| state
-> i_am
== secondary
)
2175 for (s
= shared_networks
; s
; s
= s
-> next
) {
2176 for (p
= s
-> pools
; p
; p
= p
-> next
) {
2177 if (p
-> failover_peer
!= state
)
2180 /* Right now we're giving the peer half of the free leases.
2181 If we have more leases than the peer (i.e., more than
2182 half), then the number of leases we have, less the number
2183 of leases the peer has, will be how many more leases we
2184 have than the peer has. So if we send half that number
2185 to the peer, we should be even. */
2186 if (p
-> failover_peer
-> i_am
== primary
) {
2187 lts
= (p
-> free_leases
- p
-> backup_leases
) / 2;
2188 peer_lease_state
= FTS_BACKUP
;
2189 my_lease_state
= FTS_FREE
;
2192 lts
= (p
-> backup_leases
- p
-> free_leases
) / 2;
2193 peer_lease_state
= FTS_FREE
;
2194 my_lease_state
= FTS_BACKUP
;
2198 tenper
= (p
-> backup_leases
+ p
-> free_leases
) / 10;
2202 log_info ("pool %lx %s total %d free %d %s %d lts %d",
2204 (p
-> shared_network
?
2205 p
-> shared_network
-> name
: ""), p
-> lease_count
,
2206 p
-> free_leases
, "backup", p
-> backup_leases
, lts
);
2208 lease_reference (&lp
, *lq
, MDL
);
2211 /* Remember the next lease in the list. */
2213 lease_dereference (&next
, MDL
);
2215 lease_reference (&next
, lp
-> next
, MDL
);
2219 lp
-> next_binding_state
= peer_lease_state
;
2220 lp
-> tstp
= cur_time
;
2221 lp
-> starts
= cur_time
;
2223 if (!supersede_lease (lp
, (struct lease
*)0, 0, 1, 0)
2224 || !write_lease (lp
))
2226 log_info ("can't commit lease %s on giveaway",
2227 piaddr (lp
-> ip_addr
));
2230 lease_dereference (&lp
, MDL
);
2232 lease_reference (&lp
, next
, MDL
);
2235 lease_dereference (&next
, MDL
);
2237 lease_dereference (&lp
, MDL
);
2241 log_info ("lease imbalance - lts = %d", lts
);
2246 dhcp_failover_send_poolresp (state
, leases_queued
);
2247 dhcp_failover_send_updates (state
);
2248 return leases_queued
;
2251 int dhcp_failover_pool_check (struct pool
*pool
)
2257 if (!pool
-> failover_peer
||
2258 pool
-> failover_peer
-> me
.state
!= normal
)
2261 if (pool
-> failover_peer
-> i_am
== primary
)
2262 lts
= (pool
-> backup_leases
- pool
-> free_leases
) / 2;
2264 lts
= (pool
-> free_leases
- pool
-> backup_leases
) / 2;
2266 log_info ("pool %lx %s total %d free %d backup %d lts %d",
2267 (unsigned long)pool
,
2268 pool
-> shared_network
? pool
-> shared_network
-> name
: "",
2269 pool
-> lease_count
,
2270 pool
-> free_leases
, pool
-> backup_leases
, lts
);
2272 tenper
= (pool
-> backup_leases
+ pool
-> free_leases
) / 10;
2276 /* XXX What about multiple pools? */
2277 if (pool
-> failover_peer
-> i_am
== secondary
) {
2278 /* Ask the primary to send us leases. */
2279 dhcp_failover_send_poolreq (pool
-> failover_peer
);
2282 /* Figure out how many leases to skip on the backup
2283 list. We skip the earliest leases on the list
2284 to reduce the chance of trying to steal a lease
2285 that the secondary is about to allocate. */
2286 int i
= pool
-> backup_leases
- lts
;
2287 log_info ("Taking %d leases from secondary.", lts
);
2288 for (lp
= pool
-> backup
; lp
; lp
= lp
-> next
) {
2289 /* Skip to the last leases on the free
2290 list, because they are less likely
2291 to already have been allocated. */
2295 lp
-> desired_binding_state
= FTS_FREE
;
2296 dhcp_failover_queue_update (lp
, 1);
2301 log_info ("failed to take %d leases.", lts
);
2307 int dhcp_failover_state_pool_check (dhcp_failover_state_t
*state
)
2310 struct shared_network
*s
;
2313 for (s
= shared_networks
; s
; s
= s
-> next
) {
2314 for (p
= s
-> pools
; p
; p
= p
-> next
) {
2315 if (p
-> failover_peer
!= state
)
2317 /* Only need to request rebalance on one pool. */
2318 if (dhcp_failover_pool_check (p
))
2325 isc_result_t
dhcp_failover_send_updates (dhcp_failover_state_t
*state
)
2327 struct lease
*lp
= (struct lease
*)0;
2328 isc_result_t status
;
2330 /* Can't update peer if we're not talking to it! */
2331 if (!state
-> link_to_peer
)
2332 return ISC_R_SUCCESS
;
2334 while ((state
-> partner
.max_flying_updates
>
2335 state
-> cur_unacked_updates
) && state
-> update_queue_head
) {
2336 /* Grab the head of the update queue. */
2337 lease_reference (&lp
, state
-> update_queue_head
, MDL
);
2339 /* Send the update to the peer. */
2340 status
= dhcp_failover_send_bind_update (state
, lp
);
2341 if (status
!= ISC_R_SUCCESS
) {
2342 lease_dereference (&lp
, MDL
);
2345 lp
-> flags
&= ~ON_UPDATE_QUEUE
;
2347 /* Take it off the head of the update queue and put the next
2348 item in the update queue at the head. */
2349 lease_dereference (&state
-> update_queue_head
, MDL
);
2350 if (lp
-> next_pending
) {
2351 lease_reference (&state
-> update_queue_head
,
2352 lp
-> next_pending
, MDL
);
2353 lease_dereference (&lp
-> next_pending
, MDL
);
2355 lease_dereference (&state
-> update_queue_tail
, MDL
);
2358 if (state
-> ack_queue_head
) {
2360 (&state
-> ack_queue_tail
-> next_pending
,
2362 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2364 lease_reference (&state
-> ack_queue_head
, lp
, MDL
);
2366 #if defined (POINTER_DEBUG)
2367 if (lp
-> next_pending
) {
2368 log_error ("ack_queue_tail: lp -> next_pending");
2372 lease_reference (&state
-> ack_queue_tail
, lp
, MDL
);
2373 lp
-> flags
|= ON_ACK_QUEUE
;
2374 lease_dereference (&lp
, MDL
);
2376 /* Count the object as an unacked update. */
2377 state
-> cur_unacked_updates
++;
2379 return ISC_R_SUCCESS
;
2382 /* Queue an update for a lease. Always returns 1 at this point - it's
2383 not an error for this to be called on a lease for which there's no
2386 int dhcp_failover_queue_update (struct lease
*lease
, int immediate
)
2388 dhcp_failover_state_t
*state
;
2390 if (!lease
-> pool
||
2391 !lease
-> pool
-> failover_peer
)
2394 /* If it's already on the update queue, leave it there. */
2395 if (lease
-> flags
& ON_UPDATE_QUEUE
)
2398 /* Get the failover state structure for this lease. */
2399 state
= lease
-> pool
-> failover_peer
;
2401 /* If it's on the ack queue, take it off. */
2402 if (lease
-> flags
& ON_ACK_QUEUE
)
2403 dhcp_failover_ack_queue_remove (state
, lease
);
2405 if (state
-> update_queue_head
) {
2406 lease_reference (&state
-> update_queue_tail
-> next_pending
,
2408 lease_dereference (&state
-> update_queue_tail
, MDL
);
2410 lease_reference (&state
-> update_queue_head
, lease
, MDL
);
2412 #if defined (POINTER_DEBUG)
2413 if (lease
-> next_pending
) {
2414 log_error ("next pending on update queue lease.");
2415 #if defined (DEBUG_RC_HISTORY)
2416 dump_rc_history (lease
);
2421 lease_reference (&state
-> update_queue_tail
, lease
, MDL
);
2422 lease
-> flags
|= ON_UPDATE_QUEUE
;
2424 dhcp_failover_send_updates (state
);
2428 int dhcp_failover_send_acks (dhcp_failover_state_t
*state
)
2430 failover_message_t
*msg
= (failover_message_t
*)0;
2432 /* Must commit all leases prior to acking them. */
2433 if (!commit_leases ())
2436 while (state
-> toack_queue_head
) {
2437 failover_message_reference
2438 (&msg
, state
-> toack_queue_head
, MDL
);
2439 failover_message_dereference
2440 (&state
-> toack_queue_head
, MDL
);
2442 failover_message_reference
2443 (&state
-> toack_queue_head
, msg
-> next
, MDL
);
2446 dhcp_failover_send_bind_ack (state
, msg
, 0, (const char *)0);
2448 failover_message_dereference (&msg
, MDL
);
2451 if (state
-> toack_queue_tail
)
2452 failover_message_dereference (&state
-> toack_queue_tail
, MDL
);
2453 state
-> pending_acks
= 0;
2458 void dhcp_failover_toack_queue_timeout (void *vs
)
2460 dhcp_failover_state_t
*state
= vs
;
2462 #if defined (DEBUG_FAILOVER_TIMING)
2463 log_info ("dhcp_failover_toack_queue_timeout");
2466 dhcp_failover_send_acks (state
);
2469 /* Queue an ack for a message. There is currently no way to queue a
2470 negative ack -- these need to be sent directly. */
2472 int dhcp_failover_queue_ack (dhcp_failover_state_t
*state
,
2473 failover_message_t
*msg
)
2475 if (state
-> toack_queue_head
) {
2476 failover_message_reference
2477 (&state
-> toack_queue_tail
-> next
, msg
, MDL
);
2478 failover_message_dereference (&state
-> toack_queue_tail
, MDL
);
2480 failover_message_reference (&state
-> toack_queue_head
,
2483 failover_message_reference (&state
-> toack_queue_tail
, msg
, MDL
);
2485 state
-> pending_acks
++;
2487 /* Flush the toack queue whenever we exceed half the number of
2488 allowed unacked updates. */
2489 if (state
-> pending_acks
>= state
-> partner
.max_flying_updates
/ 2) {
2490 dhcp_failover_send_acks (state
);
2493 /* Schedule a timeout to flush the ack queue. */
2494 if (state
-> pending_acks
> 0) {
2495 #if defined (DEBUG_FAILOVER_TIMING)
2496 log_info ("add_timeout +2 %s",
2497 "dhcp_failover_toack_queue_timeout");
2499 add_timeout (cur_time
+ 2,
2500 dhcp_failover_toack_queue_timeout
, state
,
2501 (tvref_t
)dhcp_failover_state_reference
,
2502 (tvunref_t
)dhcp_failover_state_dereference
);
2508 void dhcp_failover_ack_queue_remove (dhcp_failover_state_t
*state
,
2509 struct lease
*lease
)
2513 if (!(lease
-> flags
& ON_ACK_QUEUE
))
2516 if (state
-> ack_queue_head
== lease
) {
2517 lease_dereference (&state
-> ack_queue_head
, MDL
);
2518 if (lease
-> next_pending
) {
2519 lease_reference (&state
-> ack_queue_head
,
2520 lease
-> next_pending
, MDL
);
2521 lease_dereference (&lease
-> next_pending
, MDL
);
2523 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2526 for (lp
= state
-> ack_queue_head
;
2527 lp
&& lp
-> next_pending
!= lease
;
2528 lp
= lp
-> next_pending
)
2534 lease_dereference (&lp
-> next_pending
, MDL
);
2535 if (lease
-> next_pending
) {
2536 lease_reference (&lp
-> next_pending
,
2537 lease
-> next_pending
, MDL
);
2538 lease_dereference (&lease
-> next_pending
, MDL
);
2540 lease_dereference (&state
-> ack_queue_tail
, MDL
);
2541 if (lp
-> next_pending
) {
2542 log_error ("state -> ack_queue_tail");
2545 lease_reference (&state
-> ack_queue_tail
, lp
, MDL
);
2549 lease
-> flags
&= ~ON_ACK_QUEUE
;
2550 state
-> cur_unacked_updates
--;
2553 * When updating leases as a result of an ack, we defer the commit
2554 * for performance reasons. When there are no more acks pending,
2557 if (state
-> cur_unacked_updates
== 0) {
2562 isc_result_t
dhcp_failover_state_set_value (omapi_object_t
*h
,
2564 omapi_data_string_t
*name
,
2565 omapi_typed_data_t
*value
)
2567 isc_result_t status
;
2569 if (h
-> type
!= dhcp_type_failover_state
)
2570 return ISC_R_INVALIDARG
;
2572 /* This list of successful returns is completely wrong, but the
2573 fastest way to make dhcpctl do something vaguely sane when
2574 you try to change the local state. */
2576 if (!omapi_ds_strcmp (name
, "name")) {
2577 return ISC_R_SUCCESS
;
2578 } else if (!omapi_ds_strcmp (name
, "partner-address")) {
2579 return ISC_R_SUCCESS
;
2580 } else if (!omapi_ds_strcmp (name
, "local-address")) {
2581 return ISC_R_SUCCESS
;
2582 } else if (!omapi_ds_strcmp (name
, "partner-port")) {
2583 return ISC_R_SUCCESS
;
2584 } else if (!omapi_ds_strcmp (name
, "local-port")) {
2585 return ISC_R_SUCCESS
;
2586 } else if (!omapi_ds_strcmp (name
, "max-outstanding-updates")) {
2587 return ISC_R_SUCCESS
;
2588 } else if (!omapi_ds_strcmp (name
, "mclt")) {
2589 return ISC_R_SUCCESS
;
2590 } else if (!omapi_ds_strcmp (name
, "load-balance-max-secs")) {
2591 return ISC_R_SUCCESS
;
2592 } else if (!omapi_ds_strcmp (name
, "load-balance-hba")) {
2593 return ISC_R_SUCCESS
;
2594 } else if (!omapi_ds_strcmp (name
, "partner-state")) {
2595 return ISC_R_SUCCESS
;
2596 } else if (!omapi_ds_strcmp (name
, "local-state")) {
2598 status
= omapi_get_int_value (&l
, value
);
2599 if (status
!= ISC_R_SUCCESS
)
2601 return dhcp_failover_set_state ((dhcp_failover_state_t
*)h
, l
);
2602 } else if (!omapi_ds_strcmp (name
, "partner-stos")) {
2603 return ISC_R_SUCCESS
;
2604 } else if (!omapi_ds_strcmp (name
, "local-stos")) {
2605 return ISC_R_SUCCESS
;
2606 } else if (!omapi_ds_strcmp (name
, "hierarchy")) {
2607 return ISC_R_SUCCESS
;
2608 } else if (!omapi_ds_strcmp (name
, "last-packet-sent")) {
2609 return ISC_R_SUCCESS
;
2610 } else if (!omapi_ds_strcmp (name
, "last-timestamp-received")) {
2611 return ISC_R_SUCCESS
;
2612 } else if (!omapi_ds_strcmp (name
, "skew")) {
2613 return ISC_R_SUCCESS
;
2614 } else if (!omapi_ds_strcmp (name
, "max-response-delay")) {
2615 return ISC_R_SUCCESS
;
2616 } else if (!omapi_ds_strcmp (name
, "cur-unacked-updates")) {
2617 return ISC_R_SUCCESS
;
2620 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
2621 return (*(h
-> inner
-> type
-> set_value
))
2622 (h
-> inner
, id
, name
, value
);
2623 return ISC_R_NOTFOUND
;
2626 void dhcp_failover_keepalive (void *vs
)
2628 dhcp_failover_state_t
*state
= vs
;
2631 void dhcp_failover_reconnect (void *vs
)
2633 dhcp_failover_state_t
*state
= vs
;
2634 isc_result_t status
;
2636 #if defined (DEBUG_FAILOVER_TIMING)
2637 log_info ("dhcp_failover_reconnect");
2639 /* If we already connected the other way, let the connection
2640 recovery code initiate any retry that may be required. */
2641 if (state
-> link_to_peer
)
2644 status
= dhcp_failover_link_initiate ((omapi_object_t
*)state
);
2645 if (status
!= ISC_R_SUCCESS
&& status
!= ISC_R_INCOMPLETE
) {
2646 log_info ("failover peer %s: %s", state
-> name
,
2647 isc_result_totext (status
));
2648 #if defined (DEBUG_FAILOVER_TIMING)
2649 log_info ("add_timeout +90 %s",
2650 "dhcp_failover_listener_restart");
2652 add_timeout (cur_time
+ 90,
2653 dhcp_failover_listener_restart
, state
,
2654 (tvref_t
)dhcp_failover_state_reference
,
2655 (tvunref_t
)dhcp_failover_state_dereference
);
2659 void dhcp_failover_startup_timeout (void *vs
)
2661 dhcp_failover_state_t
*state
= vs
;
2662 isc_result_t status
;
2664 #if defined (DEBUG_FAILOVER_TIMING)
2665 log_info ("dhcp_failover_startup_timeout");
2668 dhcp_failover_state_transition (state
, "disconnect");
2671 void dhcp_failover_link_startup_timeout (void *vl
)
2673 dhcp_failover_link_t
*link
= vl
;
2674 isc_result_t status
;
2677 for (p
= (omapi_object_t
*)link
; p
-> inner
; p
= p
-> inner
)
2679 for (; p
; p
= p
-> outer
)
2680 if (p
-> type
== omapi_type_connection
)
2683 log_info ("failover: link startup timeout");
2684 omapi_disconnect (p
, 1);
2688 void dhcp_failover_listener_restart (void *vs
)
2690 dhcp_failover_state_t
*state
= vs
;
2691 isc_result_t status
;
2693 #if defined (DEBUG_FAILOVER_TIMING)
2694 log_info ("dhcp_failover_listener_restart");
2697 status
= dhcp_failover_listen ((omapi_object_t
*)state
);
2698 if (status
!= ISC_R_SUCCESS
) {
2699 log_info ("failover peer %s: %s", state
-> name
,
2700 isc_result_totext (status
));
2701 #if defined (DEBUG_FAILOVER_TIMING)
2702 log_info ("add_timeout +90 %s",
2703 "dhcp_failover_listener_restart");
2705 add_timeout (cur_time
+ 90,
2706 dhcp_failover_listener_restart
, state
,
2707 (tvref_t
)dhcp_failover_state_reference
,
2708 (tvunref_t
)dhcp_failover_state_dereference
);
2712 isc_result_t
dhcp_failover_state_get_value (omapi_object_t
*h
,
2714 omapi_data_string_t
*name
,
2715 omapi_value_t
**value
)
2717 dhcp_failover_state_t
*s
;
2718 struct option_cache
*oc
;
2719 struct data_string ds
;
2720 isc_result_t status
;
2722 if (h
-> type
!= dhcp_type_failover_state
)
2723 return ISC_R_INVALIDARG
;
2724 s
= (dhcp_failover_state_t
*)h
;
2726 if (!omapi_ds_strcmp (name
, "name")) {
2728 return omapi_make_string_value (value
,
2729 name
, s
-> name
, MDL
);
2730 return ISC_R_NOTFOUND
;
2731 } else if (!omapi_ds_strcmp (name
, "partner-address")) {
2732 oc
= s
-> partner
.address
;
2734 memset (&ds
, 0, sizeof ds
);
2735 if (!evaluate_option_cache (&ds
, (struct packet
*)0,
2737 (struct client_state
*)0,
2738 (struct option_state
*)0,
2739 (struct option_state
*)0,
2740 &global_scope
, oc
, MDL
)) {
2741 return ISC_R_NOTFOUND
;
2743 status
= omapi_make_const_value (value
,
2744 name
, ds
.data
, ds
.len
, MDL
);
2745 /* Disgusting kludge: */
2746 if (oc
== s
-> me
.address
&& !s
-> server_identifier
.len
)
2747 data_string_copy (&s
-> server_identifier
, &ds
, MDL
);
2748 data_string_forget (&ds
, MDL
);
2750 } else if (!omapi_ds_strcmp (name
, "local-address")) {
2751 oc
= s
-> me
.address
;
2753 } else if (!omapi_ds_strcmp (name
, "partner-port")) {
2754 return omapi_make_int_value (value
, name
,
2755 s
-> partner
.port
, MDL
);
2756 } else if (!omapi_ds_strcmp (name
, "local-port")) {
2757 return omapi_make_int_value (value
,
2758 name
, s
-> me
.port
, MDL
);
2759 } else if (!omapi_ds_strcmp (name
, "max-outstanding-updates")) {
2760 return omapi_make_uint_value (value
, name
,
2761 s
-> me
.max_flying_updates
,
2763 } else if (!omapi_ds_strcmp (name
, "mclt")) {
2764 return omapi_make_uint_value (value
, name
, s
-> mclt
, MDL
);
2765 } else if (!omapi_ds_strcmp (name
, "load-balance-max-secs")) {
2766 return omapi_make_int_value (value
, name
,
2767 s
-> load_balance_max_secs
, MDL
);
2768 } else if (!omapi_ds_strcmp (name
, "load-balance-hba")) {
2770 return omapi_make_const_value (value
, name
,
2772 return ISC_R_NOTFOUND
;
2773 } else if (!omapi_ds_strcmp (name
, "partner-state")) {
2774 return omapi_make_uint_value (value
, name
,
2775 s
-> partner
.state
, MDL
);
2776 } else if (!omapi_ds_strcmp (name
, "local-state")) {
2777 return omapi_make_uint_value (value
, name
,
2778 s
-> me
.state
, MDL
);
2779 } else if (!omapi_ds_strcmp (name
, "partner-stos")) {
2780 return omapi_make_int_value (value
, name
,
2781 s
-> partner
.stos
, MDL
);
2782 } else if (!omapi_ds_strcmp (name
, "local-stos")) {
2783 return omapi_make_int_value (value
, name
,
2785 } else if (!omapi_ds_strcmp (name
, "hierarchy")) {
2786 return omapi_make_uint_value (value
, name
, s
-> i_am
, MDL
);
2787 } else if (!omapi_ds_strcmp (name
, "last-packet-sent")) {
2788 return omapi_make_int_value (value
, name
,
2789 s
-> last_packet_sent
, MDL
);
2790 } else if (!omapi_ds_strcmp (name
, "last-timestamp-received")) {
2791 return omapi_make_int_value (value
, name
,
2792 s
-> last_timestamp_received
,
2794 } else if (!omapi_ds_strcmp (name
, "skew")) {
2795 return omapi_make_int_value (value
, name
, s
-> skew
, MDL
);
2796 } else if (!omapi_ds_strcmp (name
, "max-response-delay")) {
2797 return omapi_make_uint_value (value
, name
,
2798 s
-> me
.max_response_delay
,
2800 } else if (!omapi_ds_strcmp (name
, "cur-unacked-updates")) {
2801 return omapi_make_int_value (value
, name
,
2802 s
-> cur_unacked_updates
, MDL
);
2805 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
2806 return (*(h
-> inner
-> type
-> get_value
))
2807 (h
-> inner
, id
, name
, value
);
2808 return ISC_R_NOTFOUND
;
2811 isc_result_t
dhcp_failover_state_destroy (omapi_object_t
*h
,
2812 const char *file
, int line
)
2814 dhcp_failover_state_t
*s
;
2816 if (h
-> type
!= dhcp_type_failover_state
)
2817 return ISC_R_INVALIDARG
;
2818 s
= (dhcp_failover_state_t
*)h
;
2820 if (s
-> link_to_peer
)
2821 dhcp_failover_link_dereference (&s
-> link_to_peer
, file
, line
);
2823 dfree (s
-> name
, MDL
);
2824 s
-> name
= (char *)0;
2826 if (s
-> partner
.address
)
2827 option_cache_dereference (&s
-> partner
.address
, file
, line
);
2828 if (s
-> me
.address
)
2829 option_cache_dereference (&s
-> me
.address
, file
, line
);
2831 dfree (s
-> hba
, file
, line
);
2832 s
-> hba
= (u_int8_t
*)0;
2834 if (s
-> update_queue_head
)
2835 lease_dereference (&s
-> update_queue_head
, file
, line
);
2836 if (s
-> update_queue_tail
)
2837 lease_dereference (&s
-> update_queue_tail
, file
, line
);
2838 if (s
-> ack_queue_head
)
2839 lease_dereference (&s
-> ack_queue_head
, file
, line
);
2840 if (s
-> ack_queue_tail
)
2841 lease_dereference (&s
-> ack_queue_tail
, file
, line
);
2842 if (s
-> send_update_done
)
2843 lease_dereference (&s
-> send_update_done
, file
, line
);
2844 if (s
-> toack_queue_head
)
2845 failover_message_dereference (&s
-> toack_queue_head
,
2847 if (s
-> toack_queue_tail
)
2848 failover_message_dereference (&s
-> toack_queue_tail
,
2850 return ISC_R_SUCCESS
;
2853 /* Write all the published values associated with the object through the
2854 specified connection. */
2856 isc_result_t
dhcp_failover_state_stuff (omapi_object_t
*c
,
2860 dhcp_failover_state_t
*s
;
2861 omapi_connection_object_t
*conn
;
2862 isc_result_t status
;
2864 if (c
-> type
!= omapi_type_connection
)
2865 return ISC_R_INVALIDARG
;
2866 conn
= (omapi_connection_object_t
*)c
;
2868 if (h
-> type
!= dhcp_type_failover_state
)
2869 return ISC_R_INVALIDARG
;
2870 s
= (dhcp_failover_state_t
*)h
;
2872 status
= omapi_connection_put_name (c
, "name");
2873 if (status
!= ISC_R_SUCCESS
)
2875 status
= omapi_connection_put_string (c
, s
-> name
);
2876 if (status
!= ISC_R_SUCCESS
)
2879 status
= omapi_connection_put_name (c
, "partner-address");
2880 if (status
!= ISC_R_SUCCESS
)
2882 status
= omapi_connection_put_uint32 (c
, sizeof s
-> partner
.address
);
2883 if (status
!= ISC_R_SUCCESS
)
2885 status
= omapi_connection_copyin (c
, (u_int8_t
*)&s
-> partner
.address
,
2886 sizeof s
-> partner
.address
);
2887 if (status
!= ISC_R_SUCCESS
)
2890 status
= omapi_connection_put_name (c
, "partner-port");
2891 if (status
!= ISC_R_SUCCESS
)
2893 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
2894 if (status
!= ISC_R_SUCCESS
)
2896 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> partner
.port
);
2897 if (status
!= ISC_R_SUCCESS
)
2900 status
= omapi_connection_put_name (c
, "local-address");
2901 if (status
!= ISC_R_SUCCESS
)
2903 status
= omapi_connection_put_uint32 (c
, sizeof s
-> me
.address
);
2904 if (status
!= ISC_R_SUCCESS
)
2906 status
= omapi_connection_copyin (c
, (u_int8_t
*)&s
-> me
.address
,
2907 sizeof s
-> me
.address
);
2908 if (status
!= ISC_R_SUCCESS
)
2911 status
= omapi_connection_put_name (c
, "local-port");
2912 if (status
!= ISC_R_SUCCESS
)
2914 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
2915 if (status
!= ISC_R_SUCCESS
)
2917 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> me
.port
);
2918 if (status
!= ISC_R_SUCCESS
)
2921 status
= omapi_connection_put_name (c
, "max-outstanding-updates");
2922 if (status
!= ISC_R_SUCCESS
)
2924 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
2925 if (status
!= ISC_R_SUCCESS
)
2927 status
= omapi_connection_put_uint32 (c
,
2928 s
-> me
.max_flying_updates
);
2929 if (status
!= ISC_R_SUCCESS
)
2932 status
= omapi_connection_put_name (c
, "mclt");
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
, s
-> mclt
);
2939 if (status
!= ISC_R_SUCCESS
)
2942 status
= omapi_connection_put_name (c
, "load-balance-max-secs");
2943 if (status
!= ISC_R_SUCCESS
)
2945 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
2946 if (status
!= ISC_R_SUCCESS
)
2948 status
= (omapi_connection_put_uint32
2949 (c
, (u_int32_t
)s
-> load_balance_max_secs
));
2950 if (status
!= ISC_R_SUCCESS
)
2955 status
= omapi_connection_put_name (c
, "load-balance-hba");
2956 if (status
!= ISC_R_SUCCESS
)
2958 status
= omapi_connection_put_uint32 (c
, 32);
2959 if (status
!= ISC_R_SUCCESS
)
2961 status
= omapi_connection_copyin (c
, s
-> hba
, 32);
2962 if (status
!= ISC_R_SUCCESS
)
2966 status
= omapi_connection_put_name (c
, "partner-state");
2967 if (status
!= ISC_R_SUCCESS
)
2969 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
2970 if (status
!= ISC_R_SUCCESS
)
2972 status
= omapi_connection_put_uint32 (c
, s
-> partner
.state
);
2973 if (status
!= ISC_R_SUCCESS
)
2976 status
= omapi_connection_put_name (c
, "local-state");
2977 if (status
!= ISC_R_SUCCESS
)
2979 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
2980 if (status
!= ISC_R_SUCCESS
)
2982 status
= omapi_connection_put_uint32 (c
, s
-> me
.state
);
2983 if (status
!= ISC_R_SUCCESS
)
2986 status
= omapi_connection_put_name (c
, "partner-stos");
2987 if (status
!= ISC_R_SUCCESS
)
2989 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
2990 if (status
!= ISC_R_SUCCESS
)
2992 status
= omapi_connection_put_uint32 (c
,
2993 (u_int32_t
)s
-> partner
.stos
);
2994 if (status
!= ISC_R_SUCCESS
)
2997 status
= omapi_connection_put_name (c
, "local-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
, (u_int32_t
)s
-> me
.stos
);
3004 if (status
!= ISC_R_SUCCESS
)
3007 status
= omapi_connection_put_name (c
, "hierarchy");
3008 if (status
!= ISC_R_SUCCESS
)
3010 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3011 if (status
!= ISC_R_SUCCESS
)
3013 status
= omapi_connection_put_uint32 (c
, s
-> i_am
);
3014 if (status
!= ISC_R_SUCCESS
)
3017 status
= omapi_connection_put_name (c
, "last-packet-sent");
3018 if (status
!= ISC_R_SUCCESS
)
3020 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3021 if (status
!= ISC_R_SUCCESS
)
3023 status
= (omapi_connection_put_uint32
3024 (c
, (u_int32_t
)s
-> last_packet_sent
));
3025 if (status
!= ISC_R_SUCCESS
)
3028 status
= omapi_connection_put_name (c
, "last-timestamp-received");
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_timestamp_received
));
3036 if (status
!= ISC_R_SUCCESS
)
3039 status
= omapi_connection_put_name (c
, "skew");
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 (c
, (u_int32_t
)s
-> skew
);
3046 if (status
!= ISC_R_SUCCESS
)
3049 status
= omapi_connection_put_name (c
, "max-response-delay");
3050 if (status
!= ISC_R_SUCCESS
)
3052 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
3053 if (status
!= ISC_R_SUCCESS
)
3055 status
= (omapi_connection_put_uint32
3056 (c
, (u_int32_t
)s
-> me
.max_response_delay
));
3057 if (status
!= ISC_R_SUCCESS
)
3060 status
= omapi_connection_put_name (c
, "cur-unacked-updates");
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
-> cur_unacked_updates
));
3068 if (status
!= ISC_R_SUCCESS
)
3071 if (h
-> inner
&& h
-> inner
-> type
-> stuff_values
)
3072 return (*(h
-> inner
-> type
-> stuff_values
)) (c
, id
,
3074 return ISC_R_SUCCESS
;
3077 isc_result_t
dhcp_failover_state_lookup (omapi_object_t
**sp
,
3079 omapi_object_t
*ref
)
3081 omapi_value_t
*tv
= (omapi_value_t
*)0;
3082 isc_result_t status
;
3083 dhcp_failover_state_t
*s
;
3086 return ISC_R_NOKEYS
;
3088 /* First see if we were sent a handle. */
3089 status
= omapi_get_value_str (ref
, id
, "handle", &tv
);
3090 if (status
== ISC_R_SUCCESS
) {
3091 status
= omapi_handle_td_lookup (sp
, tv
-> value
);
3093 omapi_value_dereference (&tv
, MDL
);
3094 if (status
!= ISC_R_SUCCESS
)
3097 /* Don't return the object if the type is wrong. */
3098 if ((*sp
) -> type
!= dhcp_type_failover_state
) {
3099 omapi_object_dereference (sp
, MDL
);
3100 return ISC_R_INVALIDARG
;
3104 /* Look the failover state up by peer name. */
3105 status
= omapi_get_value_str (ref
, id
, "name", &tv
);
3106 if (status
== ISC_R_SUCCESS
) {
3107 for (s
= failover_states
; s
; s
= s
-> next
) {
3108 unsigned l
= strlen (s
-> name
);
3109 if (l
== tv
-> value
-> u
.buffer
.len
&&
3111 tv
-> value
-> u
.buffer
.value
, l
))
3114 omapi_value_dereference (&tv
, MDL
);
3116 /* If we already have a lease, and it's not the same one,
3117 then the query was invalid. */
3118 if (*sp
&& *sp
!= (omapi_object_t
*)s
) {
3119 omapi_object_dereference (sp
, MDL
);
3120 return ISC_R_KEYCONFLICT
;
3123 omapi_object_dereference (sp
, MDL
);
3124 return ISC_R_NOTFOUND
;
3126 /* XXX fix so that hash lookup itself creates
3127 XXX the reference. */
3128 omapi_object_reference (sp
, (omapi_object_t
*)s
, MDL
);
3131 /* If we get to here without finding a lease, no valid key was
3134 return ISC_R_NOKEYS
;
3135 return ISC_R_SUCCESS
;
3138 isc_result_t
dhcp_failover_state_create (omapi_object_t
**sp
,
3141 return ISC_R_NOTIMPLEMENTED
;
3144 isc_result_t
dhcp_failover_state_remove (omapi_object_t
*sp
,
3147 return ISC_R_NOTIMPLEMENTED
;
3150 int dhcp_failover_state_match (dhcp_failover_state_t
*state
,
3151 u_int8_t
*addr
, unsigned addrlen
)
3153 struct option_cache
*oc
;
3154 struct data_string ds
;
3157 memset (&ds
, 0, sizeof ds
);
3158 if (evaluate_option_cache (&ds
, (struct packet
*)0,
3160 (struct client_state
*)0,
3161 (struct option_state
*)0,
3162 (struct option_state
*)0,
3164 state
-> partner
.address
, MDL
)) {
3165 for (i
= 0; i
+ addrlen
- 1 < ds
.len
; i
+= addrlen
) {
3166 if (!memcmp (&ds
.data
[i
],
3168 data_string_forget (&ds
, MDL
);
3172 data_string_forget (&ds
, MDL
);
3177 const char *dhcp_failover_reject_reason_print (int reason
)
3180 case FTR_ILLEGAL_IP_ADDR
:
3181 return "Illegal IP address (not part of any address pool).";
3183 case FTR_FATAL_CONFLICT
:
3184 return "Fatal conflict exists: address in use by other client.";
3186 case FTR_MISSING_BINDINFO
:
3187 return "Missing binding information.";
3189 case FTR_TIMEMISMATCH
:
3190 return "Connection rejected, time mismatch too great.";
3192 case FTR_INVALID_MCLT
:
3193 return "Connection rejected, invalid MCLT.";
3195 case FTR_MISC_REJECT
:
3196 return "Connection rejected, unknown reason.";
3198 case FTR_DUP_CONNECTION
:
3199 return "Connection rejected, duplicate connection.";
3201 case FTR_INVALID_PARTNER
:
3202 return "Connection rejected, invalid failover partner.";
3204 case FTR_TLS_UNSUPPORTED
:
3205 return "TLS not supported.";
3207 case FTR_TLS_UNCONFIGURED
:
3208 return "TLS supported but not configured.";
3210 case FTR_TLS_REQUIRED
:
3211 return "TLS required but not supported by partner.";
3213 case FTR_DIGEST_UNSUPPORTED
:
3214 return "Message digest not supported.";
3216 case FTR_DIGEST_UNCONFIGURED
:
3217 return "Message digest not configured.";
3219 case FTR_VERSION_MISMATCH
:
3220 return "Protocol version mismatch.";
3222 case FTR_MISSING_BIND_INFO
:
3223 return "Missing binding information.";
3225 case FTR_OUTDATED_BIND_INFO
:
3226 return "Outdated binding information.";
3228 case FTR_LESS_CRIT_BIND_INFO
:
3229 return "Less critical binding information.";
3231 case FTR_NO_TRAFFIC
:
3232 return "No traffic within sufficient time.";
3234 case FTR_HBA_CONFLICT
:
3235 return "Hash bucket assignment conflict.";
3239 return "Unknown: Error occurred but does not match any reason code.";
3243 const char *dhcp_failover_state_name_print (enum failover_state state
)
3248 return "unknown-state";
3251 return "partner-down";
3256 case communications_interrupted
:
3257 return "communications-interrupted";
3259 case resolution_interrupted
:
3260 return "resolution-interrupted";
3262 case potential_conflict
:
3263 return "potential-conflict";
3269 return "recover-done";
3272 return "recover-wait";
3285 const char *dhcp_failover_message_name (unsigned type
)
3289 return "pool-request";
3292 return "pool-response";
3295 return "bind-update";
3303 case FTM_CONNECTACK
:
3304 return "connect-ack";
3307 return "update-request";
3310 return "update-done";
3313 return "update-request-all";
3321 case FTM_DISCONNECT
:
3322 return "disconnect";
3325 return "<unknown message type>";
3329 const char *dhcp_failover_option_name (unsigned type
)
3332 case FTO_BINDING_STATUS
:
3333 return "binding-status";
3335 case FTO_ASSIGNED_IP_ADDRESS
:
3336 return "assigned-ip-address";
3338 case FTO_SERVER_ADDR
:
3339 return "server-addr";
3341 case FTO_ADDRESSES_TRANSFERRED
:
3342 return "addresses-transferred";
3344 case FTO_CLIENT_IDENTIFIER
:
3345 return "client-identifier";
3353 case FTO_REJECT_REASON
:
3354 return "reject-reason";
3362 case FTO_VENDOR_CLASS
:
3363 return "vendor-class";
3365 case FTO_LEASE_EXPIRY
:
3366 return "lease-expiry";
3368 case FTO_POTENTIAL_EXPIRY
:
3369 return "potential-expiry";
3371 case FTO_GRACE_EXPIRY
:
3372 return "grace-expiry";
3380 case FTO_SERVER_STATE
:
3381 return "server-state";
3383 case FTO_SERVER_FLAGS
:
3384 return "server-flags";
3386 case FTO_VENDOR_OPTIONS
:
3387 return "vendor-options";
3389 case FTO_MAX_UNACKED
:
3390 return "max-unacked";
3392 case FTO_RECEIVE_TIMER
:
3393 return "receive-timer";
3398 case FTO_MESSAGE_DIGEST
:
3399 return "message-digest";
3401 case FTO_PROTOCOL_VERSION
:
3402 return "protocol-version";
3404 case FTO_TLS_REQUEST
:
3405 return "tls-request";
3410 case FTO_REQUEST_OPTIONS
:
3411 return "request-options";
3413 case FTO_REPLY_OPTIONS
:
3414 return "reply-options";
3417 return "<unknown option>";
3421 failover_option_t
*dhcp_failover_option_printf (unsigned code
,
3425 const char *fmt
, ...)
3430 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3431 * It is unclear what the effects of truncation here are, or
3432 * how that condition should be handled. It seems that this
3433 * function is used for formatting messages in the failover
3434 * command channel. For now the safest thing is for
3435 * overflow-truncation to cause a fatal log.
3438 if (vsnprintf (tbuf
, sizeof tbuf
, fmt
, va
) >= sizeof tbuf
)
3439 log_fatal ("%s: vsnprintf would truncate",
3440 "dhcp_failover_make_option");
3443 return dhcp_failover_make_option (code
, obuf
, obufix
, obufmax
,
3444 strlen (tbuf
), tbuf
);
3447 failover_option_t
*dhcp_failover_make_option (unsigned code
,
3448 char *obuf
, unsigned *obufix
,
3449 unsigned obufmax
, ...)
3452 struct failover_option_info
*info
;
3454 unsigned size
, count
;
3460 #if defined (DEBUG_FAILOVER_MESSAGES)
3464 /* Note that the failover_option structure is used differently on
3465 input than on output - on input, count is an element count, and
3466 on output it's the number of bytes total in the option, including
3467 the option code and option length. */
3468 failover_option_t option
, *op
;
3471 /* Bogus option code? */
3472 if (code
< 1 || code
> FTO_MAX
|| ft_options
[code
].type
== FT_UNDEF
) {
3473 return &null_failover_option
;
3475 info
= &ft_options
[code
];
3477 va_start (va
, obufmax
);
3479 /* Get the number of elements and the size of the buffer we need
3481 if (info
-> type
== FT_DDNS
|| info
-> type
== FT_DDNS1
) {
3482 count
= info
-> type
== FT_DDNS
? 1 : 2;
3483 size
= va_arg (va
, int) + count
;
3485 /* Find out how many items in this list. */
3486 if (info
-> num_present
)
3487 count
= info
-> num_present
;
3489 count
= va_arg (va
, int);
3491 /* Figure out size. */
3492 switch (info
-> type
) {
3499 case FT_TEXT_OR_BYTES
:
3501 txt
= va_arg (va
, char *);
3506 ilen
= va_arg (va
, unsigned);
3507 size
= count
* ilen
;
3519 /* shouldn't get here. */
3520 log_fatal ("bogus type in failover_make_option: %d",
3522 return &null_failover_option
;
3528 /* Allocate a buffer for the option. */
3529 option
.count
= size
;
3530 option
.data
= dmalloc (option
.count
, MDL
);
3533 return &null_failover_option
;
3536 /* Put in the option code and option length. */
3537 putUShort (option
.data
, code
);
3538 putUShort (&option
.data
[2], size
- 4);
3540 #if defined (DEBUG_FAILOVER_MESSAGES)
3541 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3542 * It is unclear what the effects of truncation here are, or
3543 * how that condition should be handled. It seems that this
3544 * message may be sent over the failover command channel.
3545 * For now the safest thing is for overflow-truncation to cause
3548 if (snprintf (tbuf
, sizeof tbuf
, " (%s<%d>", info
-> name
,
3549 option
.count
) >= sizeof tbuf
)
3550 log_fatal ("dhcp_failover_make_option: tbuf overflow");
3551 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3554 /* Now put in the data. */
3555 switch (info
-> type
) {
3557 for (i
= 0; i
< count
; i
++) {
3558 val
= va_arg (va
, unsigned);
3559 #if defined (DEBUG_FAILOVER_MESSAGES)
3560 /* %Audit% Cannot exceed 24 bytes. %2004.06.17,Safe% */
3561 sprintf (tbuf
, " %d", val
);
3562 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3564 option
.data
[i
+ 4] = val
;
3569 for (i
= 0; i
< count
; i
++) {
3570 iaddr
= va_arg (va
, u_int8_t
*);
3572 dfree (option
.data
, MDL
);
3573 log_error ("IP addrlen=%d, should be 4.",
3576 return &null_failover_option
;
3579 #if defined (DEBUG_FAILOVER_MESSAGES)
3580 /*%Audit% Cannot exceed 17 bytes. %2004.06.17,Safe%*/
3581 sprintf (tbuf
, " %u.%u.%u.%u",
3582 iaddr
[0], iaddr
[1], iaddr
[2], iaddr
[3]);
3583 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3585 memcpy (&option
.data
[4 + i
* ilen
], iaddr
, ilen
);
3590 for (i
= 0; i
< count
; i
++) {
3591 val
= va_arg (va
, unsigned);
3592 #if defined (DEBUG_FAILOVER_MESSAGES)
3593 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3594 sprintf (tbuf
, " %d", val
);
3595 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3597 putULong (&option
.data
[4 + i
* 4], val
);
3603 bval
= va_arg (va
, u_int8_t
*);
3604 #if defined (DEBUG_FAILOVER_MESSAGES)
3605 for (i
= 0; i
< count
; i
++) {
3606 /* 23 bytes plus nul, safe. */
3607 sprintf (tbuf
, " %d", bval
[i
]);
3608 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3611 memcpy (&option
.data
[4], bval
, count
);
3614 /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
3615 terminated. Note that the caller should be careful not
3616 to provide a format and data that amount to more than 256
3617 bytes of data, since it will cause a fatal error. */
3618 case FT_TEXT_OR_BYTES
:
3620 #if defined (DEBUG_FAILOVER_MESSAGES)
3621 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3622 * It is unclear what the effects of truncation here are, or
3623 * how that condition should be handled. It seems that this
3624 * function is used for formatting messages in the failover
3625 * command channel. For now the safest thing is for
3626 * overflow-truncation to cause a fatal log.
3628 if (snprintf (tbuf
, sizeof tbuf
, "\"%s\"", txt
) >= sizeof tbuf
)
3629 log_fatal ("dhcp_failover_make_option: tbuf overflow");
3630 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3632 memcpy (&option
.data
[4], txt
, count
);
3637 option
.data
[4] = va_arg (va
, unsigned);
3639 option
.data
[5] = va_arg (va
, unsigned);
3640 bval
= va_arg (va
, u_int8_t
*);
3641 memcpy (&option
.data
[4 + count
], bval
, size
- count
- 4);
3642 #if defined (DEBUG_FAILOVER_MESSAGES)
3643 for (i
= 4; i
< size
; i
++) {
3644 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3645 sprintf (tbuf
, " %d", option
.data
[i
]);
3646 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3652 for (i
= 0; i
< count
; i
++) {
3653 val
= va_arg (va
, u_int32_t
);
3654 #if defined (DEBUG_FAILOVER_MESSAGES)
3655 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3656 sprintf (tbuf
, " %d", val
);
3657 failover_print (obuf
, obufix
, obufmax
, tbuf
);
3659 putUShort (&option
.data
[4 + i
* 2], val
);
3668 #if defined DEBUG_FAILOVER_MESSAGES
3669 failover_print (obuf
, obufix
, obufmax
, ")");
3673 /* Now allocate a place to store what we just set up. */
3674 op
= dmalloc (sizeof (failover_option_t
), MDL
);
3676 dfree (option
.data
, MDL
);
3677 return &null_failover_option
;
3684 /* Send a failover message header. */
3686 isc_result_t
dhcp_failover_put_message (dhcp_failover_link_t
*link
,
3687 omapi_object_t
*connection
,
3695 failover_option_t
*option
;
3696 unsigned char *opbuf
;
3697 isc_result_t status
= ISC_R_SUCCESS
;
3700 /* Run through the argument list once to compute the length of
3701 the option portion of the message. */
3702 va_start (list
, msg_type
);
3703 while ((option
= va_arg (list
, failover_option_t
*))) {
3704 if (option
!= &skip_failover_option
)
3705 size
+= option
-> count
;
3706 if (option
== &null_failover_option
)
3711 /* Allocate an option buffer, unless we got an error. */
3712 if (!bad_option
&& size
) {
3713 opbuf
= dmalloc (size
, MDL
);
3715 status
= ISC_R_NOMEMORY
;
3717 opbuf
= (unsigned char *)0;
3719 va_start (list
, msg_type
);
3720 while ((option
= va_arg (list
, failover_option_t
*))) {
3721 if (option
== &skip_failover_option
)
3723 if (!bad_option
&& opbuf
)
3724 memcpy (&opbuf
[opix
],
3725 option
-> data
, option
-> count
);
3726 if (option
!= &null_failover_option
&&
3727 option
!= &skip_failover_option
) {
3728 opix
+= option
-> count
;
3729 dfree (option
-> data
, MDL
);
3730 dfree (option
, MDL
);
3735 return ISC_R_INVALIDARG
;
3737 /* Now send the message header. */
3739 /* Message length. */
3740 status
= omapi_connection_put_uint16 (connection
, size
+ 12);
3741 if (status
!= ISC_R_SUCCESS
)
3746 status
= omapi_connection_copyin (connection
, &cbuf
, 1);
3747 if (status
!= ISC_R_SUCCESS
)
3750 /* Payload offset. */
3752 status
= omapi_connection_copyin (connection
, &cbuf
, 1);
3753 if (status
!= ISC_R_SUCCESS
)
3757 status
= omapi_connection_put_uint32 (connection
, (u_int32_t
)cur_time
);
3758 if (status
!= ISC_R_SUCCESS
)
3761 /* Transaction ID. */
3762 status
= omapi_connection_put_uint32 (connection
, link
-> xid
++);
3763 if (status
!= ISC_R_SUCCESS
)
3769 status
= omapi_connection_copyin (connection
, opbuf
, size
);
3770 if (status
!= ISC_R_SUCCESS
)
3774 if (link
-> state_object
&&
3775 link
-> state_object
-> link_to_peer
== link
) {
3776 #if defined (DEBUG_FAILOVER_TIMING)
3777 log_info ("add_timeout +%d %s",
3778 (int)(link
-> state_object
->
3779 partner
.max_response_delay
) / 3,
3780 "dhcp_failover_send_contact");
3782 add_timeout (cur_time
+
3783 (int)(link
-> state_object
->
3784 partner
.max_response_delay
) / 3,
3785 dhcp_failover_send_contact
, link
-> state_object
,
3786 (tvref_t
)dhcp_failover_state_reference
,
3787 (tvunref_t
)dhcp_failover_state_dereference
);
3794 log_info ("dhcp_failover_put_message: something went wrong.");
3795 omapi_disconnect (connection
, 1);
3799 void dhcp_failover_timeout (void *vstate
)
3801 dhcp_failover_state_t
*state
= vstate
;
3802 dhcp_failover_link_t
*link
;
3803 isc_result_t status
;
3805 #if defined (DEBUG_FAILOVER_TIMING)
3806 log_info ("dhcp_failover_timeout");
3809 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
3811 link
= state
-> link_to_peer
;
3814 link
-> outer
-> type
!= omapi_type_connection
)
3817 log_error ("timeout waiting for failover peer %s", state
-> name
);
3819 /* If we haven't gotten a timely response, blow away the connection.
3820 This will cause the state to change automatically. */
3821 omapi_disconnect (link
-> outer
, 1);
3824 void dhcp_failover_send_contact (void *vstate
)
3826 dhcp_failover_state_t
*state
= vstate
;
3827 dhcp_failover_link_t
*link
;
3828 isc_result_t status
;
3830 #if defined (DEBUG_FAILOVER_MESSAGES)
3832 unsigned obufix
= 0;
3834 # define FMA obuf, &obufix, sizeof obuf
3835 failover_print (FMA
, "(contact");
3837 # define FMA (char *)0, (unsigned *)0, 0
3840 #if defined (DEBUG_FAILOVER_TIMING)
3841 log_info ("dhcp_failover_send_contact");
3844 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
3846 link
= state
-> link_to_peer
;
3849 link
-> outer
-> type
!= omapi_type_connection
)
3852 status
= (dhcp_failover_put_message
3853 (link
, link
-> outer
,
3855 (failover_option_t
*)0));
3857 #if defined (DEBUG_FAILOVER_MESSAGES)
3858 if (status
!= ISC_R_SUCCESS
)
3859 failover_print (FMA
, " (failed)");
3860 failover_print (FMA
, ")");
3862 log_debug ("%s", obuf
);
3868 isc_result_t
dhcp_failover_send_state (dhcp_failover_state_t
*state
)
3870 dhcp_failover_link_t
*link
;
3871 isc_result_t status
;
3873 #if defined (DEBUG_FAILOVER_MESSAGES)
3875 unsigned obufix
= 0;
3877 # define FMA obuf, &obufix, sizeof obuf
3878 failover_print (FMA
, "(state");
3880 # define FMA (char *)0, (unsigned *)0, 0
3883 if (!state
|| state
-> type
!= dhcp_type_failover_state
)
3884 return ISC_R_INVALIDARG
;
3885 link
= state
-> link_to_peer
;
3888 link
-> outer
-> type
!= omapi_type_connection
)
3889 return ISC_R_INVALIDARG
;
3891 status
= (dhcp_failover_put_message
3892 (link
, link
-> outer
,
3894 dhcp_failover_make_option (FTO_SERVER_STATE
, FMA
,
3895 (state
-> me
.state
== startup
3896 ? state
-> saved_state
3897 : state
-> me
.state
)),
3898 dhcp_failover_make_option
3899 (FTO_SERVER_FLAGS
, FMA
,
3900 (state
-> service_state
== service_startup
3901 ? FTF_STARTUP
: 0)),
3902 dhcp_failover_make_option (FTO_STOS
, FMA
, state
-> me
.stos
),
3903 (failover_option_t
*)0));
3905 #if defined (DEBUG_FAILOVER_MESSAGES)
3906 if (status
!= ISC_R_SUCCESS
)
3907 failover_print (FMA
, " (failed)");
3908 failover_print (FMA
, ")");
3910 log_debug ("%s", obuf
);
3913 return ISC_R_SUCCESS
;
3916 /* Send a connect message. */
3918 isc_result_t
dhcp_failover_send_connect (omapi_object_t
*l
)
3920 dhcp_failover_link_t
*link
;
3921 dhcp_failover_state_t
*state
;
3922 isc_result_t status
;
3924 #if defined (DEBUG_FAILOVER_MESSAGES)
3926 unsigned obufix
= 0;
3928 # define FMA obuf, &obufix, sizeof obuf
3929 failover_print (FMA
, "(connect");
3931 # define FMA (char *)0, (unsigned *)0, 0
3934 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
3935 return ISC_R_INVALIDARG
;
3936 link
= (dhcp_failover_link_t
*)l
;
3937 state
= link
-> state_object
;
3938 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
3939 return ISC_R_INVALIDARG
;
3942 (dhcp_failover_put_message
3945 dhcp_failover_make_option (FTO_SERVER_ADDR
, FMA
,
3946 state
-> server_identifier
.len
,
3947 state
-> server_identifier
.data
),
3948 dhcp_failover_make_option (FTO_MAX_UNACKED
, FMA
,
3949 state
-> me
.max_flying_updates
),
3950 dhcp_failover_make_option (FTO_RECEIVE_TIMER
, FMA
,
3951 state
-> me
.max_response_delay
),
3952 dhcp_failover_option_printf (FTO_VENDOR_CLASS
, FMA
,
3953 "isc-%s", DHCP_VERSION
),
3954 dhcp_failover_make_option (FTO_PROTOCOL_VERSION
, FMA
,
3955 DHCP_FAILOVER_VERSION
),
3956 dhcp_failover_make_option (FTO_TLS_REQUEST
, FMA
,
3958 dhcp_failover_make_option (FTO_MCLT
, FMA
,
3961 ? dhcp_failover_make_option (FTO_HBA
, FMA
, 32, state
-> hba
)
3962 : &skip_failover_option
),
3963 (failover_option_t
*)0));
3965 #if defined (DEBUG_FAILOVER_MESSAGES)
3966 if (status
!= ISC_R_SUCCESS
)
3967 failover_print (FMA
, " (failed)");
3968 failover_print (FMA
, ")");
3970 log_debug ("%s", obuf
);
3976 isc_result_t
dhcp_failover_send_connectack (omapi_object_t
*l
,
3977 dhcp_failover_state_t
*state
,
3978 int reason
, const char *errmsg
)
3980 dhcp_failover_link_t
*link
;
3981 isc_result_t status
;
3982 #if defined (DEBUG_FAILOVER_MESSAGES)
3984 unsigned obufix
= 0;
3986 # define FMA obuf, &obufix, sizeof obuf
3987 failover_print (FMA
, "(connectack");
3989 # define FMA (char *)0, (unsigned *)0, 0
3992 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
3993 return ISC_R_INVALIDARG
;
3994 link
= (dhcp_failover_link_t
*)l
;
3995 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
3996 return ISC_R_INVALIDARG
;
3999 (dhcp_failover_put_message
4003 ? (dhcp_failover_make_option
4004 (FTO_SERVER_ADDR
, FMA
,
4005 state
-> server_identifier
.len
,
4006 state
-> server_identifier
.data
))
4007 : &skip_failover_option
),
4009 ? dhcp_failover_make_option (FTO_MAX_UNACKED
, FMA
,
4010 state
-> me
.max_flying_updates
)
4011 : &skip_failover_option
),
4013 ? dhcp_failover_make_option (FTO_RECEIVE_TIMER
, FMA
,
4014 state
-> me
.max_response_delay
)
4015 : &skip_failover_option
),
4016 dhcp_failover_option_printf (FTO_VENDOR_CLASS
, FMA
,
4017 "isc-%s", DHCP_VERSION
),
4018 dhcp_failover_make_option (FTO_PROTOCOL_VERSION
, FMA
,
4019 DHCP_FAILOVER_VERSION
),
4020 dhcp_failover_make_option (FTO_TLS_REQUEST
, FMA
,
4023 ? dhcp_failover_make_option (FTO_REJECT_REASON
,
4025 : &skip_failover_option
),
4027 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4028 strlen (errmsg
), errmsg
)
4029 : &skip_failover_option
),
4030 (failover_option_t
*)0));
4032 #if defined (DEBUG_FAILOVER_MESSAGES)
4033 if (status
!= ISC_R_SUCCESS
)
4034 failover_print (FMA
, " (failed)");
4035 failover_print (FMA
, ")");
4037 log_debug ("%s", obuf
);
4043 isc_result_t
dhcp_failover_send_disconnect (omapi_object_t
*l
,
4045 const char *message
)
4047 dhcp_failover_link_t
*link
;
4048 dhcp_failover_state_t
*state
;
4049 isc_result_t status
;
4050 #if defined (DEBUG_FAILOVER_MESSAGES)
4052 unsigned obufix
= 0;
4054 # define FMA obuf, &obufix, sizeof obuf
4055 failover_print (FMA
, "(disconnect");
4057 # define FMA (char *)0, (unsigned *)0, 0
4060 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
4061 return ISC_R_INVALIDARG
;
4062 link
= (dhcp_failover_link_t
*)l
;
4063 state
= link
-> state_object
;
4064 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
4065 return ISC_R_INVALIDARG
;
4067 if (!message
&& reason
)
4068 message
= dhcp_failover_reject_reason_print (reason
);
4070 status
= (dhcp_failover_put_message
4073 dhcp_failover_make_option (FTO_REJECT_REASON
,
4076 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4077 strlen (message
), message
)
4078 : &skip_failover_option
),
4079 (failover_option_t
*)0));
4081 #if defined (DEBUG_FAILOVER_MESSAGES)
4082 if (status
!= ISC_R_SUCCESS
)
4083 failover_print (FMA
, " (failed)");
4084 failover_print (FMA
, ")");
4086 log_debug ("%s", obuf
);
4092 /* Send a Bind Update message. */
4094 isc_result_t
dhcp_failover_send_bind_update (dhcp_failover_state_t
*state
,
4095 struct lease
*lease
)
4097 dhcp_failover_link_t
*link
;
4098 isc_result_t status
;
4099 #if defined (DEBUG_FAILOVER_MESSAGES)
4101 unsigned obufix
= 0;
4103 # define FMA obuf, &obufix, sizeof obuf
4104 failover_print (FMA
, "(bndupd");
4106 # define FMA (char *)0, (unsigned *)0, 0
4109 if (!state
-> link_to_peer
||
4110 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4111 return ISC_R_INVALIDARG
;
4112 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4114 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4115 return ISC_R_INVALIDARG
;
4117 /* Send the update. */
4118 status
= (dhcp_failover_put_message
4119 (link
, link
-> outer
,
4121 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS
, FMA
,
4122 lease
-> ip_addr
.len
,
4123 lease
-> ip_addr
.iabuf
),
4124 dhcp_failover_make_option (FTO_BINDING_STATUS
, FMA
,
4125 lease
-> desired_binding_state
),
4127 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER
, FMA
,
4130 : &skip_failover_option
,
4131 lease
-> hardware_addr
.hlen
4132 ? dhcp_failover_make_option (FTO_CHADDR
, FMA
,
4133 lease
-> hardware_addr
.hlen
,
4134 lease
-> hardware_addr
.hbuf
)
4135 : &skip_failover_option
,
4136 dhcp_failover_make_option (FTO_LEASE_EXPIRY
, FMA
,
4138 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY
, FMA
,
4140 dhcp_failover_make_option (FTO_STOS
, FMA
,
4142 dhcp_failover_make_option (FTO_CLTT
, FMA
,
4144 &skip_failover_option
, /* XXX DDNS */
4145 &skip_failover_option
, /* XXX request options */
4146 &skip_failover_option
, /* XXX reply options */
4147 (failover_option_t
*)0));
4149 #if defined (DEBUG_FAILOVER_MESSAGES)
4150 if (status
!= ISC_R_SUCCESS
)
4151 failover_print (FMA
, " (failed)");
4152 failover_print (FMA
, ")");
4154 log_debug ("%s", obuf
);
4160 /* Send a Bind ACK message. */
4162 isc_result_t
dhcp_failover_send_bind_ack (dhcp_failover_state_t
*state
,
4163 failover_message_t
*msg
,
4164 int reason
, const char *message
)
4166 dhcp_failover_link_t
*link
;
4167 isc_result_t status
;
4168 #if defined (DEBUG_FAILOVER_MESSAGES)
4170 unsigned obufix
= 0;
4172 # define FMA obuf, &obufix, sizeof obuf
4173 failover_print (FMA
, "(bndack");
4175 # define FMA (char *)0, (unsigned *)0, 0
4178 if (!state
-> link_to_peer
||
4179 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4180 return ISC_R_INVALIDARG
;
4181 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4183 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4184 return ISC_R_INVALIDARG
;
4186 if (!message
&& reason
)
4187 message
= dhcp_failover_reject_reason_print (reason
);
4189 /* Send the update. */
4190 status
= (dhcp_failover_put_message
4191 (link
, link
-> outer
,
4193 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS
, FMA
,
4194 sizeof msg
-> assigned_addr
,
4195 &msg
-> assigned_addr
),
4196 dhcp_failover_make_option (FTO_BINDING_STATUS
, FMA
,
4197 msg
-> binding_status
),
4198 (msg
-> options_present
& FTB_CLIENT_IDENTIFIER
)
4199 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER
, FMA
,
4200 msg
-> client_identifier
.count
,
4201 msg
-> client_identifier
.data
)
4202 : &skip_failover_option
,
4203 (msg
-> options_present
& FTB_CHADDR
)
4204 ? dhcp_failover_make_option (FTO_CHADDR
, FMA
,
4205 msg
-> chaddr
.count
,
4207 : &skip_failover_option
,
4208 dhcp_failover_make_option (FTO_LEASE_EXPIRY
, FMA
,
4210 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY
, FMA
,
4211 msg
-> potential_expiry
),
4212 dhcp_failover_make_option (FTO_STOS
, FMA
,
4214 dhcp_failover_make_option (FTO_CLTT
, FMA
,
4217 ? dhcp_failover_make_option (FTO_REJECT_REASON
,
4219 : &skip_failover_option
,
4221 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
4222 strlen (message
), message
)
4223 : &skip_failover_option
),
4224 &skip_failover_option
, /* XXX DDNS */
4225 &skip_failover_option
, /* XXX request options */
4226 &skip_failover_option
, /* XXX reply options */
4227 (failover_option_t
*)0));
4229 #if defined (DEBUG_FAILOVER_MESSAGES)
4230 if (status
!= ISC_R_SUCCESS
)
4231 failover_print (FMA
, " (failed)");
4232 failover_print (FMA
, ")");
4234 log_debug ("%s", obuf
);
4240 isc_result_t
dhcp_failover_send_poolreq (dhcp_failover_state_t
*state
)
4242 dhcp_failover_link_t
*link
;
4243 isc_result_t status
;
4244 #if defined (DEBUG_FAILOVER_MESSAGES)
4246 unsigned obufix
= 0;
4248 # define FMA obuf, &obufix, sizeof obuf
4249 failover_print (FMA
, "(poolreq");
4251 # define FMA (char *)0, (unsigned *)0, 0
4254 if (!state
-> link_to_peer
||
4255 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4256 return ISC_R_INVALIDARG
;
4257 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4259 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4260 return ISC_R_INVALIDARG
;
4262 status
= (dhcp_failover_put_message
4263 (link
, link
-> outer
,
4265 (failover_option_t
*)0));
4267 #if defined (DEBUG_FAILOVER_MESSAGES)
4268 if (status
!= ISC_R_SUCCESS
)
4269 failover_print (FMA
, " (failed)");
4270 failover_print (FMA
, ")");
4272 log_debug ("%s", obuf
);
4278 isc_result_t
dhcp_failover_send_poolresp (dhcp_failover_state_t
*state
,
4281 dhcp_failover_link_t
*link
;
4282 isc_result_t status
;
4283 #if defined (DEBUG_FAILOVER_MESSAGES)
4285 unsigned obufix
= 0;
4287 # define FMA obuf, &obufix, sizeof obuf
4288 failover_print (FMA
, "(poolresp");
4290 # define FMA (char *)0, (unsigned *)0, 0
4293 if (!state
-> link_to_peer
||
4294 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4295 return ISC_R_INVALIDARG
;
4296 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4298 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4299 return ISC_R_INVALIDARG
;
4301 status
= (dhcp_failover_put_message
4302 (link
, link
-> outer
,
4304 dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED
, FMA
,
4306 (failover_option_t
*)0));
4308 #if defined (DEBUG_FAILOVER_MESSAGES)
4309 if (status
!= ISC_R_SUCCESS
)
4310 failover_print (FMA
, " (failed)");
4311 failover_print (FMA
, ")");
4313 log_debug ("%s", obuf
);
4319 isc_result_t
dhcp_failover_send_update_request (dhcp_failover_state_t
*state
)
4321 dhcp_failover_link_t
*link
;
4322 isc_result_t status
;
4323 #if defined (DEBUG_FAILOVER_MESSAGES)
4325 unsigned obufix
= 0;
4327 # define FMA obuf, &obufix, sizeof obuf
4328 failover_print (FMA
, "(updreq");
4330 # define FMA (char *)0, (unsigned *)0, 0
4333 if (!state
-> link_to_peer
||
4334 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4335 return ISC_R_INVALIDARG
;
4336 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4338 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4339 return ISC_R_INVALIDARG
;
4341 status
= (dhcp_failover_put_message
4342 (link
, link
-> outer
,
4344 (failover_option_t
*)0));
4346 #if defined (DEBUG_FAILOVER_MESSAGES)
4347 if (status
!= ISC_R_SUCCESS
)
4348 failover_print (FMA
, " (failed)");
4349 failover_print (FMA
, ")");
4351 log_debug ("%s", obuf
);
4354 log_info ("Sent update request message to %s", state
-> name
);
4358 isc_result_t
dhcp_failover_send_update_request_all (dhcp_failover_state_t
4361 dhcp_failover_link_t
*link
;
4362 isc_result_t status
;
4363 #if defined (DEBUG_FAILOVER_MESSAGES)
4365 unsigned obufix
= 0;
4367 # define FMA obuf, &obufix, sizeof obuf
4368 failover_print (FMA
, "(updreqall");
4370 # define FMA (char *)0, (unsigned *)0, 0
4373 if (!state
-> link_to_peer
||
4374 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4375 return ISC_R_INVALIDARG
;
4376 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4378 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4379 return ISC_R_INVALIDARG
;
4381 status
= (dhcp_failover_put_message
4382 (link
, link
-> outer
,
4384 (failover_option_t
*)0));
4386 #if defined (DEBUG_FAILOVER_MESSAGES)
4387 if (status
!= ISC_R_SUCCESS
)
4388 failover_print (FMA
, " (failed)");
4389 failover_print (FMA
, ")");
4391 log_debug ("%s", obuf
);
4394 log_info ("Sent update request all message to %s", state
-> name
);
4398 isc_result_t
dhcp_failover_send_update_done (dhcp_failover_state_t
*state
)
4400 dhcp_failover_link_t
*link
;
4401 isc_result_t status
;
4402 #if defined (DEBUG_FAILOVER_MESSAGES)
4404 unsigned obufix
= 0;
4406 # define FMA obuf, &obufix, sizeof obuf
4407 failover_print (FMA
, "(upddone");
4409 # define FMA (char *)0, (unsigned *)0, 0
4412 if (!state
-> link_to_peer
||
4413 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
4414 return ISC_R_INVALIDARG
;
4415 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
4417 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
4418 return ISC_R_INVALIDARG
;
4420 status
= (dhcp_failover_put_message
4421 (link
, link
-> outer
,
4423 (failover_option_t
*)0));
4425 #if defined (DEBUG_FAILOVER_MESSAGES)
4426 if (status
!= ISC_R_SUCCESS
)
4427 failover_print (FMA
, " (failed)");
4428 failover_print (FMA
, ")");
4430 log_debug ("%s", obuf
);
4434 log_info ("Sent update done message to %s", state
-> name
);
4436 /* There may be uncommitted leases at this point (since
4437 dhcp_failover_process_bind_ack() doesn't commit leases);
4438 commit the lease file. */
4444 isc_result_t
dhcp_failover_process_bind_update (dhcp_failover_state_t
*state
,
4445 failover_message_t
*msg
)
4447 struct lease
*lt
, *lease
;
4449 int reason
= FTR_MISC_REJECT
;
4450 const char *message
;
4451 int new_binding_state
;
4453 ia
.len
= sizeof msg
-> assigned_addr
;
4454 memcpy (ia
.iabuf
, &msg
-> assigned_addr
, ia
.len
);
4456 lease
= (struct lease
*)0;
4457 lt
= (struct lease
*)0;
4458 if (!find_lease_by_ip_addr (&lease
, ia
, MDL
)) {
4459 message
= "unknown IP address";
4460 reason
= FTR_ILLEGAL_IP_ADDR
;
4464 /* XXX check for conflicts. */
4466 /* Install the new info. */
4467 if (!lease_copy (<
, lease
, MDL
)) {
4468 message
= "no memory";
4472 if (msg
-> options_present
& FTB_CHADDR
) {
4473 if (msg
-> chaddr
.count
> sizeof lt
-> hardware_addr
.hbuf
) {
4474 message
= "chaddr to long";
4477 lt
-> hardware_addr
.hlen
= msg
-> chaddr
.count
;
4478 memcpy (lt
-> hardware_addr
.hbuf
, msg
-> chaddr
.data
,
4479 msg
-> chaddr
.count
);
4482 if (msg
-> options_present
& FTB_CLIENT_IDENTIFIER
) {
4483 lt
-> uid_len
= msg
-> client_identifier
.count
;
4484 if (lt
-> uid_len
> sizeof lt
-> uid_buf
) {
4485 lt
-> uid_max
= lt
-> uid_len
;
4486 lt
-> uid
= dmalloc (lt
-> uid_len
, MDL
);
4488 message
= "no memory";
4492 lt
-> uid_max
= sizeof lt
-> uid_buf
;
4493 lt
-> uid
= lt
-> uid_buf
;
4496 msg
-> client_identifier
.data
, lt
-> uid_len
);
4499 /* XXX Times may need to be adjusted based on clock skew! */
4500 if (msg
-> options_present
& FTB_STOS
) {
4501 lt
-> starts
= msg
-> stos
;
4503 if (msg
-> options_present
& FTB_LEASE_EXPIRY
) {
4504 lt
-> ends
= msg
-> expiry
;
4506 if (msg
-> options_present
& FTB_CLTT
) {
4507 lt
-> cltt
= msg
-> client_ltt
;
4509 if (msg
-> options_present
& FTB_POTENTIAL_EXPIRY
) {
4510 lt
-> tsfp
= msg
-> potential_expiry
;
4513 if (msg
-> options_present
& FTB_BINDING_STATUS
) {
4514 #if defined (DEBUG_LEASE_STATE_TRANSITIONS)
4515 log_info ("processing state transition for %s: %s to %s",
4516 piaddr (lease
-> ip_addr
),
4517 binding_state_print (lease
-> binding_state
),
4518 binding_state_print (msg
-> binding_status
));
4521 /* If we're in normal state, make sure the state transition
4523 if (state
-> me
.state
== normal
) {
4525 (normal_binding_state_transition_check
4526 (lease
, state
, msg
-> binding_status
,
4527 msg
-> potential_expiry
));
4528 /* XXX if the transition the peer asked for isn't
4529 XXX allowed, maybe we should make the transition
4530 XXX into potential-conflict at this point. */
4533 (conflict_binding_state_transition_check
4534 (lease
, state
, msg
-> binding_status
,
4535 msg
-> potential_expiry
));
4537 if (new_binding_state
!= msg
-> binding_status
) {
4540 if (snprintf (outbuf
, sizeof outbuf
,
4541 "%s: invalid state transition: %s to %s",
4542 piaddr (lease
-> ip_addr
),
4543 binding_state_print (lease
-> binding_state
),
4544 binding_state_print (msg
-> binding_status
))
4546 log_fatal ("%s: impossible outbuf overflow",
4547 "dhcp_failover_process_bind_update");
4549 dhcp_failover_send_bind_ack (state
, msg
,
4554 if (new_binding_state
== FTS_EXPIRED
||
4555 new_binding_state
== FTS_RELEASED
||
4556 new_binding_state
== FTS_RESET
)
4557 lt
-> next_binding_state
= FTS_FREE
;
4559 lt
-> next_binding_state
= new_binding_state
;
4560 msg
-> binding_status
= lt
-> next_binding_state
;
4563 /* Try to install the new information. */
4564 if (!supersede_lease (lease
, lt
, 0, 0, 0) ||
4565 !write_lease (lease
)) {
4566 message
= "database update failed";
4568 dhcp_failover_send_bind_ack (state
, msg
, reason
, message
);
4571 dhcp_failover_queue_ack (state
, msg
);
4576 lease_dereference (<
, MDL
);
4578 lease_dereference (&lease
, MDL
);
4580 return ISC_R_SUCCESS
;
4583 isc_result_t
dhcp_failover_process_bind_ack (dhcp_failover_state_t
*state
,
4584 failover_message_t
*msg
)
4586 struct lease
*lt
= (struct lease
*)0;
4587 struct lease
*lease
= (struct lease
*)0;
4589 const char *message
= "no memory";
4591 ia
.len
= sizeof msg
-> assigned_addr
;
4592 memcpy (ia
.iabuf
, &msg
-> assigned_addr
, ia
.len
);
4594 if (!find_lease_by_ip_addr (&lease
, ia
, MDL
)) {
4595 message
= "no such lease";
4599 /* XXX check for conflicts. */
4600 if (msg
-> options_present
& FTB_REJECT_REASON
) {
4601 log_error ("bind update on %s from %s rejected: %.*s",
4602 piaddr (ia
), state
-> name
,
4603 (int)((msg
-> options_present
& FTB_MESSAGE
)
4604 ? msg
-> message
.count
4605 : strlen (dhcp_failover_reject_reason_print
4606 (msg
-> reject_reason
))),
4607 (msg
-> options_present
& FTB_MESSAGE
)
4608 ? (const char *)(msg
-> message
.data
)
4609 : (dhcp_failover_reject_reason_print
4610 (msg
-> reject_reason
)));
4614 /* XXX Times may need to be adjusted based on clock skew! */
4615 if (msg
-> options_present
& FTB_POTENTIAL_EXPIRY
) {
4616 /* XXX it could be a problem to do this directly if the
4617 XXX lease is sorted by tsfp. */
4618 if ((lease
-> binding_state
== FTS_EXPIRED
||
4619 lease
-> binding_state
== FTS_RESET
||
4620 lease
-> binding_state
== FTS_RELEASED
) &&
4621 (msg
-> options_present
& FTB_BINDING_STATUS
) &&
4622 msg
-> binding_status
== FTS_FREE
)
4624 lease
-> tsfp
= msg
-> potential_expiry
;
4625 lease
-> next_binding_state
= FTS_FREE
;
4626 supersede_lease (lease
, (struct lease
*)0, 0, 0, 0);
4627 write_lease (lease
);
4628 if (state
-> me
.state
== normal
)
4631 lease
-> tsfp
= msg
-> potential_expiry
;
4632 if ((lease
-> desired_binding_state
!=
4633 lease
-> binding_state
) &&
4634 (msg
-> options_present
& FTB_BINDING_STATUS
) &&
4635 (msg
-> binding_status
==
4636 lease
-> desired_binding_state
)) {
4637 lease
-> next_binding_state
=
4638 lease
-> desired_binding_state
;
4639 supersede_lease (lease
,
4640 (struct lease
*)0, 0, 0, 0);
4642 write_lease (lease
);
4643 /* Commit the lease only after a two-second timeout,
4644 so that if we get a bunch of acks in quick
4645 successtion (e.g., when stealing leases from the
4646 secondary), we do not do an immediate commit for
4648 add_timeout (cur_time
+ 2,
4649 commit_leases_timeout
, (void *)0, 0, 0);
4651 } else if (lease
-> desired_binding_state
!= lease
-> binding_state
&&
4652 (msg
-> options_present
& FTB_BINDING_STATUS
) &&
4653 msg
-> binding_status
== lease
-> desired_binding_state
) {
4654 lease
-> next_binding_state
= lease
-> desired_binding_state
;
4655 supersede_lease (lease
, (struct lease
*)0, 0, 0, 0);
4656 write_lease (lease
);
4657 add_timeout (cur_time
+ 2, commit_leases_timeout
,
4662 dhcp_failover_ack_queue_remove (state
, lease
);
4664 /* If we are supposed to send an update done after we send
4665 this lease, go ahead and send it. */
4666 if (state
-> send_update_done
== lease
) {
4667 lease_dereference (&state
-> send_update_done
, MDL
);
4668 dhcp_failover_send_update_done (state
);
4671 /* If there are updates pending, we've created space to send at
4673 dhcp_failover_send_updates (state
);
4676 lease_dereference (&lease
, MDL
);
4678 lease_dereference (<
, MDL
);
4680 return ISC_R_SUCCESS
;
4683 log_info ("bind update on %s from %s: %s.",
4684 piaddr (ia
), state
-> name
, message
);
4688 isc_result_t
dhcp_failover_generate_update_queue (dhcp_failover_state_t
*state
,
4691 struct shared_network
*s
;
4693 struct lease
*l
, *n
;
4695 struct lease
**lptr
[5];
4696 #define FREE_LEASES 0
4697 #define ACTIVE_LEASES 1
4698 #define EXPIRED_LEASES 2
4699 #define ABANDONED_LEASES 3
4700 #define BACKUP_LEASES 4
4702 /* First remove everything from the update and ack queues. */
4703 l
= n
= (struct lease
*)0;
4704 if (state
-> update_queue_head
) {
4705 lease_reference (&l
, state
-> update_queue_head
, MDL
);
4706 lease_dereference (&state
-> update_queue_head
, MDL
);
4708 l
-> flags
&= ~ON_UPDATE_QUEUE
;
4709 if (l
-> next_pending
) {
4710 lease_reference (&n
,
4711 l
-> next_pending
, MDL
);
4712 lease_dereference (&l
-> next_pending
, MDL
);
4714 lease_dereference (&l
, MDL
);
4716 lease_reference (&l
, n
, MDL
);
4717 lease_dereference (&n
, MDL
);
4720 lease_dereference (&state
-> update_queue_tail
, MDL
);
4723 if (state
-> ack_queue_head
) {
4724 lease_reference (&l
, state
-> ack_queue_head
, MDL
);
4725 lease_dereference (&state
-> ack_queue_head
, MDL
);
4727 l
-> flags
&= ~ON_ACK_QUEUE
;
4728 if (l
-> next_pending
) {
4729 lease_reference (&n
,
4730 l
-> next_pending
, MDL
);
4731 lease_dereference (&l
-> next_pending
, MDL
);
4733 lease_dereference (&l
, MDL
);
4735 lease_reference (&l
, n
, MDL
);
4736 lease_dereference (&n
, MDL
);
4739 lease_dereference (&state
-> ack_queue_tail
, MDL
);
4741 if (state
-> send_update_done
)
4742 lease_dereference (&state
-> send_update_done
, MDL
);
4743 state
-> cur_unacked_updates
= 0;
4745 /* Loop through each pool in each shared network and call the
4746 expiry routine on the pool. */
4747 for (s
= shared_networks
; s
; s
= s
-> next
) {
4748 for (p
= s
-> pools
; p
; p
= p
-> next
) {
4749 lptr
[FREE_LEASES
] = &p
-> free
;
4750 lptr
[ACTIVE_LEASES
] = &p
-> active
;
4751 lptr
[EXPIRED_LEASES
] = &p
-> expired
;
4752 lptr
[ABANDONED_LEASES
] = &p
-> abandoned
;
4753 lptr
[BACKUP_LEASES
] = &p
-> backup
;
4755 for (i
= FREE_LEASES
; i
<= BACKUP_LEASES
; i
++) {
4756 for (l
= *(lptr
[i
]); l
; l
= l
-> next
) {
4757 if (p
-> failover_peer
== state
&&
4759 (l
-> starts
!= MIN_TIME
||
4760 l
-> ends
!= MIN_TIME
)) ||
4761 l
-> tstp
> l
-> tsfp
)) {
4762 l
-> desired_binding_state
= l
-> binding_state
;
4763 dhcp_failover_queue_update (l
, 0);
4769 return ISC_R_SUCCESS
;
4773 dhcp_failover_process_update_request (dhcp_failover_state_t
*state
,
4774 failover_message_t
*msg
)
4776 /* Generate a fresh update queue. */
4777 dhcp_failover_generate_update_queue (state
, 0);
4779 /* If there's anything on the update queue (there shouldn't be
4780 anything on the ack queue), trigger an update done message
4781 when we get an ack for that lease. */
4782 if (state
-> update_queue_tail
) {
4783 lease_reference (&state
-> send_update_done
,
4784 state
-> update_queue_tail
, MDL
);
4785 dhcp_failover_send_updates (state
);
4786 log_info ("Update request from %s: sending update",
4789 /* Otherwise, there are no updates to send, so we can
4790 just send an UPDDONE message immediately. */
4791 dhcp_failover_send_update_done (state
);
4792 log_info ("Update request from %s: nothing pending",
4796 return ISC_R_SUCCESS
;
4800 dhcp_failover_process_update_request_all (dhcp_failover_state_t
*state
,
4801 failover_message_t
*msg
)
4803 /* Generate a fresh update queue that includes every lease. */
4804 dhcp_failover_generate_update_queue (state
, 1);
4806 if (state
-> update_queue_tail
) {
4807 lease_reference (&state
-> send_update_done
,
4808 state
-> update_queue_tail
, MDL
);
4809 dhcp_failover_send_updates (state
);
4810 log_info ("Update request all from %s: sending update",
4813 /* This should really never happen, but it could happen
4814 on a server that currently has no leases configured. */
4815 dhcp_failover_send_update_done (state
);
4816 log_info ("Update request all from %s: nothing pending",
4820 return ISC_R_SUCCESS
;
4824 dhcp_failover_process_update_done (dhcp_failover_state_t
*state
,
4825 failover_message_t
*msg
)
4827 log_info ("failover peer %s: peer update completed.",
4830 switch (state
-> me
.state
) {
4834 case communications_interrupted
:
4835 case resolution_interrupted
:
4841 break; /* shouldn't happen. */
4843 /* We got the UPDDONE, so we can go into normal state! */
4844 case potential_conflict
:
4845 dhcp_failover_set_state (state
, normal
);
4849 /* Wait for MCLT to expire before moving to recover_done,
4850 except that if both peers come up in recover, there is
4851 no point in waiting for MCLT to expire - this probably
4852 indicates the initial startup of a newly-configured
4854 if (state
-> me
.stos
+ state
-> mclt
> cur_time
&&
4855 state
-> partner
.state
!= recover
&&
4856 state
-> partner
.state
!= recover_done
) {
4857 dhcp_failover_set_state (state
, recover_wait
);
4858 #if defined (DEBUG_FAILOVER_TIMING)
4859 log_info ("add_timeout +%d %s",
4861 state
-> me
.stos
+ state
-> mclt
),
4862 "dhcp_failover_recover_done");
4864 add_timeout ((int)(state
-> me
.stos
+ state
-> mclt
),
4865 dhcp_failover_recover_done
,
4867 (tvref_t
)omapi_object_reference
,
4869 omapi_object_dereference
);
4871 dhcp_failover_recover_done (state
);
4874 return ISC_R_SUCCESS
;
4877 void dhcp_failover_recover_done (void *sp
)
4879 dhcp_failover_state_t
*state
= sp
;
4881 #if defined (DEBUG_FAILOVER_TIMING)
4882 log_info ("dhcp_failover_recover_done");
4885 dhcp_failover_set_state (state
, recover_done
);
4888 #if defined (DEBUG_FAILOVER_MESSAGES)
4889 /* Print hunks of failover messages, doing line breaks as appropriate.
4890 Note that this assumes syslog is being used, rather than, e.g., the
4891 Windows NT logging facility, where just dumping the whole message in
4892 one hunk would be more appropriate. */
4894 void failover_print (char *obuf
,
4895 unsigned *obufix
, unsigned obufmax
, const char *s
)
4897 int len
= strlen (s
);
4899 while (len
+ *obufix
+ 1 >= obufmax
) {
4900 log_debug ("%s", obuf
);
4902 log_debug ("%s", s
);
4908 strcpy (&obuf
[*obufix
], s
);
4911 #endif /* defined (DEBUG_FAILOVER_MESSAGES) */
4913 /* Taken from draft-ietf-dhc-loadb-01.txt: */
4914 /* A "mixing table" of 256 distinct values, in pseudo-random order. */
4915 unsigned char loadb_mx_tbl
[256] = {
4916 251, 175, 119, 215, 81, 14, 79, 191, 103, 49,
4917 181, 143, 186, 157, 0, 232, 31, 32, 55, 60,
4918 152, 58, 17, 237, 174, 70, 160, 144, 220, 90,
4919 57, 223, 59, 3, 18, 140, 111, 166, 203, 196,
4920 134, 243, 124, 95, 222, 179, 197, 65, 180, 48,
4921 36, 15, 107, 46, 233, 130, 165, 30, 123, 161,
4922 209, 23, 97, 16, 40, 91, 219, 61, 100, 10,
4923 210, 109, 250, 127, 22, 138, 29, 108, 244, 67,
4924 207, 9, 178, 204, 74, 98, 126, 249, 167, 116,
4925 34, 77, 193, 200, 121, 5, 20, 113, 71, 35,
4926 128, 13, 182, 94, 25, 226, 227, 199, 75, 27,
4927 41, 245, 230, 224, 43, 225, 177, 26, 155, 150,
4928 212, 142, 218, 115, 241, 73, 88, 105, 39, 114,
4929 62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
4930 154, 122, 12, 84, 82, 163, 44, 139, 228, 236,
4931 205, 242, 217, 11, 187, 146, 159, 64, 86, 239,
4932 195, 42, 106, 198, 118, 112, 184, 172, 87, 2,
4933 173, 117, 176, 229, 247, 253, 137, 185, 99, 164,
4934 102, 147, 45, 66, 231, 52, 141, 211, 194, 206,
4935 246, 238, 56, 110, 78, 248, 63, 240, 189, 93,
4936 92, 51, 53, 183, 19, 171, 72, 50, 33, 104,
4937 101, 69, 8, 252, 83, 120, 76, 135, 85, 54,
4938 202, 125, 188, 213, 96, 235, 136, 208, 162, 129,
4939 190, 132, 156, 38, 47, 1, 7, 254, 24, 4,
4940 216, 131, 89, 21, 28, 133, 37, 153, 149, 80,
4941 170, 68, 6, 169, 234, 151 };
4943 static unsigned char loadb_p_hash (const unsigned char *, unsigned);
4944 static unsigned char loadb_p_hash (const unsigned char *key
, unsigned len
)
4946 unsigned char hash
= len
;
4948 for(i
= len
; i
> 0; )
4949 hash
= loadb_mx_tbl
[hash
^ (key
[--i
])];
4953 int load_balance_mine (struct packet
*packet
, dhcp_failover_state_t
*state
)
4955 struct option_cache
*oc
;
4956 struct data_string ds
;
4957 unsigned char hbaix
;
4960 if (state
-> load_balance_max_secs
< ntohs (packet
-> raw
-> secs
)) {
4964 /* If we don't have a hash bucket array, we can't tell if this
4965 one's ours, so we assume it's not. */
4969 oc
= lookup_option (&dhcp_universe
, packet
-> options
,
4970 DHO_DHCP_CLIENT_IDENTIFIER
);
4971 memset (&ds
, 0, sizeof ds
);
4973 evaluate_option_cache (&ds
, packet
, (struct lease
*)0,
4974 (struct client_state
*)0,
4975 packet
-> options
, (struct option_state
*)0,
4976 &global_scope
, oc
, MDL
)) {
4977 hbaix
= loadb_p_hash (ds
.data
, ds
.len
);
4979 hbaix
= loadb_p_hash (packet
-> raw
-> chaddr
,
4980 packet
-> raw
-> hlen
);
4982 hm
= (state
-> hba
[hbaix
/ 8] & (1 << (hbaix
& 3)));
4983 if (state
-> i_am
== primary
)
4989 /* This deals with what to do with bind updates when
4990 we're in the normal state
4992 Note that tsfp had better be set from the latest bind update
4993 _before_ this function is called! */
4996 normal_binding_state_transition_check (struct lease
*lease
,
4997 dhcp_failover_state_t
*state
,
4998 binding_state_t binding_state
,
5001 binding_state_t new_state
;
5003 /* If there is no transition, it's no problem. */
5004 if (binding_state
== lease
-> binding_state
)
5005 return binding_state
;
5007 switch (lease
-> binding_state
) {
5010 switch (binding_state
) {
5017 /* If the lease was free, and our peer is primary,
5018 then it can make it active, or abandoned, or
5019 backup. Abandoned is treated like free in
5021 if (state
-> i_am
== secondary
)
5022 return binding_state
;
5024 /* Otherwise, it can't legitimately do any sort of
5025 state transition. Because the lease was free,
5026 and the error has already been made, we allow the
5027 peer to change its state anyway, but log a warning
5028 message in hopes that the error will be fixed. */
5029 case FTS_FREE
: /* for compiler */
5030 new_state
= binding_state
;
5034 log_fatal ("Impossible case at %s:%d.", MDL
);
5038 /* The secondary can't change the state of an active
5040 if (state
-> i_am
== primary
) {
5041 /* Except that the client may send the DHCPRELEASE
5042 to the secondary, and we have to accept that. */
5043 if (binding_state
== FTS_RELEASED
)
5044 return binding_state
;
5045 new_state
= lease
-> binding_state
;
5049 /* So this is only for transitions made by the primary: */
5050 switch (binding_state
) {
5053 /* Can't set a lease to free or backup until the
5054 peer agrees that it's expired. */
5055 if (tsfp
> cur_time
) {
5056 new_state
= lease
-> binding_state
;
5059 return binding_state
;
5062 /* XXX 65 should be the clock skew between the peers
5063 XXX plus a fudge factor. This code will result
5064 XXX in problems if MCLT is really short or the
5065 XXX max-lease-time is really short (less than the
5066 XXX fudge factor. */
5067 if (lease
-> ends
- 65 > cur_time
) {
5068 new_state
= lease
-> binding_state
;
5076 return binding_state
;
5079 log_fatal ("Impossible case at %s:%d.", MDL
);
5084 switch (binding_state
) {
5087 /* Can't set a lease to free or backup until the
5088 peer agrees that it's expired. */
5089 if (tsfp
> cur_time
) {
5090 new_state
= lease
-> binding_state
;
5093 return binding_state
;
5100 return binding_state
;
5103 log_fatal ("Impossible case at %s:%d.", MDL
);
5107 switch (binding_state
) {
5111 /* These are invalid state transitions - should we
5118 return binding_state
;
5121 log_fatal ("Impossible case at %s:%d.", MDL
);
5125 switch (binding_state
) {
5128 /* Can't set a lease to free or backup until the
5129 peer agrees that it's expired. */
5130 if (tsfp
> cur_time
) {
5131 new_state
= lease
-> binding_state
;
5134 return binding_state
;
5141 return binding_state
;
5144 log_fatal ("Impossible case at %s:%d.", MDL
);
5148 switch (binding_state
) {
5154 /* If the lease was in backup, and our peer
5155 is secondary, then it can make it active
5157 if (state
-> i_am
== primary
)
5158 return binding_state
;
5160 /* Either the primary or the secondary can
5161 reasonably move a lease from the backup
5162 state to the free state. */
5164 return binding_state
;
5167 new_state
= lease
-> binding_state
;
5171 log_fatal ("Impossible case at %s:%d.", MDL
);
5176 log_fatal ("Impossible case at %s:%d.", MDL
);
5183 /* Determine whether the state transition is okay when we're potentially
5184 in conflict with the peer. */
5186 conflict_binding_state_transition_check (struct lease
*lease
,
5187 dhcp_failover_state_t
*state
,
5188 binding_state_t binding_state
,
5191 binding_state_t new_state
;
5193 /* If there is no transition, it's no problem. */
5194 if (binding_state
== lease
-> binding_state
)
5195 new_state
= binding_state
;
5197 switch (lease
-> binding_state
) {
5198 /* If we think the lease is not in use, then the
5199 state into which the partner put it is just fine,
5207 new_state
= binding_state
;
5210 /* If we think the lease *is* in use, then we're not
5211 going to take the partner's change if the partner
5212 thinks it's free. */
5214 switch (binding_state
) {
5218 new_state
= lease
-> binding_state
;
5224 if (lease
-> ends
> cur_time
)
5226 lease
-> binding_state
;
5228 new_state
= binding_state
;
5232 new_state
= binding_state
;
5236 log_fatal ("Impossible case at %s:%d.", MDL
);
5242 log_fatal ("Impossible case at %s:%d.", MDL
);
5249 /* We can reallocate a lease under the following circumstances:
5251 (1) It belongs to us - it's FTS_FREE, and we're primary, or it's
5252 FTS_BACKUP, and we're secondary.
5253 (2) We're in partner_down, and the lease is not active, and we
5254 can be sure that the other server didn't make it active.
5255 We can only be sure that the server didn't make it active
5256 when we are in the partner_down state and one of the following
5257 two conditions holds:
5258 (a) in the case that the time sent from the peer is earlier than
5259 the time we entered the partner_down state, at least MCLT has
5260 gone by since we entered partner_down, or
5261 (b) in the case that the time sent from the peer is later than
5262 the time when we entered partner_down, the current time is
5263 later than the time sent from the peer by at least MCLT. */
5265 int lease_mine_to_reallocate (struct lease
*lease
)
5267 dhcp_failover_state_t
*peer
;
5269 if (lease
&& lease
-> pool
&&
5270 lease
-> pool
-> failover_peer
) {
5271 peer
= lease
-> pool
-> failover_peer
;
5272 switch (lease
-> binding_state
) {
5277 if (peer
-> i_am
== primary
)
5279 if (peer
-> service_state
== service_partner_down
&&
5280 (lease
-> tsfp
< peer
-> me
.stos
5281 ? peer
-> me
.stos
+ peer
-> mclt
< cur_time
5282 : lease
-> tsfp
+ peer
-> mclt
< cur_time
))
5290 if (peer
-> service_state
== service_partner_down
&&
5291 (lease
-> tsfp
< peer
-> me
.stos
5292 ? peer
-> me
.stos
+ peer
-> mclt
< cur_time
5293 : lease
-> tsfp
+ peer
-> mclt
< cur_time
))
5297 if (peer
-> i_am
== secondary
)
5299 if (peer
-> service_state
== service_partner_down
&&
5300 (lease
-> tsfp
< peer
-> me
.stos
5301 ? peer
-> me
.stos
+ peer
-> mclt
< cur_time
5302 : lease
-> tsfp
+ peer
-> mclt
< cur_time
))
5309 return !(lease
-> binding_state
!= FTS_FREE
&&
5310 lease
-> binding_state
!= FTS_BACKUP
);
5315 static isc_result_t
failover_message_reference (failover_message_t
**mp
,
5316 failover_message_t
*m
,
5317 const char *file
, int line
)
5321 return ISC_R_SUCCESS
;
5324 static isc_result_t
failover_message_dereference (failover_message_t
**mp
,
5325 const char *file
, int line
)
5327 failover_message_t
*m
;
5330 if (m
-> refcnt
== 0) {
5332 failover_message_dereference (&m
-> next
,
5334 if (m
-> chaddr
.data
)
5335 dfree (m
-> chaddr
.data
, file
, line
);
5336 if (m
-> client_identifier
.data
)
5337 dfree (m
-> client_identifier
.data
, file
, line
);
5339 dfree (m
-> hba
.data
, file
, line
);
5340 if (m
-> message
.data
)
5341 dfree (m
-> message
.data
, file
, line
);
5342 if (m
-> reply_options
.data
)
5343 dfree (m
-> reply_options
.data
, file
, line
);
5344 if (m
-> request_options
.data
)
5345 dfree (m
-> request_options
.data
, file
, line
);
5346 if (m
-> vendor_class
.data
)
5347 dfree (m
-> vendor_class
.data
, file
, line
);
5348 if (m
-> vendor_options
.data
)
5349 dfree (m
-> vendor_options
.data
, file
, line
);
5351 dfree (m
-> ddns
.data
, file
, line
);
5352 dfree (*mp
, file
, line
);
5355 return ISC_R_SUCCESS
;
5358 OMAPI_OBJECT_ALLOC (dhcp_failover_state
, dhcp_failover_state_t
,
5359 dhcp_type_failover_state
)
5360 OMAPI_OBJECT_ALLOC (dhcp_failover_listener
, dhcp_failover_listener_t
,
5361 dhcp_type_failover_listener
)
5362 OMAPI_OBJECT_ALLOC (dhcp_failover_link
, dhcp_failover_link_t
,
5363 dhcp_type_failover_link
)
5364 #endif /* defined (FAILOVER_PROTOCOL) */
5366 const char *binding_state_print (enum failover_state state
)