3 Failover protocol support code... */
6 * Copyright (c) 1999-2000 Internet Software Consortium.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of The Internet Software Consortium nor the names
19 * of its contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * This software has been written for the Internet Software Consortium
37 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
38 * To learn more about the Internet Software Consortium, see
39 * ``http://www.isc.org/''. To learn more about Vixie Enterprises,
40 * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
41 * ``http://www.nominum.com''.
45 static char copyright
[] =
46 "$Id: failover.c,v 1.13 2000/05/04 18:58:15 mellon Exp $ Copyright (c) 1999-2000 The Internet Software Consortium. All rights reserved.\n";
51 #include <omapip/omapip_p.h>
53 #if defined (FAILOVER_PROTOCOL)
54 static struct hash_table
*failover_hash
;
55 static dhcp_failover_state_t
*failover_states
;
56 static isc_result_t
do_a_failover_option (omapi_object_t
*,
57 dhcp_failover_link_t
*);
59 void dhcp_failover_startup ()
61 dhcp_failover_state_t
*state
;
64 for (state
= failover_states
; state
; state
= state
-> next
) {
65 dhcp_failover_state_transition (state
, "startup");
66 if (state
-> i_am
== secondary
) {
67 status
= (dhcp_failover_listen
68 ((omapi_object_t
*)state
));
69 if (status
!= ISC_R_SUCCESS
) {
70 add_timeout (cur_time
+ 90,
71 dhcp_failover_listener_restart
,
75 status
= (dhcp_failover_link_initiate
76 ((omapi_object_t
*)state
));
77 if (status
!= ISC_R_SUCCESS
) {
78 add_timeout (cur_time
+ 90,
79 dhcp_failover_reconnect
, state
);
82 if (status
!= ISC_R_SUCCESS
) {
83 log_error ("failover peer %s: %s", state
-> name
,
84 isc_result_totext (status
));
89 void dhcp_failover_write_all_states ()
91 dhcp_failover_state_t
*state
;
93 for (state
= failover_states
; state
; state
= state
-> next
) {
94 write_failover_state (state
);
98 isc_result_t
enter_failover_peer (peer
)
99 dhcp_failover_state_t
*peer
;
101 dhcp_failover_state_t
*dup
= (dhcp_failover_state_t
*)0;
104 status
= find_failover_peer (&dup
, peer
-> name
);
105 if (status
== ISC_R_NOTFOUND
) {
106 if (failover_states
) {
107 omapi_object_reference
108 ((omapi_object_t
**)&peer
-> next
,
109 (omapi_object_t
*)failover_states
, MDL
);
110 omapi_object_dereference
111 ((omapi_object_t
**)&failover_states
, MDL
);
113 omapi_object_reference ((omapi_object_t
**)&failover_states
,
114 (omapi_object_t
*)peer
, MDL
);
115 return ISC_R_SUCCESS
;
117 if (status
== ISC_R_SUCCESS
)
123 isc_result_t
find_failover_peer (peer
, name
)
124 dhcp_failover_state_t
**peer
;
127 dhcp_failover_state_t
*p
;
129 for (p
= failover_states
; p
; p
= p
-> next
)
130 if (!strcmp (name
, p
-> name
))
133 return omapi_object_reference ((omapi_object_t
**)peer
,
134 (omapi_object_t
*)p
, MDL
);
135 return ISC_R_NOTFOUND
;
138 /* The failover protocol has three objects associated with it. For
139 each failover partner declaration in the dhcpd.conf file, primary
140 or secondary, there is a failover_state object. For any primary or
141 secondary state object that has a connection to its peer, there is
142 also a failover_link object, which has its own input state seperate
143 from the failover protocol state for managing the actual bytes
144 coming in off the wire. Finally, there will be one listener object
145 for every distinct port number associated with a secondary
146 failover_state object. Normally all secondary failover_state
147 objects are expected to listen on the same port number, so there
148 need be only one listener object, but if different port numbers are
149 specified for each failover object, there could be as many as one
150 listener object for each secondary failover_state object. */
152 /* This, then, is the implemention of the failover link object. */
154 isc_result_t
dhcp_failover_link_initiate (omapi_object_t
*h
)
157 dhcp_failover_link_t
*obj
;
158 omapi_value_t
*value
= (omapi_value_t
*)0;
159 dhcp_failover_state_t
*state
;
162 struct data_string ds
;
163 omapi_addr_list_t
*addrs
= (omapi_addr_list_t
*)0;
164 omapi_addr_t local_addr
;
166 /* Find the failover state in the object chain. */
167 for (o
= h
; o
-> outer
; o
= o
-> outer
)
169 for (; o
; o
= o
-> inner
) {
170 if (o
-> type
== dhcp_type_failover_state
)
174 return ISC_R_INVALIDARG
;
175 state
= (dhcp_failover_state_t
*)o
;
177 obj
= (dhcp_failover_link_t
*)dmalloc (sizeof *obj
, MDL
);
179 return ISC_R_NOMEMORY
;
180 memset (obj
, 0, sizeof *obj
);
182 obj
-> type
= dhcp_type_failover_link
;
183 option_cache_reference (&obj
-> peer_address
, state
-> address
, MDL
);
184 obj
-> peer_port
= state
-> port
;
185 omapi_object_reference ((omapi_object_t
**)&obj
-> state_object
,
186 (omapi_object_t
*)state
, MDL
);
188 memset (&ds
, 0, sizeof ds
);
189 if (!evaluate_option_cache (&ds
, (struct packet
*)0, (struct lease
*)0,
190 (struct option_state
*)0,
191 (struct option_state
*)0,
192 &global_scope
, obj
-> peer_address
, MDL
)) {
193 omapi_object_dereference ((omapi_object_t
**)&obj
, MDL
);
194 return ISC_R_UNEXPECTED
;
197 /* Make an omapi address list out of a buffer containing zero or more
199 status
= omapi_addr_list_new (&addrs
, ds
.len
/ 4, MDL
);
200 if (status
!= ISC_R_SUCCESS
) {
201 omapi_object_dereference ((omapi_object_t
**)&obj
, MDL
);
205 for (i
= 0; i
< addrs
-> count
; i
++) {
206 addrs
-> addresses
[i
].addrtype
= AF_INET
;
207 addrs
-> addresses
[i
].addrlen
= sizeof (struct in_addr
);
208 memcpy (addrs
-> addresses
[i
].address
,
209 &ds
.data
[i
* 4], sizeof (struct in_addr
));
210 addrs
-> addresses
[i
].port
= obj
-> peer_port
;
212 data_string_forget (&ds
, MDL
);
214 /* Now figure out the local address that we're supposed to use. */
215 if (!evaluate_option_cache (&ds
, (struct packet
*)0, (struct lease
*)0,
216 (struct option_state
*)0,
217 (struct option_state
*)0,
218 &global_scope
, state
-> server_addr
,
220 memset (&local_addr
, 0, sizeof local_addr
);
221 local_addr
.addrtype
= AF_INET
;
222 local_addr
.addrlen
= sizeof (struct in_addr
);
223 if (!state
-> server_identifier
.len
) {
224 log_fatal ("failover peer %s: no identifier.",
228 if (ds
.len
!= sizeof (struct in_addr
)) {
229 data_string_forget (&ds
, MDL
);
230 omapi_object_dereference ((omapi_object_t
**)&obj
,
232 omapi_addr_list_dereference (&addrs
, MDL
);
233 return ISC_R_INVALIDARG
;
235 local_addr
.addrtype
= AF_INET
;
236 local_addr
.addrlen
= ds
.len
;
237 memcpy (local_addr
.address
, ds
.data
, ds
.len
);
238 if (!state
-> server_identifier
.len
)
239 data_string_copy (&state
-> server_identifier
,
241 data_string_forget (&ds
, MDL
);
242 local_addr
.port
= 0; /* Let the O.S. choose. */
245 status
= omapi_connect_list ((omapi_object_t
*)obj
,
247 omapi_addr_list_dereference (&addrs
, MDL
);
249 /* If we didn't succeed in starting to connect, fail. */
250 if (status
!= ISC_R_SUCCESS
) {
252 omapi_object_dereference ((omapi_object_t
**)&obj
, MDL
);
255 omapi_object_dereference ((omapi_object_t
**)&obj
, MDL
);
256 return ISC_R_SUCCESS
;
259 isc_result_t
dhcp_failover_link_signal (omapi_object_t
*h
,
260 const char *name
, va_list ap
)
263 dhcp_failover_link_t
*link
;
267 dhcp_failover_state_t
*s
, *state
= (dhcp_failover_state_t
*)0;
269 if (h
-> type
!= dhcp_type_failover_link
) {
270 /* XXX shouldn't happen. Put an assert here? */
271 return ISC_R_UNEXPECTED
;
273 link
= (dhcp_failover_link_t
*)h
;
275 if (!strcmp (name
, "connect")) {
276 /* If we're primary, send the connect message. */
277 status
= dhcp_failover_send_connect (h
);
278 if (status
!= ISC_R_SUCCESS
)
279 omapi_disconnect (h
-> outer
, 1);
283 if (!strcmp (name
, "disconnect")) {
284 if (link
-> state_object
&&
285 link
-> state_object
-> i_am
== primary
) {
286 add_timeout (cur_time
+ 90, dhcp_failover_reconnect
,
287 link
-> state_object
);
288 /* XXX this is a reference! */
290 return ISC_R_SUCCESS
;
293 /* Not a signal we recognize? */
294 if (strcmp (name
, "ready")) {
295 if (h
-> inner
&& h
-> inner
-> type
-> signal_handler
)
296 return (*(h
-> inner
-> type
-> signal_handler
))
297 (h
-> inner
, name
, ap
);
298 return ISC_R_NOTFOUND
;
301 if (!h
-> outer
|| h
-> outer
-> type
!= omapi_type_connection
)
302 return ISC_R_INVALIDARG
;
305 /* We get here because we requested that we be woken up after
306 some number of bytes were read, and that number of bytes
307 has in fact been read. */
308 switch (link
-> state
) {
309 case dhcp_flink_start
:
310 link
-> state
= dhcp_flink_message_length_wait
;
311 if ((omapi_connection_require (c
, 2)) != ISC_R_SUCCESS
)
313 case dhcp_flink_message_length_wait
:
315 link
-> state
= dhcp_flink_message_wait
;
316 link
-> imsg
= dmalloc (sizeof (failover_message_t
), MDL
);
320 dfree (link
-> imsg
, MDL
);
321 link
-> imsg
= (failover_message_t
*)0;
323 link
-> state
= dhcp_flink_disconnected
;
324 omapi_disconnect (c
, 1);
325 /* XXX just blow away the protocol state now?
326 XXX or will disconnect blow it away? */
327 return ISC_R_UNEXPECTED
;
329 memset (link
-> imsg
, 0, sizeof (failover_message_t
));
330 /* Get the length: */
331 omapi_connection_get_uint16 (c
, &link
-> imsg_len
);
332 link
-> imsg_count
= 0; /* Bytes read. */
334 /* Maximum of 2048 bytes in any failover message. */
335 if (link
-> imsg_len
> DHCP_FAILOVER_MAX_MESSAGE_SIZE
)
336 goto dhcp_flink_fail
;
338 if ((omapi_connection_require (c
, link
-> imsg_len
- 2U)) !=
341 case dhcp_flink_message_wait
:
342 /* Read in the message. At this point we have the
343 entire message in the input buffer. For each
344 incoming value ID, set a bit in the bitmask
345 indicating that we've gotten it. Maybe flag an
346 error message if the bit is already set. Once
347 we're done reading, we can check the bitmask to
348 make sure that the required fields for each message
349 have been included. */
351 link
-> imsg_count
+= 2; /* Count the length as read. */
353 /* Get message type. */
354 omapi_connection_copyout (&link
-> imsg
-> type
, c
, 1);
355 link
-> imsg_count
++;
357 /* Get message payload offset. */
358 omapi_connection_copyout (&link
-> imsg_payoff
, c
, 1);
359 link
-> imsg_count
++;
361 /* Get message time. */
362 omapi_connection_get_uint32 (c
, &link
-> imsg
-> time
);
363 link
-> imsg_count
+= 4;
365 /* Get transaction ID. */
366 omapi_connection_get_uint32 (c
, &link
-> imsg
-> xid
);
367 link
-> imsg_count
+= 4;
369 /* Skip over any portions of the message header that we
371 if (link
-> imsg_payoff
- link
-> imsg_count
) {
372 omapi_connection_copyout ((unsigned char *)0, c
,
373 (link
-> imsg_payoff
-
374 link
-> imsg_count
));
375 link
-> imsg_count
= link
-> imsg_payoff
;
378 /* Now start sucking options off the wire. */
379 while (link
-> imsg_count
< link
-> imsg_len
) {
380 if (do_a_failover_option (c
, link
) != ISC_R_SUCCESS
)
381 goto dhcp_flink_fail
;
384 /* If it's a connect message, try to associate it with
386 /* XXX this should be authenticated! */
387 if (link
-> imsg
-> type
== FTM_CONNECT
) {
388 /* See if we can find a failover_state object that
389 matches this connection. This message should only
390 be received by a secondary from a primary. */
391 for (s
= failover_states
; s
; s
= s
-> next
) {
392 if (dhcp_failover_state_match
393 (s
, (u_int8_t
*)&link
-> imsg
-> server_addr
,
394 sizeof link
-> imsg
-> server_addr
))
398 /* If we can't find a failover protocol state
399 for this remote host, drop the connection */
401 /* XXX Send a refusal message first?
402 XXX Look in protocol spec for guidance. */
403 log_error ("Failover CONNECT from %s %d.%d.%d.%d",
406 (&link
-> imsg
-> server_addr
)) [0],
408 (&link
-> imsg
-> server_addr
)) [1],
410 (&link
-> imsg
-> server_addr
)) [2],
412 (&link
-> imsg
-> server_addr
)) [3]);
413 dhcp_failover_send_connectack
414 ((omapi_object_t
*)link
,
415 FTR_INVALID_PARTNER
);
416 omapi_disconnect (c
, 0);
417 link
-> state
= dhcp_flink_disconnected
;
418 return ISC_R_SUCCESS
;
421 if (!link
-> state_object
)
422 omapi_object_reference
423 ((omapi_object_t
**)&link
-> state_object
,
424 (omapi_object_t
*)state
, MDL
);
425 option_cache_reference
426 (&link
-> peer_address
, state
-> address
, MDL
);
429 /* If we don't have a state object at this point, it's
430 some kind of bogus situation, so just drop the
432 if (!link
-> state_object
) {
433 omapi_disconnect (c
, 1);
434 link
-> state
= dhcp_flink_disconnected
;
435 return ISC_R_INVALIDARG
;
438 /* Once we have the entire message, and we've validated
439 it as best we can here, pass it to the parent. */
440 omapi_signal ((omapi_object_t
*)link
-> state_object
,
442 link
-> state
= dhcp_flink_message_length_wait
;
443 dfree (link
-> imsg
, MDL
);
444 link
-> imsg
= (failover_message_t
*)0;
445 /* XXX This is dangerous because we could get into a tight
446 XXX loop reading input without servicing any other stuff.
447 XXX There needs to be a way to relinquish control but
448 XXX get it back immediately if there's no other work to
450 if ((omapi_connection_require (c
, 2)) == ISC_R_SUCCESS
)
455 /* XXX should never get here. Assertion? */
458 return ISC_R_SUCCESS
;
461 static isc_result_t
do_a_failover_option (c
, link
)
463 dhcp_failover_link_t
*link
;
465 u_int16_t option_code
;
466 u_int16_t option_len
;
473 if (link
-> imsg_count
+ 2 > link
-> imsg_len
) {
474 log_error ("FAILOVER: message overflow at option code.");
475 return ISC_R_PROTOCOLERROR
;
478 /* Get option code. */
479 omapi_connection_get_uint16 (c
, &option_code
);
480 link
-> imsg_count
+= 2;
482 if (link
-> imsg_count
+ 2 > link
-> imsg_len
) {
483 log_error ("FAILOVER: message overflow at length.");
484 return ISC_R_PROTOCOLERROR
;
487 /* Get option length. */
488 omapi_connection_get_uint16 (c
, &option_len
);
489 link
-> imsg_count
+= 2;
491 if (link
-> imsg_count
+ option_len
> link
-> imsg_len
) {
492 log_error ("FAILOVER: message overflow at data.");
493 return ISC_R_PROTOCOLERROR
;
496 /* If it's an unknown code, skip over it. */
497 if (option_code
> FTO_MAX
) {
498 #if defined (FAILOVER_PROTOCOL_DEBUG) && defined (FAILOVER_DEBUG_VERBOSE)
499 log_debug (" option code %d len %d (not recognized)",
500 option_code
, option_len
);
502 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
503 link
-> imsg_count
+= option_len
;
504 return ISC_R_SUCCESS
;
507 /* If it's the digest, do it now. */
508 if (ft_options
[option_code
].type
== FT_DIGEST
) {
509 link
-> imsg_count
+= option_len
;
510 if (link
-> imsg_count
!= link
-> imsg_len
) {
511 log_error ("FAILOVER: digest not at end of message");
512 return ISC_R_PROTOCOLERROR
;
514 #if defined (FAILOVER_PROTOCOL_DEBUG) && defined (FAILOVER_DEBUG_VERBOSE)
515 log_debug (" option %s len %d",
516 ft_options
[option_code
].name
, option_len
);
518 /* For now, just dump it. */
519 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
520 return ISC_R_SUCCESS
;
523 /* Only accept an option once. */
524 if (link
-> imsg
-> options_present
& ft_options
[option_code
].bit
) {
525 log_error ("FAILOVER: duplicate option %s",
526 ft_options
[option_code
].name
);
527 return ISC_R_PROTOCOLERROR
;
530 /* Make sure the option is appropriate for this type of message.
531 Really, any option is generally allowed for any message, and the
532 cases where this is not true are too complicated to represent in
533 this way - what this code is doing is to just avoid saving the
534 value of an option we don't have any way to use, which allows
535 us to make the failover_message structure smaller. */
536 if (ft_options
[option_code
].bit
&&
537 !(fto_allowed
[link
-> imsg
-> type
] &
538 ft_options
[option_code
].bit
)) {
539 omapi_connection_copyout ((unsigned char *)0, c
, option_len
);
540 link
-> imsg_count
+= option_len
;
541 return ISC_R_SUCCESS
;
544 /* Figure out how many elements, how big they are, and where
546 if (ft_options
[option_code
].num_present
) {
547 /* If this option takes a fixed number of elements,
548 we expect the space for them to be preallocated,
549 and we can just read the data in. */
551 op
= ((unsigned char *)link
-> imsg
) +
552 ft_options
[option_code
].offset
;
553 op_size
= ft_sizes
[ft_options
[option_code
].type
];
554 op_count
= ft_options
[option_code
].num_present
;
556 if (option_len
!= op_size
* op_count
) {
557 log_error ("FAILOVER: option size (%d:%d), option %s",
559 (ft_sizes
[ft_options
[option_code
].type
] *
560 ft_options
[option_code
].num_present
),
561 ft_options
[option_code
].name
);
562 return ISC_R_PROTOCOLERROR
;
565 failover_option_t
*fo
;
567 /* FT_DDNS* are special - one or two bytes of status
568 followed by the client FQDN. */
569 if (ft_options
[option_code
].type
== FT_DDNS1
||
570 ft_options
[option_code
].type
== FT_DDNS1
) {
573 (((char *)&link
-> imsg
) +
574 ft_options
[option_code
].offset
));
576 op_count
= (ft_options
[option_code
].type
== FT_DDNS1
579 omapi_connection_copyout (&ddns
-> codes
[0],
581 link
-> imsg_count
+= op_count
;
583 ddns
-> codes
[1] = 0;
585 op_count
= option_len
- op_count
;
587 ddns
-> length
= op_count
;
588 ddns
-> data
= dmalloc (op_count
, MDL
);
590 log_error ("FAILOVER: no memory getting%s(%d)",
591 " DNS data ", op_count
);
593 /* Actually, NO_MEMORY, but if we lose here
594 we have to drop the connection. */
595 return ISC_R_PROTOCOLERROR
;
597 omapi_connection_copyout (ddns
-> data
, c
, op_count
);
601 /* A zero for num_present means that any number of
602 elements can appear, so we have to figure out how
603 many we got from the length of the option, and then
604 fill out a failover_option structure describing the
606 op_size
= ft_sizes
[ft_options
[option_code
].type
];
608 /* Make sure that option data length is a multiple of the
609 size of the data type being sent. */
610 if (op_size
> 1 && option_len
% op_size
) {
611 log_error ("FAILOVER: option_len %d not %s%d",
612 option_len
, "multiple of ", op_size
);
613 return ISC_R_PROTOCOLERROR
;
616 op_count
= option_len
/ op_size
;
618 fo
= ((failover_option_t
*)
619 (((char *)&link
-> imsg
) +
620 ft_options
[option_code
].offset
));
622 fo
-> count
= op_count
;
623 fo
-> data
= dmalloc (option_len
, MDL
);
625 log_error ("FAILOVER: no memory getting %s (%d)",
626 "option data", op_count
);
628 return ISC_R_PROTOCOLERROR
;
633 /* For single-byte message values and multi-byte values that
634 don't need swapping, just read them in all at once. */
635 if (op_size
== 1 || ft_options
[option_code
].type
== FT_IPADDR
) {
636 omapi_connection_copyout ((unsigned char *)op
, c
, option_len
);
637 link
-> imsg_count
+= option_len
;
641 /* For values that require swapping, read them in one at a time
642 using routines that swap bytes. */
643 for (i
= 0; i
< op_count
; i
++) {
644 switch (ft_options
[option_code
].type
) {
646 omapi_connection_get_uint32 (c
, (u_int32_t
*)op
);
648 link
-> imsg_count
+= 4;
652 omapi_connection_get_uint16 (c
, (u_int16_t
*)op
);
654 link
-> imsg_count
+= 2;
658 /* Everything else should have been handled
660 log_error ("FAILOVER: option %s: bad type %d",
661 ft_options
[option_code
].name
,
662 ft_options
[option_code
].type
);
663 return ISC_R_PROTOCOLERROR
;
667 /* Remember that we got this option. */
668 link
-> imsg
-> options_present
|= ft_options
[option_code
].bit
;
669 return ISC_R_SUCCESS
;
672 isc_result_t
dhcp_failover_link_set_value (omapi_object_t
*h
,
674 omapi_data_string_t
*name
,
675 omapi_typed_data_t
*value
)
677 if (h
-> type
!= omapi_type_protocol
)
678 return ISC_R_INVALIDARG
;
680 /* Never valid to set these. */
681 if (!omapi_ds_strcmp (name
, "link-port") ||
682 !omapi_ds_strcmp (name
, "link-name") ||
683 !omapi_ds_strcmp (name
, "link-state"))
686 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
687 return (*(h
-> inner
-> type
-> set_value
))
688 (h
-> inner
, id
, name
, value
);
689 return ISC_R_NOTFOUND
;
692 isc_result_t
dhcp_failover_link_get_value (omapi_object_t
*h
,
694 omapi_data_string_t
*name
,
695 omapi_value_t
**value
)
697 dhcp_failover_link_t
*link
;
699 if (h
-> type
!= omapi_type_protocol
)
700 return ISC_R_INVALIDARG
;
701 link
= (dhcp_failover_link_t
*)h
;
703 if (!omapi_ds_strcmp (name
, "link-port")) {
704 return omapi_make_int_value (value
, name
,
705 (int)link
-> peer_port
, MDL
);
706 } else if (!omapi_ds_strcmp (name
, "link-state")) {
707 if (link
-> state
< 0 ||
708 link
-> state
>= dhcp_flink_state_max
)
709 return omapi_make_string_value (value
, name
,
710 "invalid link state",
712 return omapi_make_string_value
714 dhcp_flink_state_names
[link
-> state
], MDL
);
717 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
718 return (*(h
-> inner
-> type
-> get_value
))
719 (h
-> inner
, id
, name
, value
);
720 return ISC_R_NOTFOUND
;
723 isc_result_t
dhcp_failover_link_destroy (omapi_object_t
*h
,
724 const char *file
, int line
)
726 dhcp_failover_link_t
*link
;
727 if (h
-> type
!= dhcp_type_failover_link
)
728 return ISC_R_INVALIDARG
;
729 link
= (dhcp_failover_link_t
*)h
;
731 dfree (link
-> imsg
, file
, line
);
732 link
-> imsg
= (failover_message_t
*)0;
734 if (link
-> state_object
)
735 omapi_object_dereference
736 ((omapi_object_t
**)&link
-> state_object
, MDL
);
737 return ISC_R_SUCCESS
;
740 /* Write all the published values associated with the object through the
741 specified connection. */
743 isc_result_t
dhcp_failover_link_stuff_values (omapi_object_t
*c
,
747 dhcp_failover_link_t
*link
;
750 if (l
-> type
!= dhcp_type_failover_link
)
751 return ISC_R_INVALIDARG
;
752 link
= (dhcp_failover_link_t
*)l
;
754 status
= omapi_connection_put_name (c
, "link-port");
755 if (status
!= ISC_R_SUCCESS
)
757 status
= omapi_connection_put_uint32 (c
, sizeof (int));
758 if (status
!= ISC_R_SUCCESS
)
760 status
= omapi_connection_put_uint32 (c
, link
-> peer_port
);
761 if (status
!= ISC_R_SUCCESS
)
764 status
= omapi_connection_put_name (c
, "link-state");
765 if (status
!= ISC_R_SUCCESS
)
767 if (link
-> state
< 0 ||
768 link
-> state
>= dhcp_flink_state_max
)
769 status
= omapi_connection_put_string (c
, "invalid link state");
771 status
= (omapi_connection_put_string
772 (c
, dhcp_flink_state_names
[link
-> state
]));
773 if (status
!= ISC_R_SUCCESS
)
776 if (link
-> inner
&& link
-> inner
-> type
-> stuff_values
)
777 return (*(link
-> inner
-> type
-> stuff_values
)) (c
, id
,
779 return ISC_R_SUCCESS
;
782 /* Set up a listener for the omapi protocol. The handle stored points to
783 a listener object, not a protocol object. */
785 isc_result_t
dhcp_failover_listen (omapi_object_t
*h
)
788 dhcp_failover_listener_t
*obj
;
789 omapi_value_t
*value
= (omapi_value_t
*)0;
790 omapi_addr_t local_addr
;
793 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
794 "local-port", &value
);
795 if (status
!= ISC_R_SUCCESS
)
797 if (!value
-> value
) {
798 omapi_value_dereference (&value
, MDL
);
799 return ISC_R_INVALIDARG
;
802 status
= omapi_get_int_value (&port
, value
-> value
);
803 omapi_value_dereference (&value
, MDL
);
804 if (status
!= ISC_R_SUCCESS
)
806 local_addr
.port
= port
;
808 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
809 "local-address", &value
);
810 if (status
!= ISC_R_SUCCESS
)
812 if (!value
-> value
) {
814 omapi_value_dereference (&value
, MDL
);
815 return ISC_R_INVALIDARG
;
818 if (value
-> value
-> type
!= omapi_datatype_data
||
819 value
-> value
-> u
.buffer
.len
!= sizeof (struct in_addr
))
822 memcpy (local_addr
.address
, value
-> value
-> u
.buffer
.value
,
823 value
-> value
-> u
.buffer
.len
);
824 local_addr
.addrlen
= value
-> value
-> u
.buffer
.len
;
825 local_addr
.addrtype
= AF_INET
;
827 omapi_value_dereference (&value
, MDL
);
829 obj
= (dhcp_failover_listener_t
*)dmalloc (sizeof *obj
, MDL
);
831 return ISC_R_NOMEMORY
;
832 memset (obj
, 0, sizeof *obj
);
834 obj
-> type
= dhcp_type_failover_listener
;
835 obj
-> local_port
= local_addr
.port
;
837 status
= omapi_listen_addr ((omapi_object_t
*)obj
, &local_addr
, 1);
838 if (status
!= ISC_R_SUCCESS
)
841 status
= omapi_object_reference (&h
-> outer
,
842 (omapi_object_t
*)obj
, MDL
);
843 if (status
!= ISC_R_SUCCESS
) {
844 omapi_object_dereference ((omapi_object_t
**)&obj
, MDL
);
847 status
= omapi_object_reference (&obj
-> inner
, h
, MDL
);
848 if (status
!= ISC_R_SUCCESS
) {
849 omapi_object_dereference ((omapi_object_t
**)&obj
, MDL
);
853 status
= omapi_object_dereference ((omapi_object_t
**)&obj
, MDL
);
857 /* Signal handler for protocol listener - if we get a connect signal,
858 create a new protocol connection, otherwise pass the signal down. */
860 isc_result_t
dhcp_failover_listener_signal (omapi_object_t
*o
,
861 const char *name
, va_list ap
)
864 omapi_connection_object_t
*c
;
865 dhcp_failover_link_t
*obj
;
866 dhcp_failover_listener_t
*p
;
867 dhcp_failover_state_t
*s
, *state
= (dhcp_failover_state_t
*)0;
869 if (!o
|| o
-> type
!= dhcp_type_failover_listener
)
870 return ISC_R_INVALIDARG
;
871 p
= (dhcp_failover_listener_t
*)o
;
873 /* Not a signal we recognize? */
874 if (strcmp (name
, "connect")) {
875 if (p
-> inner
&& p
-> inner
-> type
-> signal_handler
)
876 return (*(p
-> inner
-> type
-> signal_handler
))
877 (p
-> inner
, name
, ap
);
878 return ISC_R_NOTFOUND
;
881 c
= va_arg (ap
, omapi_connection_object_t
*);
882 if (!c
|| c
-> type
!= omapi_type_connection
)
883 return ISC_R_INVALIDARG
;
885 obj
= (dhcp_failover_link_t
*)dmalloc (sizeof *obj
, MDL
);
887 return ISC_R_NOMEMORY
;
888 memset (obj
, 0, sizeof *obj
);
890 obj
-> type
= dhcp_type_failover_link
;
891 obj
-> peer_port
= ntohs (c
-> remote_addr
.sin_port
);
893 status
= omapi_object_reference (&obj
-> outer
,
894 (omapi_object_t
*)c
, MDL
);
895 if (status
!= ISC_R_SUCCESS
) {
897 omapi_object_dereference ((omapi_object_t
**)&obj
, MDL
);
898 omapi_disconnect ((omapi_object_t
*)c
, 1);
902 status
= omapi_object_reference (&c
-> inner
,
903 (omapi_object_t
*)obj
, MDL
);
904 if (status
!= ISC_R_SUCCESS
)
907 omapi_object_dereference ((omapi_object_t
**)&obj
, MDL
);
911 isc_result_t
dhcp_failover_listener_set_value (omapi_object_t
*h
,
913 omapi_data_string_t
*name
,
914 omapi_typed_data_t
*value
)
916 if (h
-> type
!= dhcp_type_failover_listener
)
917 return ISC_R_INVALIDARG
;
919 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
920 return (*(h
-> inner
-> type
-> set_value
))
921 (h
-> inner
, id
, name
, value
);
922 return ISC_R_NOTFOUND
;
925 isc_result_t
dhcp_failover_listener_get_value (omapi_object_t
*h
,
927 omapi_data_string_t
*name
,
928 omapi_value_t
**value
)
930 if (h
-> type
!= dhcp_type_failover_listener
)
931 return ISC_R_INVALIDARG
;
933 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
934 return (*(h
-> inner
-> type
-> get_value
))
935 (h
-> inner
, id
, name
, value
);
936 return ISC_R_NOTFOUND
;
939 isc_result_t
dhcp_failover_listener_destroy (omapi_object_t
*h
,
940 const char *file
, int line
)
942 if (h
-> type
!= dhcp_type_failover_listener
)
943 return ISC_R_INVALIDARG
;
944 return ISC_R_SUCCESS
;
947 /* Write all the published values associated with the object through the
948 specified connection. */
950 isc_result_t
dhcp_failover_listener_stuff (omapi_object_t
*c
,
956 if (p
-> type
!= dhcp_type_failover_listener
)
957 return ISC_R_INVALIDARG
;
959 if (p
-> inner
&& p
-> inner
-> type
-> stuff_values
)
960 return (*(p
-> inner
-> type
-> stuff_values
)) (c
, id
,
962 return ISC_R_SUCCESS
;
965 /* Set up master state machine for the failover protocol. */
967 isc_result_t
dhcp_failover_register (omapi_object_t
*h
)
970 dhcp_failover_state_t
*obj
;
972 omapi_value_t
*value
= (omapi_value_t
*)0;
974 status
= omapi_get_value_str (h
, (omapi_object_t
*)0,
975 "local-port", &value
);
976 if (status
!= ISC_R_SUCCESS
)
978 if (!value
-> value
) {
979 omapi_value_dereference (&value
, MDL
);
980 return ISC_R_INVALIDARG
;
983 status
= omapi_get_int_value (&port
, value
-> value
);
984 omapi_value_dereference (&value
, MDL
);
985 if (status
!= ISC_R_SUCCESS
)
988 obj
= (dhcp_failover_state_t
*)dmalloc (sizeof *obj
, MDL
);
990 return ISC_R_NOMEMORY
;
991 memset (obj
, 0, sizeof *obj
);
993 obj
-> type
= dhcp_type_failover_state
;
994 obj
-> listen_port
= port
;
996 status
= omapi_listen ((omapi_object_t
*)obj
, port
, 1);
997 omapi_object_dereference ((omapi_object_t
**)&obj
, MDL
);
998 if (status
!= ISC_R_SUCCESS
)
1001 status
= omapi_object_reference (&h
-> outer
, (omapi_object_t
*)obj
,
1003 if (status
!= ISC_R_SUCCESS
) {
1004 omapi_object_dereference ((omapi_object_t
**)&obj
, MDL
);
1007 status
= omapi_object_reference (&obj
-> inner
, h
, MDL
);
1008 if (status
!= ISC_R_SUCCESS
) {
1009 omapi_object_dereference ((omapi_object_t
**)&obj
, MDL
);
1016 /* Signal handler for protocol state machine. */
1018 isc_result_t
dhcp_failover_state_signal (omapi_object_t
*o
,
1019 const char *name
, va_list ap
)
1021 isc_result_t status
;
1022 omapi_connection_object_t
*c
;
1023 omapi_protocol_object_t
*obj
;
1024 dhcp_failover_state_t
*state
;
1025 dhcp_failover_link_t
*link
;
1028 if (!o
|| o
-> type
!= dhcp_type_failover_state
)
1029 return ISC_R_INVALIDARG
;
1030 state
= (dhcp_failover_state_t
*)o
;
1032 /* Not a signal we recognize? */
1033 if (strcmp (name
, "disconnect") &&
1034 strcmp (name
, "message")) {
1035 if (state
-> inner
&& state
-> inner
-> type
-> signal_handler
)
1036 return (*(state
-> inner
-> type
-> signal_handler
))
1037 (state
-> inner
, name
, ap
);
1038 return ISC_R_NOTFOUND
;
1041 /* Handle connect signals by seeing what state we're in
1042 and potentially doing a state transition. */
1043 if (!strcmp (name
, "disconnect")) {
1044 link
= va_arg (ap
, dhcp_failover_link_t
*);
1046 omapi_object_dereference (&state
-> link_to_peer
, MDL
);
1047 dhcp_failover_state_transition (state
, "disconnect");
1048 if (state
-> i_am
== primary
)
1049 add_timeout (cur_time
+ 90, dhcp_failover_reconnect
,
1050 state
); /* XXX this is a reference! */
1051 } else if (!strcmp (name
, "message")) {
1052 link
= va_arg (ap
, dhcp_failover_link_t
*);
1054 if (link
-> imsg
-> type
== FTM_CONNECT
) {
1055 /* If we already have a link to the peer, it must be
1057 XXX Is this the right thing to do? */
1058 if (state
-> link_to_peer
) {
1059 omapi_disconnect ((omapi_object_t
*)
1060 state
-> link_to_peer
, 1);
1061 omapi_object_dereference
1062 (&state
-> link_to_peer
, MDL
);
1063 dhcp_failover_state_transition (state
,
1066 omapi_object_reference (&state
-> link_to_peer
,
1067 (omapi_object_t
*)link
, MDL
);
1068 status
= (dhcp_failover_send_connectack
1069 ((omapi_object_t
*)link
, 0));
1070 if (status
!= ISC_R_SUCCESS
) {
1071 omapi_object_dereference
1072 (&state
-> link_to_peer
, MDL
);
1073 omapi_disconnect (link
-> outer
, 1);
1074 return ISC_R_SUCCESS
;
1076 dhcp_failover_state_transition (state
, "connect");
1077 } else if (link
-> imsg
-> type
== FTM_CONNECTACK
) {
1078 if (link
-> imsg
-> reject_reason
) {
1079 log_error ("Failover CONNECTACK from %d.%d.%d.%d%s%s",
1081 (&link
-> imsg
-> server_addr
)) [0],
1083 (&link
-> imsg
-> server_addr
)) [1],
1085 (&link
-> imsg
-> server_addr
)) [2],
1087 (&link
-> imsg
-> server_addr
)) [3],
1089 (dhcp_failover_reject_reason_print
1090 (link
-> imsg
-> reject_reason
)));
1091 omapi_disconnect (link
-> outer
, 1);
1092 return ISC_R_SUCCESS
;
1095 if (!dhcp_failover_state_match
1097 (u_int8_t
*)&link
-> imsg
-> server_addr
,
1098 sizeof link
-> imsg
-> server_addr
)) {
1099 log_error ("Failover CONNECTACK from %s %d.%d.%d.%d",
1102 (&link
-> imsg
-> server_addr
)) [0],
1104 (&link
-> imsg
-> server_addr
)) [1],
1106 (&link
-> imsg
-> server_addr
)) [2],
1108 (&link
-> imsg
-> server_addr
)) [3]);
1109 dhcp_failover_send_disconnect ((omapi_object_t
*)link
,
1110 FTR_INVALID_PARTNER
, 0);
1111 omapi_disconnect (link
-> outer
, 0);
1114 if (state
-> link_to_peer
) {
1115 log_error ("Failover CONNECTACK %s %d.%d.%d.%d",
1116 "while already connected",
1118 (&link
-> imsg
-> server_addr
)) [0],
1120 (&link
-> imsg
-> server_addr
)) [1],
1122 (&link
-> imsg
-> server_addr
)) [2],
1124 (&link
-> imsg
-> server_addr
)) [3]);
1125 dhcp_failover_send_disconnect ((omapi_object_t
*)link
,
1126 FTR_DUP_CONNECTION
, 0);
1127 omapi_disconnect (link
-> outer
, 0);
1129 omapi_object_reference (&state
-> link_to_peer
,
1130 (omapi_object_t
*)link
, MDL
);
1131 dhcp_failover_state_transition (state
, "connect");
1132 } else if (link
-> imsg
-> type
== FTM_DISCONNECT
) {
1133 if (link
-> imsg
-> reject_reason
) {
1134 log_error ("Failover DISCONNECT from %d.%d.%d.%d%s%s",
1136 (&link
-> imsg
-> server_addr
)) [0],
1138 (&link
-> imsg
-> server_addr
)) [1],
1140 (&link
-> imsg
-> server_addr
)) [2],
1142 (&link
-> imsg
-> server_addr
)) [3],
1144 (dhcp_failover_reject_reason_print
1145 (link
-> imsg
-> reject_reason
)));
1147 omapi_disconnect (link
-> outer
, 1);
1148 return ISC_R_SUCCESS
;
1149 } else if (link
-> imsg
-> type
== FTM_BNDUPD
) {
1150 dhcp_failover_process_bind_update (state
,
1152 } else if (link
-> imsg
-> type
== FTM_BNDACK
) {
1153 dhcp_failover_process_bind_ack (state
, link
-> imsg
);
1154 } else if (link
-> imsg
-> type
== FTM_POOLREQ
) {
1155 dhcp_failover_pool_rebalance (state
);
1156 } else if (link
-> imsg
-> type
== FTM_POOLRESP
) {
1157 log_info ("pool response: %d leases",
1158 link
-> imsg
-> addresses_transferred
);
1162 /* Handle all the events we care about... */
1163 return ISC_R_SUCCESS
;
1166 isc_result_t
dhcp_failover_state_transition (dhcp_failover_state_t
*state
,
1169 /* XXX Check these state transitions against the spec! */
1170 if (!strcmp (name
, "disconnect")) {
1171 switch (state
-> my_state
) {
1172 case potential_conflict_nic
:
1174 case communications_interrupted
:
1175 /* Already in the right state? */
1176 return ISC_R_SUCCESS
;
1178 case potential_conflict
:
1179 return dhcp_failover_set_state
1180 (state
, potential_conflict_nic
);
1183 /* XXX I don't think it makes sense to make a
1184 XXX transition from recover to communications-
1185 XXX interrupted, because then when the connect
1186 XXX occurred, we'd make a transition into
1187 XXX normal, not recover. */
1189 return dhcp_failover_set_state
1190 (state
, communications_interrupted
);
1195 return dhcp_failover_set_state
1196 (state
, potential_conflict_nic
);
1199 } else if (!strcmp (name
, "connect")) {
1200 switch (state
-> my_state
) {
1201 case communications_interrupted
:
1202 return dhcp_failover_set_state (state
, normal
);
1205 return dhcp_failover_set_state (state
, recover
);
1207 case potential_conflict_nic
:
1208 return dhcp_failover_set_state (state
,
1209 potential_conflict
);
1211 /* We should never be in these states when we make
1212 a "connect" transition. */
1214 case potential_conflict
:
1216 return ISC_R_INVALIDARG
;
1218 /* We should never be in the unknown_state or an
1219 unknown state either. */
1222 return dhcp_failover_set_state (state
,
1223 potential_conflict
);
1225 } else if (!strcmp (name
, "startup")) {
1226 switch (state
-> my_state
) {
1227 case communications_interrupted
:
1229 case potential_conflict_nic
:
1230 return ISC_R_SUCCESS
;
1234 return dhcp_failover_set_state
1235 (state
, communications_interrupted
);
1237 case potential_conflict
:
1238 return dhcp_failover_set_state
1239 (state
, potential_conflict_nic
);
1242 return dhcp_failover_set_state
1243 (state
, communications_interrupted
);
1246 return dhcp_failover_set_state
1247 (state
, potential_conflict_nic
);
1250 return ISC_R_INVALIDARG
;
1253 isc_result_t
dhcp_failover_set_state (dhcp_failover_state_t
*state
,
1254 enum failover_state new_state
)
1256 enum failover_state saved_state
;
1259 /* First make the transition out of the current state. */
1260 switch (state
-> my_state
) {
1263 case potential_conflict
:
1264 /* Any updates that haven't been acked yet, we have to
1265 resend, just in case. */
1266 if (state
-> ack_queue_tail
) {
1269 /* Zap the flags. */
1270 for (lp
= state
-> ack_queue_head
; lp
; lp
= lp
-> next_pending
)
1271 lp
-> flags
= ((lp
-> flags
& ~ON_ACK_QUEUE
) |
1274 /* Now hook the ack queue to the beginning of the update
1276 if (state
-> update_queue_head
) {
1277 omapi_object_reference
1278 ((omapi_object_t
**)
1279 &state
-> ack_queue_tail
-> next_pending
,
1280 (omapi_object_t
*)state
-> update_queue_head
,
1282 omapi_object_dereference ((omapi_object_t
**)
1283 &state
-> update_queue_head
,
1286 omapi_object_reference ((omapi_object_t
**)
1287 &state
-> update_queue_head
,
1288 (omapi_object_t
*)state
-> ack_queue_head
,
1290 if (!state
-> update_queue_tail
)
1291 omapi_object_reference
1292 ((omapi_object_t
**)
1293 &state
-> update_queue_tail
,
1294 (omapi_object_t
*)state
-> ack_queue_tail
, MDL
);
1295 omapi_object_dereference ((omapi_object_t
**)
1296 &state
-> ack_queue_tail
, MDL
);
1297 omapi_object_dereference ((omapi_object_t
**)
1298 &state
-> ack_queue_head
, MDL
);
1299 state
-> cur_unacked_updates
= 0;
1301 cancel_timeout (dhcp_failover_keepalive
, state
);
1305 case communications_interrupted
:
1306 case potential_conflict_nic
:
1311 /* Tentatively make the transition. */
1312 saved_state
= state
-> my_state
;
1313 saved_stos
= state
-> my_stos
;
1314 state
-> my_stos
= cur_time
;
1315 state
-> my_state
= new_state
;
1317 if (!write_failover_state (state
) || !commit_leases ()) {
1318 /* XXX What to do? What to do? */
1319 log_error ("Unable to record current failover state for %s",
1321 /* XXX for now, we don't make the state transition, but this is
1322 XXX kind of a scary choice. */
1323 state
-> my_state
= saved_state
;
1324 state
-> my_stos
= saved_stos
;
1325 return ISC_R_IOERROR
;
1328 log_info ("failover peer %s moves from %s to %s",
1329 state
-> name
, dhcp_failover_state_name_print (saved_state
),
1330 dhcp_failover_state_name_print (state
-> my_state
));
1336 case potential_conflict
:
1337 /* If we go into communications-interrupted and then back into
1338 potential-conflict, do we need to start over, or just continue
1339 with the reconciliation process? This came up at one of the
1340 teleconferences, so it's probably answered in the draft. */
1341 if (state
-> max_flying_updates
> state
-> cur_unacked_updates
&&
1342 state
-> update_queue_head
) {
1343 dhcp_failover_send_updates (state
);
1345 dhcp_failover_state_pool_check (state
);
1351 return ISC_R_SUCCESS
;
1354 int dhcp_failover_pool_rebalance (dhcp_failover_state_t
*state
)
1357 int leases_queued
= 0;
1359 struct shared_network
*s
;
1362 if (state
-> my_state
!= normal
|| state
-> i_am
== secondary
)
1365 for (s
= shared_networks
; s
; s
= s
-> next
) {
1366 for (p
= s
-> pools
; p
; p
= p
-> next
) {
1367 if (p
-> failover_peer
!= state
)
1369 log_info ("pool %lx total %d local free %d peer free %d",
1370 (unsigned long)p
, p
-> lease_count
,
1371 p
-> local_leases
, p
-> peer_leases
);
1373 lts
= ((p
-> local_leases
+
1374 p
-> peer_leases
) / 2) - p
-> peer_leases
;
1378 leases_queued
+= lts
;
1379 for (lp
= p
-> last_lease
; lp
&& lts
;
1381 if (!(lp
-> flags
& PEER_IS_OWNER
)) {
1382 lp
-> flags
|= PEER_IS_OWNER
;
1383 lp
-> tstp
= cur_time
;
1384 if (!write_lease (lp
) ||
1385 !commit_leases () ||
1386 !dhcp_failover_queue_update (lp
)) {
1387 log_info ("%s lease %s on giveaway",
1389 piaddr (lp
-> ip_addr
));
1396 log_info ("lease imbalance - lts = %d", lts
);
1397 leases_queued
-= lts
;
1401 dhcp_failover_send_poolresp (state
, leases_queued
);
1402 return leases_queued
;
1405 int dhcp_failover_pool_check (struct pool
*pool
)
1410 if (!pool
-> failover_peer
||
1411 pool
-> failover_peer
-> i_am
== primary
||
1412 pool
-> failover_peer
-> my_state
!= normal
)
1415 log_info ("pool %lx total %d local free %d peer free %d",
1416 (unsigned long)pool
, pool
-> lease_count
,
1417 pool
-> local_leases
, pool
-> peer_leases
);
1419 lts
= ((pool
-> local_leases
+
1420 pool
-> peer_leases
) / 2) - pool
-> local_leases
;
1422 /* XXX What about multiple pools? */
1423 dhcp_failover_send_poolreq (pool
-> failover_peer
);
1429 int dhcp_failover_state_pool_check (dhcp_failover_state_t
*state
)
1432 struct shared_network
*s
;
1435 for (s
= shared_networks
; s
; s
= s
-> next
) {
1436 for (p
= s
-> pools
; p
; p
= p
-> next
) {
1437 if (p
-> failover_peer
!= state
)
1439 /* Only need to request rebalance on one pool. */
1440 if (dhcp_failover_pool_check (p
))
1447 isc_result_t
dhcp_failover_send_updates (dhcp_failover_state_t
*state
)
1449 struct lease
*lp
= (struct lease
*)0;
1450 isc_result_t status
;
1452 /* Can't update peer if we're not talking to it! */
1453 if (state
-> my_state
!= normal
&&
1454 state
-> my_state
!= recover
&&
1455 state
-> my_state
!= potential_conflict
)
1456 return ISC_R_SUCCESS
;
1458 while (state
-> max_flying_updates
> state
-> cur_unacked_updates
&&
1459 state
-> update_queue_head
) {
1460 /* Grab the head of the update queue. */
1461 omapi_object_reference ((omapi_object_t
**)&lp
,
1463 state
-> update_queue_head
, MDL
);
1465 /* Send the update to the peer. */
1466 status
= dhcp_failover_send_bind_update (state
, lp
);
1467 if (status
!= ISC_R_SUCCESS
) {
1468 omapi_object_dereference ((omapi_object_t
**)&lp
, MDL
);
1471 lp
-> flags
&= ~ON_UPDATE_QUEUE
;
1473 /* Take it off the head of the update queue and put the next
1474 item in the update queue at the head. */
1475 omapi_object_dereference ((omapi_object_t
**)
1476 &state
-> update_queue_head
, MDL
);
1477 if (lp
-> next_pending
) {
1478 omapi_object_reference ((omapi_object_t
**)
1479 &state
-> update_queue_head
,
1481 lp
-> next_pending
, MDL
);
1482 omapi_object_dereference ((omapi_object_t
**)
1483 &lp
-> next_pending
, MDL
);
1485 omapi_object_dereference ((omapi_object_t
**)
1486 &state
-> update_queue_tail
,
1490 if (state
-> ack_queue_head
) {
1491 omapi_object_reference
1492 ((omapi_object_t
**)
1493 &state
-> ack_queue_tail
-> next_pending
,
1494 (omapi_object_t
*)lp
, MDL
);
1495 omapi_object_dereference ((omapi_object_t
**)
1496 &state
-> ack_queue_tail
,
1499 omapi_object_reference ((omapi_object_t
**)
1500 &state
-> ack_queue_head
,
1501 (omapi_object_t
*)lp
, MDL
);
1503 omapi_object_reference ((omapi_object_t
**)
1504 &state
-> ack_queue_tail
,
1505 (omapi_object_t
*)lp
, MDL
);
1506 lp
-> flags
|= ON_ACK_QUEUE
;
1507 omapi_object_dereference ((omapi_object_t
**)&lp
, MDL
);
1509 /* Count the object as an unacked update. */
1510 state
-> cur_unacked_updates
++;
1512 return ISC_R_SUCCESS
;
1515 /* Queue an update for a lease. Always returns 1 at this point - it's
1516 not an error for this to be called on a lease for which there's no
1519 int dhcp_failover_queue_update (struct lease
*lease
)
1521 dhcp_failover_state_t
*state
;
1523 if (!lease
-> pool
||
1524 !lease
-> pool
-> failover_peer
)
1527 /* If it's already on the update queue, leave it there. */
1528 if (lease
-> flags
& ON_UPDATE_QUEUE
)
1531 /* Get the failover state structure for this lease. */
1532 state
= lease
-> pool
-> failover_peer
;
1534 /* If it's on the ack queue, take it off. */
1535 if (lease
-> flags
& ON_ACK_QUEUE
)
1536 dhcp_failover_ack_queue_remove (state
, lease
);
1538 if (state
-> update_queue_head
) {
1539 omapi_object_reference
1540 ((omapi_object_t
**)
1541 &state
-> update_queue_tail
-> next_pending
,
1542 (omapi_object_t
*)lease
, MDL
);
1543 omapi_object_dereference ((omapi_object_t
**)
1544 &state
-> update_queue_tail
, MDL
);
1546 omapi_object_reference ((omapi_object_t
**)
1547 &state
-> update_queue_head
,
1548 (omapi_object_t
*)lease
, MDL
);
1550 omapi_object_reference ((omapi_object_t
**)
1551 &state
-> update_queue_tail
,
1552 (omapi_object_t
*)lease
, MDL
);
1553 lease
-> flags
|= ON_UPDATE_QUEUE
;
1554 dhcp_failover_send_updates (state
);
1558 void dhcp_failover_ack_queue_remove (dhcp_failover_state_t
*state
,
1559 struct lease
*lease
)
1563 if (state
-> ack_queue_head
== lease
) {
1564 omapi_object_dereference ((omapi_object_t
**)
1565 &state
-> ack_queue_head
, MDL
);
1566 if (lease
-> next_pending
) {
1567 omapi_object_reference ((omapi_object_t
**)
1568 &state
-> ack_queue_head
,
1570 lease
-> next_pending
, MDL
);
1572 omapi_object_dereference ((omapi_object_t
**)
1573 &state
-> ack_queue_tail
,
1576 lease
-> flags
&= ~ON_ACK_QUEUE
;
1579 for (lp
= state
-> ack_queue_head
;
1580 lp
-> next_pending
!= lease
; lp
= lp
-> next_pending
)
1583 omapi_object_dereference ((omapi_object_t
**)
1584 &lp
-> next_pending
, MDL
);
1585 if (lease
-> next_pending
)
1586 omapi_object_reference ((omapi_object_t
**)
1587 &lp
-> next_pending
,
1589 lease
-> next_pending
, MDL
);
1591 omapi_object_dereference ((omapi_object_t
**)
1592 &state
-> ack_queue_tail
,
1594 omapi_object_reference ((omapi_object_t
**)
1595 &state
-> ack_queue_tail
,
1596 (omapi_object_t
*)lp
, MDL
);
1599 lease
-> flags
&= ~ON_ACK_QUEUE
;
1602 isc_result_t
dhcp_failover_state_set_value (omapi_object_t
*h
,
1604 omapi_data_string_t
*name
,
1605 omapi_typed_data_t
*value
)
1607 if (h
-> type
!= dhcp_type_failover_state
)
1608 return ISC_R_INVALIDARG
;
1610 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
1611 return (*(h
-> inner
-> type
-> set_value
))
1612 (h
-> inner
, id
, name
, value
);
1613 return ISC_R_NOTFOUND
;
1616 void dhcp_failover_keepalive (void *vs
)
1618 dhcp_failover_state_t
*state
= vs
;
1621 void dhcp_failover_reconnect (void *vs
)
1623 dhcp_failover_state_t
*state
= vs
;
1624 isc_result_t status
;
1626 status
= dhcp_failover_link_initiate ((omapi_object_t
*)state
);
1627 if (status
!= ISC_R_SUCCESS
) {
1628 log_info ("failover peer %s: %s", state
-> name
,
1629 isc_result_totext (status
));
1630 add_timeout (cur_time
+ 90,
1631 dhcp_failover_listener_restart
, state
);
1635 void dhcp_failover_listener_restart (void *vs
)
1637 dhcp_failover_state_t
*state
= vs
;
1638 isc_result_t status
;
1640 status
= dhcp_failover_listen ((omapi_object_t
*)state
);
1641 if (status
!= ISC_R_SUCCESS
) {
1642 log_info ("failover peer %s: %s", state
-> name
,
1643 isc_result_totext (status
));
1644 add_timeout (cur_time
+ 90,
1645 dhcp_failover_listener_restart
, state
);
1649 isc_result_t
dhcp_failover_state_get_value (omapi_object_t
*h
,
1651 omapi_data_string_t
*name
,
1652 omapi_value_t
**value
)
1654 dhcp_failover_state_t
*s
;
1655 struct option_cache
*oc
;
1656 struct data_string ds
;
1657 isc_result_t status
;
1659 if (h
-> type
!= dhcp_type_failover_state
)
1660 return ISC_R_INVALIDARG
;
1661 s
= (dhcp_failover_state_t
*)h
;
1663 if (!omapi_ds_strcmp (name
, "name")) {
1665 return omapi_make_string_value (value
,
1666 name
, s
-> name
, MDL
);
1667 return ISC_R_NOTFOUND
;
1668 } else if (!omapi_ds_strcmp (name
, "partner-address")) {
1671 memset (&ds
, 0, sizeof ds
);
1672 if (!evaluate_option_cache (&ds
, (struct packet
*)0,
1674 (struct option_state
*)0,
1675 (struct option_state
*)0,
1676 &global_scope
, oc
, MDL
)) {
1677 return ISC_R_NOTFOUND
;
1679 status
= omapi_make_const_value (value
,
1680 name
, ds
.data
, ds
.len
, MDL
);
1681 /* Disgusting kludge: */
1682 if (oc
== s
-> server_addr
&& !s
-> server_identifier
.len
)
1683 data_string_copy (&s
-> server_identifier
, &ds
, MDL
);
1684 data_string_forget (&ds
, MDL
);
1686 } else if (!omapi_ds_strcmp (name
, "local-address")) {
1687 oc
= s
-> server_addr
;
1689 } else if (!omapi_ds_strcmp (name
, "partner-port")) {
1690 return omapi_make_int_value (value
, name
, s
-> port
, MDL
);
1691 } else if (!omapi_ds_strcmp (name
, "local-port")) {
1692 return omapi_make_int_value (value
,
1693 name
, s
-> listen_port
, MDL
);
1694 } else if (!omapi_ds_strcmp (name
, "max-outstanding-updates")) {
1695 return omapi_make_uint_value (value
, name
,
1696 s
-> max_flying_updates
, MDL
);
1697 } else if (!omapi_ds_strcmp (name
, "mclt")) {
1698 return omapi_make_uint_value (value
, name
, s
-> mclt
, MDL
);
1699 } else if (!omapi_ds_strcmp (name
, "load-balance-max-secs")) {
1700 return omapi_make_int_value (value
, name
,
1701 s
-> load_balance_max_secs
, MDL
);
1702 } else if (!omapi_ds_strcmp (name
, "load-balance-hba")) {
1704 return omapi_make_const_value (value
, name
,
1706 return ISC_R_NOTFOUND
;
1707 } else if (!omapi_ds_strcmp (name
, "partner-state")) {
1708 return omapi_make_uint_value (value
, name
,
1709 s
-> partner_state
, MDL
);
1710 } else if (!omapi_ds_strcmp (name
, "local-state")) {
1711 return omapi_make_uint_value (value
, name
,
1712 s
-> my_state
, MDL
);
1713 } else if (!omapi_ds_strcmp (name
, "partner-stos")) {
1714 return omapi_make_int_value (value
, name
,
1715 s
-> partner_stos
, MDL
);
1716 } else if (!omapi_ds_strcmp (name
, "local-stos")) {
1717 return omapi_make_int_value (value
, name
,
1719 } else if (!omapi_ds_strcmp (name
, "hierarchy")) {
1720 return omapi_make_uint_value (value
, name
, s
-> i_am
, MDL
);
1721 } else if (!omapi_ds_strcmp (name
, "last-packet-sent")) {
1722 return omapi_make_int_value (value
, name
,
1723 s
-> last_packet_sent
, MDL
);
1724 } else if (!omapi_ds_strcmp (name
, "last-timestamp-received")) {
1725 return omapi_make_int_value (value
, name
,
1726 s
-> last_timestamp_received
,
1728 } else if (!omapi_ds_strcmp (name
, "skew")) {
1729 return omapi_make_int_value (value
, name
, s
-> skew
, MDL
);
1730 } else if (!omapi_ds_strcmp (name
, "max-transmit-idle")) {
1731 return omapi_make_uint_value (value
, name
,
1732 s
-> max_transmit_idle
, MDL
);
1733 } else if (!omapi_ds_strcmp (name
, "max-response-delay")) {
1734 return omapi_make_uint_value (value
, name
,
1735 s
-> max_response_delay
, MDL
);
1736 } else if (!omapi_ds_strcmp (name
, "cur-unacked-updates")) {
1737 return omapi_make_int_value (value
, name
,
1738 s
-> cur_unacked_updates
, MDL
);
1741 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
1742 return (*(h
-> inner
-> type
-> get_value
))
1743 (h
-> inner
, id
, name
, value
);
1744 return ISC_R_NOTFOUND
;
1747 isc_result_t
dhcp_failover_state_destroy (omapi_object_t
*h
,
1748 const char *file
, int line
)
1750 dhcp_failover_state_t
*s
;
1752 if (h
-> type
!= dhcp_type_failover_state
)
1753 return ISC_R_INVALIDARG
;
1754 s
= (dhcp_failover_state_t
*)h
;
1755 if (s
-> link_to_peer
)
1756 omapi_object_dereference (&s
-> link_to_peer
, MDL
);
1758 dfree (s
-> name
, MDL
);
1760 option_cache_dereference (&s
-> address
, MDL
);
1761 if (s
-> server_addr
)
1762 option_cache_dereference (&s
-> server_addr
, MDL
);
1764 return ISC_R_SUCCESS
;
1767 /* Write all the published values associated with the object through the
1768 specified connection. */
1770 isc_result_t
dhcp_failover_state_stuff (omapi_object_t
*c
,
1774 dhcp_failover_state_t
*s
;
1775 omapi_connection_object_t
*conn
;
1776 isc_result_t status
;
1778 if (c
-> type
!= omapi_type_connection
)
1779 return ISC_R_INVALIDARG
;
1780 conn
= (omapi_connection_object_t
*)c
;
1782 if (h
-> type
!= dhcp_type_failover_state
)
1783 return ISC_R_INVALIDARG
;
1784 s
= (dhcp_failover_state_t
*)h
;
1786 status
= omapi_connection_put_name (c
, "name");
1787 if (status
!= ISC_R_SUCCESS
)
1789 status
= omapi_connection_put_string (c
, s
-> name
);
1790 if (status
!= ISC_R_SUCCESS
)
1793 status
= omapi_connection_put_name (c
, "partner-address");
1794 if (status
!= ISC_R_SUCCESS
)
1796 status
= omapi_connection_put_uint32 (c
, sizeof s
-> address
);
1797 if (status
!= ISC_R_SUCCESS
)
1799 status
= omapi_connection_copyin (c
, (u_int8_t
*)&s
-> address
,
1800 sizeof s
-> address
);
1801 if (status
!= ISC_R_SUCCESS
)
1804 status
= omapi_connection_put_name (c
, "partner-port");
1805 if (status
!= ISC_R_SUCCESS
)
1807 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
1808 if (status
!= ISC_R_SUCCESS
)
1810 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> port
);
1811 if (status
!= ISC_R_SUCCESS
)
1814 status
= omapi_connection_put_name (c
, "local-address");
1815 if (status
!= ISC_R_SUCCESS
)
1817 status
= omapi_connection_put_uint32 (c
, sizeof s
-> server_addr
);
1818 if (status
!= ISC_R_SUCCESS
)
1820 status
= omapi_connection_copyin (c
, (u_int8_t
*)&s
-> server_addr
,
1821 sizeof s
-> server_addr
);
1822 if (status
!= ISC_R_SUCCESS
)
1825 status
= omapi_connection_put_name (c
, "local-port");
1826 if (status
!= ISC_R_SUCCESS
)
1828 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
1829 if (status
!= ISC_R_SUCCESS
)
1831 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> listen_port
);
1832 if (status
!= ISC_R_SUCCESS
)
1835 status
= omapi_connection_put_name (c
, "max-outstanding-updates");
1836 if (status
!= ISC_R_SUCCESS
)
1838 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
1839 if (status
!= ISC_R_SUCCESS
)
1841 status
= omapi_connection_put_uint32 (c
, s
-> max_flying_updates
);
1842 if (status
!= ISC_R_SUCCESS
)
1845 status
= omapi_connection_put_name (c
, "mclt");
1846 if (status
!= ISC_R_SUCCESS
)
1848 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
1849 if (status
!= ISC_R_SUCCESS
)
1851 status
= omapi_connection_put_uint32 (c
, s
-> mclt
);
1852 if (status
!= ISC_R_SUCCESS
)
1855 status
= omapi_connection_put_name (c
, "load-balance-max-secs");
1856 if (status
!= ISC_R_SUCCESS
)
1858 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
1859 if (status
!= ISC_R_SUCCESS
)
1861 status
= (omapi_connection_put_uint32
1862 (c
, (u_int32_t
)s
-> load_balance_max_secs
));
1863 if (status
!= ISC_R_SUCCESS
)
1868 status
= omapi_connection_put_name (c
, "load-balance-hba");
1869 if (status
!= ISC_R_SUCCESS
)
1871 status
= omapi_connection_put_uint32 (c
, 32);
1872 if (status
!= ISC_R_SUCCESS
)
1874 status
= omapi_connection_copyin (c
, s
-> hba
, 32);
1875 if (status
!= ISC_R_SUCCESS
)
1879 status
= omapi_connection_put_name (c
, "partner-state");
1880 if (status
!= ISC_R_SUCCESS
)
1882 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
1883 if (status
!= ISC_R_SUCCESS
)
1885 status
= omapi_connection_put_uint32 (c
, s
-> partner_state
);
1886 if (status
!= ISC_R_SUCCESS
)
1889 status
= omapi_connection_put_name (c
, "local-state");
1890 if (status
!= ISC_R_SUCCESS
)
1892 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
1893 if (status
!= ISC_R_SUCCESS
)
1895 status
= omapi_connection_put_uint32 (c
, s
-> my_state
);
1896 if (status
!= ISC_R_SUCCESS
)
1899 status
= omapi_connection_put_name (c
, "partner-stos");
1900 if (status
!= ISC_R_SUCCESS
)
1902 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
1903 if (status
!= ISC_R_SUCCESS
)
1905 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> partner_stos
);
1906 if (status
!= ISC_R_SUCCESS
)
1909 status
= omapi_connection_put_name (c
, "local-stos");
1910 if (status
!= ISC_R_SUCCESS
)
1912 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
1913 if (status
!= ISC_R_SUCCESS
)
1915 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> my_stos
);
1916 if (status
!= ISC_R_SUCCESS
)
1919 status
= omapi_connection_put_name (c
, "hierarchy");
1920 if (status
!= ISC_R_SUCCESS
)
1922 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
1923 if (status
!= ISC_R_SUCCESS
)
1925 status
= omapi_connection_put_uint32 (c
, s
-> i_am
);
1926 if (status
!= ISC_R_SUCCESS
)
1929 status
= omapi_connection_put_name (c
, "last-packet-sent");
1930 if (status
!= ISC_R_SUCCESS
)
1932 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
1933 if (status
!= ISC_R_SUCCESS
)
1935 status
= (omapi_connection_put_uint32
1936 (c
, (u_int32_t
)s
-> last_packet_sent
));
1937 if (status
!= ISC_R_SUCCESS
)
1940 status
= omapi_connection_put_name (c
, "last-timestamp-received");
1941 if (status
!= ISC_R_SUCCESS
)
1943 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
1944 if (status
!= ISC_R_SUCCESS
)
1946 status
= (omapi_connection_put_uint32
1947 (c
, (u_int32_t
)s
-> last_timestamp_received
));
1948 if (status
!= ISC_R_SUCCESS
)
1951 status
= omapi_connection_put_name (c
, "skew");
1952 if (status
!= ISC_R_SUCCESS
)
1954 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
1955 if (status
!= ISC_R_SUCCESS
)
1957 status
= omapi_connection_put_uint32 (c
, (u_int32_t
)s
-> skew
);
1958 if (status
!= ISC_R_SUCCESS
)
1961 status
= omapi_connection_put_name (c
, "max-transmit-idle");
1962 if (status
!= ISC_R_SUCCESS
)
1964 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
1965 if (status
!= ISC_R_SUCCESS
)
1967 status
= (omapi_connection_put_uint32
1968 (c
, (u_int32_t
)s
-> max_transmit_idle
));
1969 if (status
!= ISC_R_SUCCESS
)
1972 status
= omapi_connection_put_name (c
, "max-response-delay");
1973 if (status
!= ISC_R_SUCCESS
)
1975 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
1976 if (status
!= ISC_R_SUCCESS
)
1978 status
= (omapi_connection_put_uint32
1979 (c
, (u_int32_t
)s
-> max_response_delay
));
1980 if (status
!= ISC_R_SUCCESS
)
1983 status
= omapi_connection_put_name (c
, "cur-unacked-updates");
1984 if (status
!= ISC_R_SUCCESS
)
1986 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
1987 if (status
!= ISC_R_SUCCESS
)
1989 status
= (omapi_connection_put_uint32
1990 (c
, (u_int32_t
)s
-> cur_unacked_updates
));
1991 if (status
!= ISC_R_SUCCESS
)
1994 if (h
-> inner
&& h
-> inner
-> type
-> stuff_values
)
1995 return (*(h
-> inner
-> type
-> stuff_values
)) (c
, id
,
1997 return ISC_R_SUCCESS
;
2000 isc_result_t
dhcp_failover_state_lookup (omapi_object_t
**sp
,
2002 omapi_object_t
*ref
)
2004 omapi_value_t
*tv
= (omapi_value_t
*)0;
2005 isc_result_t status
;
2006 dhcp_failover_state_t
*s
;
2008 /* First see if we were sent a handle. */
2009 status
= omapi_get_value_str (ref
, id
, "handle", &tv
);
2010 if (status
== ISC_R_SUCCESS
) {
2011 status
= omapi_handle_td_lookup (sp
, tv
-> value
);
2013 omapi_value_dereference (&tv
, MDL
);
2014 if (status
!= ISC_R_SUCCESS
)
2017 /* Don't return the object if the type is wrong. */
2018 if ((*sp
) -> type
!= dhcp_type_failover_state
) {
2019 omapi_object_dereference (sp
, MDL
);
2020 return ISC_R_INVALIDARG
;
2024 /* Look the failover state up by peer name. */
2025 status
= omapi_get_value_str (ref
, id
, "peer_name", &tv
);
2026 if (status
== ISC_R_SUCCESS
) {
2027 for (s
= failover_states
; s
; s
= s
-> next
) {
2028 unsigned l
= strlen (s
-> name
);
2029 if (l
== tv
-> value
-> u
.buffer
.len
||
2031 tv
-> value
-> u
.buffer
.value
, l
))
2034 omapi_value_dereference (&tv
, MDL
);
2036 /* If we already have a lease, and it's not the same one,
2037 then the query was invalid. */
2038 if (*sp
&& *sp
!= (omapi_object_t
*)s
) {
2039 omapi_object_dereference (sp
, MDL
);
2040 return ISC_R_KEYCONFLICT
;
2043 omapi_object_dereference (sp
, MDL
);
2044 return ISC_R_NOTFOUND
;
2046 /* XXX fix so that hash lookup itself creates
2047 XXX the reference. */
2048 omapi_object_reference (sp
, (omapi_object_t
*)s
, MDL
);
2051 /* If we get to here without finding a lease, no valid key was
2054 return ISC_R_NOKEYS
;
2055 return ISC_R_SUCCESS
;
2058 isc_result_t
dhcp_failover_state_create (omapi_object_t
**sp
,
2061 return ISC_R_NOTIMPLEMENTED
;
2064 isc_result_t
dhcp_failover_state_remove (omapi_object_t
*sp
,
2067 return ISC_R_NOTIMPLEMENTED
;
2070 int dhcp_failover_state_match (dhcp_failover_state_t
*state
,
2071 u_int8_t
*addr
, unsigned addrlen
)
2073 struct option_cache
*oc
;
2074 struct data_string ds
;
2077 memset (&ds
, 0, sizeof ds
);
2078 if (evaluate_option_cache (&ds
, (struct packet
*)0,
2080 (struct option_state
*)0,
2081 (struct option_state
*)0,
2083 state
-> address
, MDL
)) {
2084 for (i
= 0; i
+ addrlen
- 1 < ds
.len
; i
+= addrlen
) {
2085 if (!memcmp (&ds
.data
[i
],
2087 data_string_forget (&ds
, MDL
);
2091 data_string_forget (&ds
, MDL
);
2096 const char *dhcp_failover_reject_reason_print (int reason
)
2099 case FTR_ILLEGAL_IP_ADDR
:
2100 return "Illegal IP address (not part of any address pool).";
2102 case FTR_FATAL_CONFLICT
:
2103 return "Fatal conflict exists: address in use by other client.";
2105 case FTR_MISSING_BINDINFO
:
2106 return "Missing binding information.";
2108 case FTR_TIMEMISMATCH
:
2109 return "Connection rejected, time mismatch too great.";
2111 case FTR_INVALID_MCLT
:
2112 return "Connection rejected, invalid MCLT.";
2114 case FTR_MISC_REJECT
:
2115 return "Connection rejected, unknown reason.";
2117 case FTR_DUP_CONNECTION
:
2118 return "Connection rejected, duplicate connection.";
2120 case FTR_INVALID_PARTNER
:
2121 return "Connection rejected, invalid failover partner.";
2123 case FTR_TLS_UNSUPPORTED
:
2124 return "TLS not supported.";
2126 case FTR_TLS_UNCONFIGURED
:
2127 return "TLS supported but not configured.";
2129 case FTR_TLS_REQUIRED
:
2130 return "TLS required but not supported by partner.";
2132 case FTR_DIGEST_UNSUPPORTED
:
2133 return "Message digest not supported.";
2135 case FTR_DIGEST_UNCONFIGURED
:
2136 return "Message digest not configured.";
2138 case FTR_VERSION_MISMATCH
:
2139 return "Protocol version mismatch.";
2141 case FTR_MISSING_BIND_INFO
:
2142 return "Missing binding information.";
2144 case FTR_OUTDATED_BIND_INFO
:
2145 return "Outdated binding information.";
2147 case FTR_LESS_CRIT_BIND_INFO
:
2148 return "Less critical binding information.";
2150 case FTR_NO_TRAFFIC
:
2151 return "No traffic within sufficient time.";
2153 case FTR_HBA_CONFLICT
:
2154 return "Hash bucket assignment conflict.";
2158 return "Unknown: Error occurred but does not match any reason code.";
2162 const char *dhcp_failover_state_name_print (enum failover_state state
)
2167 return "unknown-state";
2170 return "partner-down";
2175 case communications_interrupted
:
2176 return "communications-interrupted";
2178 case potential_conflict_nic
:
2179 return "potential-conflict-nic";
2181 case potential_conflict
:
2182 return "potential-conflict";
2189 failover_option_t
*dhcp_failover_option_printf (unsigned code
,
2193 const char *fmt
, ...) {
2198 #if defined (HAVE_SNPRINTF)
2199 /* Presumably if we have snprintf, we also have
2201 vsnprintf (tbuf
, sizeof tbuf
, fmt
, va
);
2203 vsprintf (tbuf
, fmt
, va
);
2207 return dhcp_failover_make_option (code
, obuf
, obufix
, obufmax
,
2208 strlen (tbuf
), tbuf
);
2211 failover_option_t
*dhcp_failover_make_option (unsigned code
,
2212 char *obuf
, unsigned *obufix
,
2213 unsigned obufmax
, ...)
2216 struct failover_option_info
*info
;
2218 unsigned size
, count
;
2224 #if defined (DEBUG_FAILOVER_MESSAGES)
2228 /* Note that the failover_option structure is used differently on
2229 input than on output - on input, count is an element count, and
2230 on output it's the number of bytes total in the option, including
2231 the option code and option length. */
2232 failover_option_t option
, *op
;
2235 /* Bogus option code? */
2236 if (code
< 1 || code
> FTO_MAX
|| ft_options
[code
].type
== FT_UNDEF
) {
2237 return &null_failover_option
;
2239 info
= &ft_options
[code
];
2241 va_start (va
, obufmax
);
2243 /* Get the number of elements and the size of the buffer we need
2245 if (info
-> type
== FT_DDNS
|| info
-> type
== FT_DDNS1
) {
2246 count
= info
-> type
== FT_DDNS
? 1 : 2;
2247 size
= va_arg (va
, int) + count
;
2249 /* Find out how many items in this list. */
2250 if (info
-> num_present
)
2251 count
= info
-> num_present
;
2253 count
= va_arg (va
, int);
2255 /* Figure out size. */
2256 switch (info
-> type
) {
2263 case FT_TEXT_OR_BYTES
:
2265 txt
= va_arg (va
, char *);
2270 ilen
= va_arg (va
, unsigned);
2271 size
= count
* ilen
;
2283 /* shouldn't get here. */
2284 log_fatal ("bogus type in failover_make_option: %d",
2292 /* Allocate a buffer for the option. */
2293 option
.count
= size
;
2294 option
.data
= dmalloc (option
.count
, MDL
);
2296 return &null_failover_option
;
2298 /* Put in the option code and option length. */
2299 putUShort (option
.data
, code
);
2300 putUShort (&option
.data
[2], size
- 4);
2302 #if defined (DEBUG_FAILOVER_MESSAGES)
2303 sprintf (tbuf
, " (%s<%d>", info
-> name
, option
.count
);
2304 failover_print (obuf
, obufix
, obufmax
, tbuf
);
2307 /* Now put in the data. */
2308 switch (info
-> type
) {
2310 for (i
= 0; i
< count
; i
++) {
2311 val
= va_arg (va
, unsigned);
2312 #if defined (DEBUG_FAILOVER_MESSAGES)
2313 sprintf (tbuf
, " %d", val
);
2314 failover_print (obuf
, obufix
, obufmax
, tbuf
);
2316 option
.data
[i
+ 4] = val
;
2321 for (i
= 0; i
< count
; i
++) {
2322 iaddr
= va_arg (va
, u_int8_t
*);
2324 dfree (option
.data
, MDL
);
2325 log_error ("IP addrlen=%d, should be 4.",
2327 return &null_failover_option
;
2330 #if defined (DEBUG_FAILOVER_MESSAGES)
2331 sprintf (tbuf
, " %u.%u.%u.%u", iaddr
[0], iaddr
[1],
2332 iaddr
[2], iaddr
[3]);
2333 failover_print (obuf
, obufix
, obufmax
, tbuf
);
2335 memcpy (&option
.data
[4 + i
* ilen
], iaddr
, ilen
);
2340 for (i
= 0; i
< count
; i
++) {
2341 val
= va_arg (va
, unsigned);
2342 #if defined (DEBUG_FAILOVER_MESSAGES)
2343 sprintf (tbuf
, " %d", val
);
2344 failover_print (obuf
, obufix
, obufmax
, tbuf
);
2346 putULong (&option
.data
[4 + i
* 4], val
);
2352 bval
= va_arg (va
, u_int8_t
*);
2353 #if defined (DEBUG_FAILOVER_MESSAGES)
2354 for (i
= 0; i
< count
; i
++) {
2355 sprintf (tbuf
, " %d", bval
[i
]);
2356 failover_print (obuf
, obufix
, obufmax
, tbuf
);
2359 memcpy (&option
.data
[4], bval
, count
);
2362 /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
2363 terminated. Note that the caller should be careful not to
2364 provide a format and data that amount to more than 256 bytes
2365 of data, since it will be truncated on platforms that
2366 support snprintf, and will mung the stack on those platforms
2367 that do not support snprintf. Also, callers should not pass
2368 data acquired from the network without specifically checking
2369 it to make sure it won't bash the stack. */
2370 case FT_TEXT_OR_BYTES
:
2372 #if defined (DEBUG_FAILOVER_MESSAGES)
2373 sprintf (tbuf
, "\"%s\"", txt
);
2374 failover_print (obuf
, obufix
, obufmax
, tbuf
);
2376 memcpy (&option
.data
[4], txt
, count
);
2381 option
.data
[4] = va_arg (va
, unsigned);
2383 option
.data
[5] = va_arg (va
, unsigned);
2384 bval
= va_arg (va
, u_int8_t
*);
2385 memcpy (&option
.data
[4 + count
], bval
, size
- count
- 4);
2386 #if defined (DEBUG_FAILOVER_MESSAGES)
2387 for (i
= 4; i
< size
; i
++) {
2388 sprintf (tbuf
, " %d", option
.data
[i
]);
2389 failover_print (obuf
, obufix
, obufmax
, tbuf
);
2395 for (i
= 0; i
< count
; i
++) {
2396 val
= va_arg (va
, u_int32_t
);
2397 #if defined (DEBUG_FAILOVER_MESSAGES)
2398 sprintf (tbuf
, " %d", val
);
2399 failover_print (obuf
, obufix
, obufmax
, tbuf
);
2401 putUShort (&option
.data
[4 + i
* 2], val
);
2409 failover_print (obuf
, obufix
, obufmax
, ")");
2411 /* Now allocate a place to store what we just set up. */
2412 op
= dmalloc (sizeof (failover_option_t
), MDL
);
2414 dfree (option
.data
, MDL
);
2415 return &null_failover_option
;
2422 /* Send a failover message header. */
2424 isc_result_t
dhcp_failover_put_message (dhcp_failover_link_t
*link
,
2425 omapi_object_t
*connection
,
2433 failover_option_t
*option
;
2434 unsigned char *opbuf
;
2435 isc_result_t status
= ISC_R_SUCCESS
;
2438 /* Run through the argument list once to compute the length of
2439 the option portion of the message. */
2440 va_start (list
, msg_type
);
2441 while ((option
= va_arg (list
, failover_option_t
*))) {
2442 if (option
!= &skip_failover_option
)
2443 size
+= option
-> count
;
2444 if (option
== &null_failover_option
)
2449 /* Allocate an option buffer, unless we got an error. */
2451 opbuf
= dmalloc (size
, MDL
);
2453 status
= ISC_R_NOMEMORY
;
2456 va_start (list
, msg_type
);
2457 while ((option
= va_arg (list
, failover_option_t
*))) {
2458 if (option
== &skip_failover_option
)
2460 if (!bad_option
&& opbuf
)
2461 memcpy (&opbuf
[opix
],
2462 option
-> data
, option
-> count
);
2463 if (option
!= &null_failover_option
&&
2464 option
!= &skip_failover_option
) {
2465 opix
+= option
-> count
;
2466 dfree (option
-> data
, MDL
);
2467 dfree (option
, MDL
);
2472 return ISC_R_INVALIDARG
;
2474 /* Now send the message header. */
2476 /* Message length. */
2477 status
= omapi_connection_put_uint16 (connection
, size
+ 12);
2478 if (status
!= ISC_R_SUCCESS
)
2483 status
= omapi_connection_copyin (connection
, &cbuf
, 1);
2484 if (status
!= ISC_R_SUCCESS
)
2487 /* Payload offset. */
2489 status
= omapi_connection_copyin (connection
, &cbuf
, 1);
2490 if (status
!= ISC_R_SUCCESS
)
2494 status
= omapi_connection_put_uint32 (connection
, (u_int32_t
)cur_time
);
2495 if (status
!= ISC_R_SUCCESS
)
2498 /* Transaction ID. */
2499 status
= omapi_connection_put_uint32 (connection
, link
-> xid
++);
2500 if (status
!= ISC_R_SUCCESS
)
2505 status
= omapi_connection_copyin (connection
, opbuf
, size
);
2506 if (status
!= ISC_R_SUCCESS
)
2513 omapi_disconnect (connection
, 1);
2517 /* Send a connect message. */
2519 isc_result_t
dhcp_failover_send_connect (omapi_object_t
*l
)
2521 dhcp_failover_link_t
*link
;
2522 dhcp_failover_state_t
*state
;
2523 isc_result_t status
;
2524 #if defined (DEBUG_FAILOVER_MESSAGES)
2526 unsigned obufix
= 0;
2528 # define FMA obuf, &obufix, sizeof obuf
2529 failover_print (FMA
, "(connect");
2531 # define FMA (unsigned char *)0, (unsigned *)0, 0
2534 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
2535 return ISC_R_INVALIDARG
;
2536 link
= (dhcp_failover_link_t
*)l
;
2537 state
= link
-> state_object
;
2538 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
2539 return ISC_R_INVALIDARG
;
2541 status
= (dhcp_failover_put_message
2544 dhcp_failover_make_option (FTO_SERVER_ADDR
, FMA
,
2545 state
-> server_identifier
.len
,
2546 state
-> server_identifier
.data
),
2547 dhcp_failover_make_option (FTO_MAX_UNACKED
, FMA
,
2548 state
-> max_flying_updates
),
2549 dhcp_failover_make_option (FTO_RECEIVE_TIMER
, FMA
,
2550 state
-> max_response_delay
),
2551 dhcp_failover_option_printf (FTO_VENDOR_CLASS
, FMA
,
2552 "isc-%s", DHCP_VERSION
),
2553 dhcp_failover_make_option (FTO_PROTOCOL_VERSION
, FMA
,
2554 DHCP_FAILOVER_VERSION
),
2555 dhcp_failover_make_option (FTO_TLS_REQUEST
, FMA
,
2557 dhcp_failover_make_option (FTO_MCLT
, FMA
,
2559 dhcp_failover_make_option (FTO_HBA
, FMA
, 32, state
-> hba
),
2560 (failover_option_t
*)0));
2562 #if defined (DEBUG_FAILOVER_MESSAGES)
2563 if (status
!= ISC_R_SUCCESS
)
2564 failover_print (FMA
, " (failed)");
2565 failover_print (FMA
, ")");
2567 log_debug ("%s", obuf
);
2573 isc_result_t
dhcp_failover_send_connectack (omapi_object_t
*l
, int reason
)
2575 dhcp_failover_link_t
*link
;
2576 dhcp_failover_state_t
*state
;
2577 isc_result_t status
;
2578 #if defined (DEBUG_FAILOVER_MESSAGES)
2580 unsigned obufix
= 0;
2582 # define FMA obuf, &obufix, sizeof obuf
2583 failover_print (FMA
, "(connectack");
2585 # define FMA (unsigned char *)0, (unsigned *)0, 0
2588 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
2589 return ISC_R_INVALIDARG
;
2590 link
= (dhcp_failover_link_t
*)l
;
2591 state
= link
-> state_object
;
2592 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
2593 return ISC_R_INVALIDARG
;
2595 status
= (dhcp_failover_put_message
2598 dhcp_failover_make_option (FTO_SERVER_ADDR
, FMA
,
2599 state
-> server_identifier
.len
,
2600 state
-> server_identifier
.data
),
2601 dhcp_failover_make_option (FTO_MAX_UNACKED
, FMA
,
2602 state
-> max_flying_updates
),
2603 dhcp_failover_make_option (FTO_RECEIVE_TIMER
, FMA
,
2604 state
-> max_response_delay
),
2605 dhcp_failover_option_printf (FTO_VENDOR_CLASS
, FMA
,
2606 "isc-%s", DHCP_VERSION
),
2607 dhcp_failover_make_option (FTO_PROTOCOL_VERSION
, FMA
,
2608 DHCP_FAILOVER_VERSION
),
2609 dhcp_failover_make_option (FTO_TLS_REQUEST
, FMA
,
2612 ? dhcp_failover_make_option (FTO_REJECT_REASON
,
2614 : &skip_failover_option
),
2615 (failover_option_t
*)0));
2617 #if defined (DEBUG_FAILOVER_MESSAGES)
2618 if (status
!= ISC_R_SUCCESS
)
2619 failover_print (FMA
, " (failed)");
2620 failover_print (FMA
, ")");
2622 log_debug ("%s", obuf
);
2628 isc_result_t
dhcp_failover_send_disconnect (omapi_object_t
*l
,
2630 const char *message
)
2632 dhcp_failover_link_t
*link
;
2633 dhcp_failover_state_t
*state
;
2634 isc_result_t status
;
2635 #if defined (DEBUG_FAILOVER_MESSAGES)
2637 unsigned obufix
= 0;
2639 # define FMA obuf, &obufix, sizeof obuf
2640 failover_print (FMA
, "(disconnect");
2642 # define FMA (unsigned char *)0, (unsigned *)0, 0
2645 if (!l
|| l
-> type
!= dhcp_type_failover_link
)
2646 return ISC_R_INVALIDARG
;
2647 link
= (dhcp_failover_link_t
*)l
;
2648 state
= link
-> state_object
;
2649 if (!l
-> outer
|| l
-> outer
-> type
!= omapi_type_connection
)
2650 return ISC_R_INVALIDARG
;
2652 if (!message
&& reason
)
2653 message
= dhcp_failover_reject_reason_print (reason
);
2655 status
= (dhcp_failover_put_message
2658 dhcp_failover_make_option (FTO_REJECT_REASON
,
2661 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
2662 strlen (message
), message
)
2663 : &skip_failover_option
),
2664 (failover_option_t
*)0));
2666 #if defined (DEBUG_FAILOVER_MESSAGES)
2667 if (status
!= ISC_R_SUCCESS
)
2668 failover_print (FMA
, " (failed)");
2669 failover_print (FMA
, ")");
2671 log_debug ("%s", obuf
);
2677 /* Send a Bind Update message. */
2679 isc_result_t
dhcp_failover_send_bind_update (dhcp_failover_state_t
*state
,
2680 struct lease
*lease
)
2682 dhcp_failover_link_t
*link
;
2683 isc_result_t status
;
2684 #if defined (DEBUG_FAILOVER_MESSAGES)
2686 unsigned obufix
= 0;
2689 # define FMA obuf, &obufix, sizeof obuf
2690 failover_print (FMA
, "(bndupd");
2692 # define FMA (unsigned char *)0, (unsigned *)0, 0
2695 if (!state
-> link_to_peer
||
2696 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
2697 return ISC_R_INVALIDARG
;
2698 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
2700 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
2701 return ISC_R_INVALIDARG
;
2703 /* Kludge up the binding status. */
2704 if (lease
-> flags
& ABANDONED
)
2705 binding_status
= FTS_ABANDONED
;
2706 else if (lease
-> tsfp
<= cur_time
) {
2707 if (lease
-> flags
& PEER_IS_OWNER
) {
2708 if (state
-> i_am
== primary
)
2709 binding_status
= FTS_BACKUP
;
2711 binding_status
= FTS_FREE
;
2713 if (state
-> i_am
== primary
)
2714 binding_status
= FTS_FREE
;
2716 binding_status
= FTS_BACKUP
;
2718 } else if (lease
-> ends
<= cur_time
) {
2719 binding_status
= FTS_EXPIRED
;
2721 binding_status
= FTS_ACTIVE
;
2723 /* Send the update. */
2724 status
= (dhcp_failover_put_message
2725 (link
, link
-> outer
,
2727 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS
, FMA
,
2728 lease
-> ip_addr
.len
,
2729 lease
-> ip_addr
.iabuf
),
2730 dhcp_failover_make_option (FTO_BINDING_STATUS
, FMA
,
2733 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER
, FMA
,
2736 : &skip_failover_option
,
2737 lease
-> hardware_addr
.hlen
2738 ? dhcp_failover_make_option (FTO_CHADDR
, FMA
,
2739 lease
-> hardware_addr
.hlen
,
2740 lease
-> hardware_addr
.hbuf
)
2741 : &skip_failover_option
,
2742 dhcp_failover_make_option (FTO_LEASE_EXPIRY
, FMA
,
2744 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY
, FMA
,
2746 dhcp_failover_make_option (FTO_STOS
, FMA
,
2748 dhcp_failover_make_option (FTO_CLTT
, FMA
,
2750 &skip_failover_option
, /* XXX DDNS */
2751 &skip_failover_option
, /* XXX request options */
2752 &skip_failover_option
, /* XXX reply options */
2753 (failover_option_t
*)0));
2755 #if defined (DEBUG_FAILOVER_MESSAGES)
2756 if (status
!= ISC_R_SUCCESS
)
2757 failover_print (FMA
, " (failed)");
2758 failover_print (FMA
, ")");
2760 log_debug ("%s", obuf
);
2766 /* Send a Bind ACK message. */
2768 isc_result_t
dhcp_failover_send_bind_ack (dhcp_failover_state_t
*state
,
2769 struct lease
*lease
,
2770 failover_message_t
*msg
,
2771 int reason
, const char *message
)
2773 dhcp_failover_link_t
*link
;
2774 isc_result_t status
;
2775 #if defined (DEBUG_FAILOVER_MESSAGES)
2777 unsigned obufix
= 0;
2780 # define FMA obuf, &obufix, sizeof obuf
2781 failover_print (FMA
, "(bndack");
2783 # define FMA (unsigned char *)0, (unsigned *)0, 0
2786 if (!state
-> link_to_peer
||
2787 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
2788 return ISC_R_INVALIDARG
;
2789 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
2791 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
2792 return ISC_R_INVALIDARG
;
2794 /* Kludge up the binding status. */
2795 if (lease
-> flags
& ABANDONED
)
2796 binding_status
= FTS_ABANDONED
;
2797 else if (lease
-> tsfp
<= cur_time
) {
2798 if (lease
-> flags
& PEER_IS_OWNER
) {
2799 if (state
-> i_am
== primary
)
2800 binding_status
= FTS_BACKUP
;
2802 binding_status
= FTS_FREE
;
2804 if (state
-> i_am
== primary
)
2805 binding_status
= FTS_FREE
;
2807 binding_status
= FTS_BACKUP
;
2809 } else if (lease
-> ends
<= cur_time
) {
2810 binding_status
= FTS_EXPIRED
;
2812 binding_status
= FTS_ACTIVE
;
2814 if (!message
&& reason
)
2815 message
= dhcp_failover_reject_reason_print (reason
);
2817 /* Send the update. */
2818 status
= (dhcp_failover_put_message
2819 (link
, link
-> outer
,
2821 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS
, FMA
,
2822 lease
-> ip_addr
.len
,
2823 lease
-> ip_addr
.iabuf
),
2824 dhcp_failover_make_option (FTO_BINDING_STATUS
, FMA
,
2827 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER
, FMA
,
2830 : &skip_failover_option
,
2831 lease
-> hardware_addr
.hlen
2832 ? dhcp_failover_make_option (FTO_CHADDR
, FMA
,
2833 lease
-> hardware_addr
.hlen
,
2834 lease
-> hardware_addr
.hbuf
)
2835 : &skip_failover_option
,
2836 dhcp_failover_make_option (FTO_LEASE_EXPIRY
, FMA
,
2838 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY
, FMA
,
2839 msg
-> potential_expiry
),
2840 dhcp_failover_make_option (FTO_STOS
, FMA
,
2842 dhcp_failover_make_option (FTO_CLTT
, FMA
,
2845 ? dhcp_failover_make_option (FTO_REJECT_REASON
,
2847 : &skip_failover_option
,
2849 ? dhcp_failover_make_option (FTO_MESSAGE
, FMA
,
2850 strlen (message
), message
)
2851 : &skip_failover_option
),
2852 &skip_failover_option
, /* XXX DDNS */
2853 &skip_failover_option
, /* XXX request options */
2854 &skip_failover_option
, /* XXX reply options */
2855 (failover_option_t
*)0));
2857 #if defined (DEBUG_FAILOVER_MESSAGES)
2858 if (status
!= ISC_R_SUCCESS
)
2859 failover_print (FMA
, " (failed)");
2860 failover_print (FMA
, ")");
2862 log_debug ("%s", obuf
);
2868 isc_result_t
dhcp_failover_send_poolreq (dhcp_failover_state_t
*state
)
2870 dhcp_failover_link_t
*link
;
2871 isc_result_t status
;
2872 #if defined (DEBUG_FAILOVER_MESSAGES)
2874 unsigned obufix
= 0;
2876 # define FMA obuf, &obufix, sizeof obuf
2877 failover_print (FMA
, "(poolreq");
2879 # define FMA (unsigned char *)0, (unsigned *)0, 0
2882 if (!state
-> link_to_peer
||
2883 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
2884 return ISC_R_INVALIDARG
;
2885 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
2887 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
2888 return ISC_R_INVALIDARG
;
2890 status
= (dhcp_failover_put_message
2891 (link
, link
-> outer
,
2893 (failover_option_t
*)0));
2895 #if defined (DEBUG_FAILOVER_MESSAGES)
2896 if (status
!= ISC_R_SUCCESS
)
2897 failover_print (FMA
, " (failed)");
2898 failover_print (FMA
, ")");
2900 log_debug ("%s", obuf
);
2906 isc_result_t
dhcp_failover_send_poolresp (dhcp_failover_state_t
*state
,
2909 dhcp_failover_link_t
*link
;
2910 isc_result_t status
;
2911 #if defined (DEBUG_FAILOVER_MESSAGES)
2913 unsigned obufix
= 0;
2915 # define FMA obuf, &obufix, sizeof obuf
2916 failover_print (FMA
, "(poolreq");
2918 # define FMA (unsigned char *)0, (unsigned *)0, 0
2921 if (!state
-> link_to_peer
||
2922 state
-> link_to_peer
-> type
!= dhcp_type_failover_link
)
2923 return ISC_R_INVALIDARG
;
2924 link
= (dhcp_failover_link_t
*)state
-> link_to_peer
;
2926 if (!link
-> outer
|| link
-> outer
-> type
!= omapi_type_connection
)
2927 return ISC_R_INVALIDARG
;
2929 status
= (dhcp_failover_put_message
2930 (link
, link
-> outer
,
2932 dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED
, FMA
,
2934 (failover_option_t
*)0));
2936 #if defined (DEBUG_FAILOVER_MESSAGES)
2937 if (status
!= ISC_R_SUCCESS
)
2938 failover_print (FMA
, " (failed)");
2939 failover_print (FMA
, ")");
2941 log_debug ("%s", obuf
);
2947 isc_result_t
dhcp_failover_process_bind_update (dhcp_failover_state_t
*state
,
2948 failover_message_t
*msg
)
2950 struct lease lt
, *lease
;
2953 ia
.len
= sizeof msg
-> assigned_addr
;
2954 memcpy (ia
.iabuf
, &msg
-> assigned_addr
, ia
.len
);
2956 lease
= find_lease_by_ip_addr (ia
);
2958 log_info ("bind update on %s from %s: no such lease.",
2959 piaddr (ia
), state
-> name
);
2960 return ISC_R_SUCCESS
;
2963 /* XXX check for conflicts. */
2965 /* Install the new info. */
2971 if (msg
-> options_present
& FTB_CHADDR
) {
2972 if (msg
-> chaddr
.count
> sizeof lt
.hardware_addr
.hbuf
) {
2973 log_info ("bind update on %s from %s: chaddr too long",
2974 piaddr (ia
), state
-> name
);
2975 return ISC_R_SUCCESS
;
2977 lt
.hardware_addr
.hlen
= msg
-> chaddr
.count
;
2978 memcpy (lt
.hardware_addr
.hbuf
, msg
-> chaddr
.data
,
2979 msg
-> chaddr
.count
);
2982 if (msg
-> options_present
& FTB_CLIENT_IDENTIFIER
) {
2983 lt
.uid_len
= msg
-> client_identifier
.count
;
2984 if (lt
.uid_len
> sizeof lt
.uid_buf
) {
2985 lt
.uid_max
= lt
.uid_len
;
2986 lt
.uid
= dmalloc (lt
.uid_len
, MDL
);
2988 return ISC_R_SUCCESS
;
2990 lt
.uid_max
= sizeof lt
.uid_buf
;
2991 lt
.uid
= lt
.uid_buf
;
2993 memcpy (lt
.uid
, msg
-> client_identifier
.data
, lt
.uid_len
);
2996 /* XXX Times may need to be adjusted based on clock skew! */
2997 if (msg
-> options_present
& FTB_STOS
) {
2998 lt
.starts
= msg
-> stos
;
3000 if (msg
-> options_present
& FTB_LEASE_EXPIRY
) {
3001 lt
.ends
= msg
-> expiry
;
3003 if (msg
-> options_present
& FTB_CLTT
) {
3004 lt
.cltt
= msg
-> client_ltt
;
3006 if (msg
-> options_present
& FTB_POTENTIAL_EXPIRY
) {
3007 lt
.tsfp
= msg
-> potential_expiry
;
3010 if (msg
-> options_present
& FTB_BINDING_STATUS
) {
3011 if (state
-> i_am
== primary
) {
3012 if (msg
-> binding_status
== FTS_BACKUP
)
3013 lease
-> flags
|= PEER_IS_OWNER
;
3014 else if (msg
-> binding_status
== FTS_FREE
)
3015 lease
-> flags
&= ~PEER_IS_OWNER
;
3017 if (msg
-> binding_status
== FTS_BACKUP
)
3018 lease
-> flags
&= PEER_IS_OWNER
;
3019 else if (msg
-> binding_status
== FTS_FREE
)
3020 lease
-> flags
|= ~PEER_IS_OWNER
;
3024 /* Try to install the new information. */
3025 if (!supersede_lease (lease
, <
, 1, 0)) {
3026 dhcp_failover_send_bind_ack (state
, lease
, msg
,
3028 "database update failed.");
3030 dhcp_failover_send_bind_ack (state
, lease
, msg
, 0, 0);
3032 return ISC_R_SUCCESS
;
3035 isc_result_t
dhcp_failover_process_bind_ack (dhcp_failover_state_t
*state
,
3036 failover_message_t
*msg
)
3038 struct lease lt
, *lease
;
3041 ia
.len
= sizeof msg
-> assigned_addr
;
3042 memcpy (ia
.iabuf
, &msg
-> assigned_addr
, ia
.len
);
3044 lease
= find_lease_by_ip_addr (ia
);
3046 log_info ("bind update on %s from %s: no such lease.",
3047 piaddr (ia
), state
-> name
);
3048 return ISC_R_SUCCESS
;
3051 /* XXX check for conflicts. */
3053 /* Install the new info. */
3059 /* XXX Times may need to be adjusted based on clock skew! */
3060 if (msg
-> options_present
& FTB_POTENTIAL_EXPIRY
) {
3061 lt
.tsfp
= msg
-> potential_expiry
;
3064 /* Try to install the new information. */
3065 supersede_lease (lease
, <
, 1, 0);
3067 state
-> cur_unacked_updates
--;
3068 dhcp_failover_ack_queue_remove (state
, lease
);
3070 /* If there are updates pending, we've created space to send at
3072 dhcp_failover_send_updates (state
);
3074 return ISC_R_SUCCESS
;
3077 #if defined (DEBUG_FAILOVER_MESSAGES)
3078 /* Print hunks of failover messages, doing line breaks as appropriate.
3079 Note that this assumes syslog is being used, rather than, e.g., the
3080 Windows NT logging facility, where just dumping the whole message in
3081 one hunk would be more appropriate. */
3083 void failover_print (char *obuf
,
3084 unsigned *obufix
, unsigned obufmax
, const char *s
)
3086 int len
= strlen (s
);
3088 while (len
+ *obufix
+ 1 >= obufmax
) {
3089 log_debug ("%s", obuf
);
3091 log_debug ("%s", s
);
3097 strcpy (&obuf
[*obufix
], s
);
3100 #endif /* defined (DEBUG_FAILOVER_MESSAGES) */
3102 /* Taken from draft-ietf-dhc-loadb-01.txt: */
3103 /* A "mixing table" of 256 distinct values, in pseudo-random order. */
3104 unsigned char loadb_mx_tbl
[256] = {
3105 251, 175, 119, 215, 81, 14, 79, 191, 103, 49,
3106 181, 143, 186, 157, 0, 232, 31, 32, 55, 60,
3107 152, 58, 17, 237, 174, 70, 160, 144, 220, 90,
3108 57, 223, 59, 3, 18, 140, 111, 166, 203, 196,
3109 134, 243, 124, 95, 222, 179, 197, 65, 180, 48,
3110 36, 15, 107, 46, 233, 130, 165, 30, 123, 161,
3111 209, 23, 97, 16, 40, 91, 219, 61, 100, 10,
3112 210, 109, 250, 127, 22, 138, 29, 108, 244, 67,
3113 207, 9, 178, 204, 74, 98, 126, 249, 167, 116,
3114 34, 77, 193, 200, 121, 5, 20, 113, 71, 35,
3115 128, 13, 182, 94, 25, 226, 227, 199, 75, 27,
3116 41, 245, 230, 224, 43, 225, 177, 26, 155, 150,
3117 212, 142, 218, 115, 241, 73, 88, 105, 39, 114,
3118 62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
3119 154, 122, 12, 84, 82, 163, 44, 139, 228, 236,
3120 205, 242, 217, 11, 187, 146, 159, 64, 86, 239,
3121 195, 42, 106, 198, 118, 112, 184, 172, 87, 2,
3122 173, 117, 176, 229, 247, 253, 137, 185, 99, 164,
3123 102, 147, 45, 66, 231, 52, 141, 211, 194, 206,
3124 246, 238, 56, 110, 78, 248, 63, 240, 189, 93,
3125 92, 51, 53, 183, 19, 171, 72, 50, 33, 104,
3126 101, 69, 8, 252, 83, 120, 76, 135, 85, 54,
3127 202, 125, 188, 213, 96, 235, 136, 208, 162, 129,
3128 190, 132, 156, 38, 47, 1, 7, 254, 24, 4,
3129 216, 131, 89, 21, 28, 133, 37, 153, 149, 80,
3130 170, 68, 6, 169, 234, 151 };
3132 static unsigned char loadb_p_hash (const unsigned char *, unsigned);
3133 static unsigned char loadb_p_hash (const unsigned char *key
, unsigned len
)
3135 unsigned char hash
= len
;
3137 for(i
= len
; i
> 0; )
3138 hash
= loadb_mx_tbl
[hash
^ (key
[--i
])];
3142 int load_balance_mine (struct packet
*packet
, dhcp_failover_state_t
*state
)
3144 struct option_cache
*oc
;
3145 struct data_string ds
;
3146 unsigned char hbaix
;
3149 if (state
-> load_balance_max_secs
< ntohs (packet
-> raw
-> secs
)) {
3153 oc
= lookup_option (&dhcp_universe
, packet
-> options
,
3154 DHO_DHCP_CLIENT_IDENTIFIER
);
3155 memset (&ds
, 0, sizeof ds
);
3157 evaluate_option_cache (&ds
, packet
, (struct lease
*)0,
3158 packet
-> options
, (struct option_state
*)0,
3159 &global_scope
, oc
, MDL
)) {
3160 hbaix
= loadb_p_hash (ds
.data
, ds
.len
);
3162 hbaix
= loadb_p_hash (packet
-> raw
-> chaddr
,
3163 packet
-> raw
-> hlen
);
3165 hm
= (state
-> hba
[hbaix
/ 8] & (1 << (hbaix
& 3)));
3166 if (state
-> i_am
== primary
)
3171 #endif /* defined (FAILOVER_PROTOCOL) */