]> git.ipfire.org Git - thirdparty/dhcp.git/blame - server/failover.c
Couple of casts to fix build errors reported by robie.
[thirdparty/dhcp.git] / server / failover.c
CommitLineData
c68d2a87
TL
1/* failover.c
2
3 Failover protocol support code... */
4
5/*
706792c9 6 * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
98311e4b 7 * Copyright (c) 1999-2003 by Internet Software Consortium
c68d2a87 8 *
98311e4b
DH
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.
c68d2a87 12 *
98311e4b
DH
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.
c68d2a87 20 *
98311e4b
DH
21 * Internet Systems Consortium, Inc.
22 * 950 Charter Street
23 * Redwood City, CA 94063
24 * <info@isc.org>
25 * http://www.isc.org/
49733f31 26 *
98311e4b 27 * This software has been written for Internet Systems Consortium
49733f31 28 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
98311e4b 29 * To learn more about Internet Systems Consortium, see
49733f31
TL
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''.
c68d2a87
TL
33 */
34
c68d2a87 35#include "dhcpd.h"
d9eefc5d 36#include <omapip/omapip_p.h>
c68d2a87
TL
37
38#if defined (FAILOVER_PROTOCOL)
d758ad8c 39dhcp_failover_state_t *failover_states;
d9eefc5d 40static isc_result_t do_a_failover_option (omapi_object_t *,
d50d0b82 41 dhcp_failover_link_t *);
02ca1b45 42dhcp_failover_listener_t *failover_listeners;
c68d2a87 43
988c1ca7
DN
44static isc_result_t failover_message_reference (failover_message_t **,
45 failover_message_t *,
46 const char *file, int line);
47static isc_result_t failover_message_dereference (failover_message_t **,
48 const char *file, int line);
49
fc3b9c90 50static void dhcp_failover_pool_balance(dhcp_failover_state_t *state);
2426234f
DH
51static void dhcp_failover_pool_reqbalance(dhcp_failover_state_t *state);
52static int dhcp_failover_pool_dobalance(dhcp_failover_state_t *state);
53static INLINE int secondary_not_hoarding(dhcp_failover_state_t *state,
54 struct pool *p);
55
56
85a53735
TL
57void dhcp_failover_startup ()
58{
59 dhcp_failover_state_t *state;
60 isc_result_t status;
61
62 for (state = failover_states; state; state = state -> next) {
9f3da356 63 dhcp_failover_state_transition (state, "startup");
02ca1b45 64
a609e69b
TL
65 if (state -> pool_count == 0) {
66 log_error ("failover peer declaration with no %s",
67 "referring pools.");
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.");
73 }
02ca1b45
TL
74 /* In case the peer is already running, immediately try
75 to establish a connection with it. */
8998f8df
TL
76 status = dhcp_failover_link_initiate ((omapi_object_t *)state);
77 if (status != ISC_R_SUCCESS && status != ISC_R_INCOMPLETE) {
0db87765
TL
78#if defined (DEBUG_FAILOVER_TIMING)
79 log_info ("add_timeout +90 dhcp_failover_reconnect");
80#endif
02ca1b45
TL
81 add_timeout (cur_time + 90,
82 dhcp_failover_reconnect, state,
83 (tvref_t)
84 dhcp_failover_state_reference,
85 (tvunref_t)
86 dhcp_failover_state_dereference);
85a53735
TL
87 log_error ("failover peer %s: %s", state -> name,
88 isc_result_totext (status));
89 }
02ca1b45
TL
90
91 status = (dhcp_failover_listen
92 ((omapi_object_t *)state));
93 if (status != ISC_R_SUCCESS) {
0db87765
TL
94#if defined (DEBUG_FAILOVER_TIMING)
95 log_info ("add_timeout +90 %s",
96 "dhcp_failover_listener_restart");
97#endif
02ca1b45
TL
98 add_timeout (cur_time + 90,
99 dhcp_failover_listener_restart,
100 state,
101 (tvref_t)omapi_object_reference,
102 (tvunref_t)omapi_object_dereference);
103 }
85a53735
TL
104 }
105}
106
d758ad8c 107int dhcp_failover_write_all_states ()
9f3da356
TL
108{
109 dhcp_failover_state_t *state;
110
111 for (state = failover_states; state; state = state -> next) {
d758ad8c
TL
112 if (!write_failover_state (state))
113 return 0;
9f3da356 114 }
d758ad8c 115 return 1;
9f3da356
TL
116}
117
85a53735 118isc_result_t enter_failover_peer (peer)
dd53dc5a 119 dhcp_failover_state_t *peer;
c68d2a87 120{
85a53735
TL
121 dhcp_failover_state_t *dup = (dhcp_failover_state_t *)0;
122 isc_result_t status;
123
20916cae 124 status = find_failover_peer (&dup, peer -> name, MDL);
85a53735
TL
125 if (status == ISC_R_NOTFOUND) {
126 if (failover_states) {
20916cae
TL
127 dhcp_failover_state_reference (&peer -> next,
128 failover_states, MDL);
129 dhcp_failover_state_dereference (&failover_states,
130 MDL);
85a53735 131 }
20916cae 132 dhcp_failover_state_reference (&failover_states, peer, MDL);
85a53735
TL
133 return ISC_R_SUCCESS;
134 }
20916cae 135 dhcp_failover_state_dereference (&dup, MDL);
85a53735
TL
136 if (status == ISC_R_SUCCESS)
137 return ISC_R_EXISTS;
85a53735 138 return status;
c68d2a87
TL
139}
140
20916cae 141isc_result_t find_failover_peer (peer, name, file, line)
dd53dc5a 142 dhcp_failover_state_t **peer;
85a53735 143 const char *name;
20916cae
TL
144 const char *file;
145 int line;
c68d2a87 146{
dd53dc5a 147 dhcp_failover_state_t *p;
c68d2a87 148
dd53dc5a
TL
149 for (p = failover_states; p; p = p -> next)
150 if (!strcmp (name, p -> name))
151 break;
152 if (p)
20916cae 153 return dhcp_failover_state_reference (peer, p, file, line);
dd53dc5a 154 return ISC_R_NOTFOUND;
c68d2a87
TL
155}
156
d50d0b82
TL
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
c57db45c 161 also a failover_link object, which has its own input state separate
d50d0b82
TL
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. */
170
171/* This, then, is the implemention of the failover link object. */
172
173isc_result_t dhcp_failover_link_initiate (omapi_object_t *h)
174{
175 isc_result_t status;
176 dhcp_failover_link_t *obj;
dd53dc5a
TL
177 dhcp_failover_state_t *state;
178 omapi_object_t *o;
179 int i;
180 struct data_string ds;
85a53735
TL
181 omapi_addr_list_t *addrs = (omapi_addr_list_t *)0;
182 omapi_addr_t local_addr;
d50d0b82 183
dd53dc5a
TL
184 /* Find the failover state in the object chain. */
185 for (o = h; o -> outer; o = o -> outer)
186 ;
187 for (; o; o = o -> inner) {
188 if (o -> type == dhcp_type_failover_state)
189 break;
d9eefc5d 190 }
dd53dc5a 191 if (!o)
d50d0b82 192 return ISC_R_INVALIDARG;
dd53dc5a 193 state = (dhcp_failover_state_t *)o;
d50d0b82 194
20916cae
TL
195 obj = (dhcp_failover_link_t *)0;
196 status = dhcp_failover_link_allocate (&obj, MDL);
197 if (status != ISC_R_SUCCESS)
198 return status;
02ca1b45
TL
199 option_cache_reference (&obj -> peer_address,
200 state -> partner.address, MDL);
201 obj -> peer_port = state -> partner.port;
20916cae 202 dhcp_failover_state_reference (&obj -> state_object, state, MDL);
dd53dc5a
TL
203
204 memset (&ds, 0, sizeof ds);
205 if (!evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0,
9e383163 206 (struct client_state *)0,
dd53dc5a
TL
207 (struct option_state *)0,
208 (struct option_state *)0,
4bd8800e 209 &global_scope, obj -> peer_address, MDL)) {
20916cae 210 dhcp_failover_link_dereference (&obj, MDL);
dd53dc5a 211 return ISC_R_UNEXPECTED;
d50d0b82 212 }
dd53dc5a 213
85a53735
TL
214 /* Make an omapi address list out of a buffer containing zero or more
215 IPv4 addresses. */
216 status = omapi_addr_list_new (&addrs, ds.len / 4, MDL);
217 if (status != ISC_R_SUCCESS) {
20916cae 218 dhcp_failover_link_dereference (&obj, MDL);
85a53735
TL
219 return status;
220 }
221
222 for (i = 0; i < addrs -> count; i++) {
223 addrs -> addresses [i].addrtype = AF_INET;
224 addrs -> addresses [i].addrlen = sizeof (struct in_addr);
225 memcpy (addrs -> addresses [i].address,
226 &ds.data [i * 4], sizeof (struct in_addr));
227 addrs -> addresses [i].port = obj -> peer_port;
dd53dc5a 228 }
4bd8800e 229 data_string_forget (&ds, MDL);
dd53dc5a 230
85a53735 231 /* Now figure out the local address that we're supposed to use. */
de8dfadf
TL
232 if (!state -> me.address ||
233 !evaluate_option_cache (&ds, (struct packet *)0,
234 (struct lease *)0,
9e383163 235 (struct client_state *)0,
85a53735
TL
236 (struct option_state *)0,
237 (struct option_state *)0,
02ca1b45 238 &global_scope, state -> me.address,
85a53735
TL
239 MDL)) {
240 memset (&local_addr, 0, sizeof local_addr);
241 local_addr.addrtype = AF_INET;
242 local_addr.addrlen = sizeof (struct in_addr);
9f3da356 243 if (!state -> server_identifier.len) {
a609e69b 244 log_fatal ("failover peer %s: no local address.",
9f3da356
TL
245 state -> name);
246 }
85a53735
TL
247 } else {
248 if (ds.len != sizeof (struct in_addr)) {
249 data_string_forget (&ds, MDL);
20916cae 250 dhcp_failover_link_dereference (&obj, MDL);
85a53735
TL
251 omapi_addr_list_dereference (&addrs, MDL);
252 return ISC_R_INVALIDARG;
253 }
254 local_addr.addrtype = AF_INET;
255 local_addr.addrlen = ds.len;
256 memcpy (local_addr.address, ds.data, ds.len);
9f3da356
TL
257 if (!state -> server_identifier.len)
258 data_string_copy (&state -> server_identifier,
259 &ds, MDL);
85a53735 260 data_string_forget (&ds, MDL);
9f3da356 261 local_addr.port = 0; /* Let the O.S. choose. */
85a53735
TL
262 }
263
264 status = omapi_connect_list ((omapi_object_t *)obj,
265 addrs, &local_addr);
266 omapi_addr_list_dereference (&addrs, MDL);
267
20916cae
TL
268 dhcp_failover_link_dereference (&obj, MDL);
269 return status;
d50d0b82
TL
270}
271
272isc_result_t dhcp_failover_link_signal (omapi_object_t *h,
273 const char *name, va_list ap)
274{
275 isc_result_t status;
276 dhcp_failover_link_t *link;
277 omapi_object_t *c;
9f3da356 278 dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
fa9b593d
DH
279 char *sname;
280 int slen;
d50d0b82
TL
281
282 if (h -> type != dhcp_type_failover_link) {
283 /* XXX shouldn't happen. Put an assert here? */
284 return ISC_R_UNEXPECTED;
285 }
286 link = (dhcp_failover_link_t *)h;
287
85a53735 288 if (!strcmp (name, "connect")) {
02ca1b45 289 if (link -> state_object -> i_am == primary) {
85a53735 290 status = dhcp_failover_send_connect (h);
02ca1b45
TL
291 if (status != ISC_R_SUCCESS) {
292 log_info ("dhcp_failover_send_connect: %s",
293 isc_result_totext (status));
294 omapi_disconnect (h -> outer, 1);
295 }
da292833
TL
296 } else
297 status = ISC_R_SUCCESS;
298 /* Allow the peer fifteen seconds to send us a
299 startup message. */
0db87765
TL
300#if defined (DEBUG_FAILOVER_TIMING)
301 log_info ("add_timeout +15 %s",
302 "dhcp_failover_link_startup_timeout");
303#endif
da292833
TL
304 add_timeout (cur_time + 15,
305 dhcp_failover_link_startup_timeout,
306 link,
307 (tvref_t)dhcp_failover_link_reference,
308 (tvunref_t)dhcp_failover_link_dereference);
309 return status;
85a53735
TL
310 }
311
9f3da356 312 if (!strcmp (name, "disconnect")) {
da292833 313 if (link -> state_object) {
db6960a7
TL
314 dhcp_failover_state_reference (&state,
315 link -> state_object, MDL);
e651f9b2 316 link -> state = dhcp_flink_disconnected;
db6960a7 317
e651f9b2 318 /* Make the transition. */
da292833 319 if (state -> link_to_peer == link) {
01c6e68a
TL
320 dhcp_failover_state_transition (link -> state_object,
321 name);
db6960a7 322
da292833 323 /* Start trying to reconnect. */
0db87765
TL
324#if defined (DEBUG_FAILOVER_TIMING)
325 log_info ("add_timeout +5 %s",
326 "dhcp_failover_reconnect");
327#endif
da292833
TL
328 add_timeout (cur_time + 5, dhcp_failover_reconnect,
329 state,
330 (tvref_t)dhcp_failover_state_reference,
331 (tvunref_t)dhcp_failover_state_dereference);
332 }
380da6c0 333 dhcp_failover_state_dereference (&state, MDL);
e651f9b2
TL
334 }
335 return ISC_R_SUCCESS;
9f3da356
TL
336 }
337
98311e4b
DH
338 if (!strcmp (name, "status")) {
339 if (link -> state_object) {
340 isc_result_t status;
341
342 status = va_arg(ap, isc_result_t);
343
344 if ((status == ISC_R_HOSTUNREACH) || (status == ISC_R_TIMEDOUT)) {
345 dhcp_failover_state_reference (&state,
346 link -> state_object, MDL);
347 link -> state = dhcp_flink_disconnected;
348
349 /* Make the transition. */
350 dhcp_failover_state_transition (link -> state_object,
351 "disconnect");
352
353 /* Start trying to reconnect. */
354#if defined (DEBUG_FAILOVER_TIMING)
355 log_info ("add_timeout +5 %s",
356 "dhcp_failover_reconnect");
357#endif
358 add_timeout (cur_time + 5, dhcp_failover_reconnect,
359 state,
360 (tvref_t)dhcp_failover_state_reference,
361 (tvunref_t)dhcp_failover_state_dereference);
362 }
363 dhcp_failover_state_dereference (&state, MDL);
364 }
365 return ISC_R_SUCCESS;
366 }
367
d50d0b82
TL
368 /* Not a signal we recognize? */
369 if (strcmp (name, "ready")) {
d9eefc5d 370 if (h -> inner && h -> inner -> type -> signal_handler)
9f3da356
TL
371 return (*(h -> inner -> type -> signal_handler))
372 (h -> inner, name, ap);
d50d0b82
TL
373 return ISC_R_NOTFOUND;
374 }
375
d9eefc5d 376 if (!h -> outer || h -> outer -> type != omapi_type_connection)
d50d0b82 377 return ISC_R_INVALIDARG;
d9eefc5d 378 c = h -> outer;
d50d0b82
TL
379
380 /* We get here because we requested that we be woken up after
381 some number of bytes were read, and that number of bytes
382 has in fact been read. */
d9eefc5d 383 switch (link -> state) {
d50d0b82 384 case dhcp_flink_start:
d9eefc5d 385 link -> state = dhcp_flink_message_length_wait;
d50d0b82
TL
386 if ((omapi_connection_require (c, 2)) != ISC_R_SUCCESS)
387 break;
388 case dhcp_flink_message_length_wait:
25a69ca1 389 next_message:
d9eefc5d 390 link -> state = dhcp_flink_message_wait;
4bd8800e 391 link -> imsg = dmalloc (sizeof (failover_message_t), MDL);
d9eefc5d 392 if (!link -> imsg) {
988c1ca7 393 status = ISC_R_NOMEMORY;
d50d0b82 394 dhcp_flink_fail:
d9eefc5d 395 if (link -> imsg) {
988c1ca7
DN
396 failover_message_dereference (&link->imsg,
397 MDL);
d9eefc5d 398 }
d50d0b82 399 link -> state = dhcp_flink_disconnected;
988c1ca7
DN
400 log_info ("message length wait: %s",
401 isc_result_totext (status));
d50d0b82
TL
402 omapi_disconnect (c, 1);
403 /* XXX just blow away the protocol state now?
404 XXX or will disconnect blow it away? */
405 return ISC_R_UNEXPECTED;
406 }
25a69ca1 407 memset (link -> imsg, 0, sizeof (failover_message_t));
fa0b4709 408 link -> imsg -> refcnt = 1;
d9eefc5d
TL
409 /* Get the length: */
410 omapi_connection_get_uint16 (c, &link -> imsg_len);
411 link -> imsg_count = 0; /* Bytes read. */
a55ccdd0
DH
412
413 /* Ensure the message is of valid length. */
414 if (link->imsg_len < DHCP_FAILOVER_MIN_MESSAGE_SIZE ||
415 link->imsg_len > DHCP_FAILOVER_MAX_MESSAGE_SIZE) {
988c1ca7 416 status = ISC_R_UNEXPECTED;
d9eefc5d 417 goto dhcp_flink_fail;
988c1ca7 418 }
d50d0b82 419
9f3da356 420 if ((omapi_connection_require (c, link -> imsg_len - 2U)) !=
d50d0b82
TL
421 ISC_R_SUCCESS)
422 break;
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. */
432
433 link -> imsg_count += 2; /* Count the length as read. */
434
435 /* Get message type. */
436 omapi_connection_copyout (&link -> imsg -> type, c, 1);
437 link -> imsg_count++;
438
439 /* Get message payload offset. */
440 omapi_connection_copyout (&link -> imsg_payoff, c, 1);
441 link -> imsg_count++;
442
443 /* Get message time. */
444 omapi_connection_get_uint32 (c, &link -> imsg -> time);
445 link -> imsg_count += 4;
446
447 /* Get transaction ID. */
448 omapi_connection_get_uint32 (c, &link -> imsg -> xid);
449 link -> imsg_count += 4;
450
02ca1b45
TL
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),
454 link -> imsg_payoff,
455 (unsigned long)link -> imsg -> time,
456 (unsigned long)link -> imsg -> xid);
457#endif
d50d0b82
TL
458 /* Skip over any portions of the message header that we
459 don't understand. */
d9eefc5d 460 if (link -> imsg_payoff - link -> imsg_count) {
d50d0b82 461 omapi_connection_copyout ((unsigned char *)0, c,
d9eefc5d 462 (link -> imsg_payoff -
d50d0b82 463 link -> imsg_count));
d9eefc5d 464 link -> imsg_count = link -> imsg_payoff;
d50d0b82
TL
465 }
466
d50d0b82
TL
467 /* Now start sucking options off the wire. */
468 while (link -> imsg_count < link -> imsg_len) {
988c1ca7
DN
469 status = do_a_failover_option (c, link);
470 if (status != ISC_R_SUCCESS)
d50d0b82
TL
471 goto dhcp_flink_fail;
472 }
473
9f3da356
TL
474 /* If it's a connect message, try to associate it with
475 a state object. */
476 /* XXX this should be authenticated! */
477 if (link -> imsg -> type == FTM_CONNECT) {
007e3ee4
TL
478 const char *errmsg;
479 int reason;
a55ccdd0
DH
480
481 if (!(link->imsg->options_present &
482 FTB_RELATIONSHIP_NAME)) {
483 errmsg = "missing relationship-name";
484 reason = FTR_INVALID_PARTNER;
485 goto badconnect;
486 }
487
9f3da356
TL
488 /* See if we can find a failover_state object that
489 matches this connection. This message should only
490 be received by a secondary from a primary. */
491 for (s = failover_states; s; s = s -> next) {
a55ccdd0
DH
492 if (dhcp_failover_state_match_by_name(s,
493 &link->imsg->relationship_name))
9f3da356 494 state = s;
fa9b593d 495 }
9f3da356
TL
496
497 /* If we can't find a failover protocol state
498 for this remote host, drop the connection */
499 if (!state) {
fa9b593d 500 errmsg = "unknown failover relationship name";
007e3ee4
TL
501 reason = FTR_INVALID_PARTNER;
502
503 badconnect:
9f3da356
TL
504 /* XXX Send a refusal message first?
505 XXX Look in protocol spec for guidance. */
fa9b593d
DH
506
507 if (state != NULL) {
508 sname = state->name;
509 slen = strlen(sname);
510 } else if (link->imsg->options_present &
511 FTB_RELATIONSHIP_NAME) {
d7ec175b
SK
512 sname = (char *)link->imsg->
513 relationship_name.data;
fa9b593d
DH
514 slen = link->imsg->relationship_name.count;
515 } else {
516 sname = "unknown";
517 slen = strlen(sname);
518 }
519
520 log_error("Failover CONNECT from %.*s: %s",
521 slen, sname, errmsg);
9f3da356 522 dhcp_failover_send_connectack
007e3ee4
TL
523 ((omapi_object_t *)link, state,
524 reason, errmsg);
02ca1b45 525 log_info ("failover: disconnect: %s", errmsg);
9f3da356 526 omapi_disconnect (c, 0);
25a69ca1 527 link -> state = dhcp_flink_disconnected;
9f3da356
TL
528 return ISC_R_SUCCESS;
529 }
530
d758ad8c
TL
531 if ((cur_time > link -> imsg -> time &&
532 cur_time - link -> imsg -> time > 60) ||
533 (cur_time < link -> imsg -> time &&
534 link -> imsg -> time - cur_time > 60)) {
535 errmsg = "time offset too large";
536 reason = FTR_TIMEMISMATCH;
537 goto badconnect;
538 }
539
007e3ee4
TL
540 if (!(link -> imsg -> options_present & FTB_HBA) ||
541 link -> imsg -> hba.count != 32) {
542 errmsg = "invalid HBA";
543 reason = FTR_HBA_CONFLICT; /* XXX */
544 goto badconnect;
545 }
546 if (state -> hba)
547 dfree (state -> hba, MDL);
548 state -> hba = dmalloc (32, MDL);
549 if (!state -> hba) {
550 errmsg = "no memory";
551 reason = FTR_MISC_REJECT;
552 goto badconnect;
553 }
554 memcpy (state -> hba, link -> imsg -> hba.data, 32);
555
9f3da356 556 if (!link -> state_object)
20916cae
TL
557 dhcp_failover_state_reference
558 (&link -> state_object, state, MDL);
02ca1b45
TL
559 if (!link -> peer_address)
560 option_cache_reference
561 (&link -> peer_address,
562 state -> partner.address, MDL);
9f3da356
TL
563 }
564
565 /* If we don't have a state object at this point, it's
566 some kind of bogus situation, so just drop the
567 connection. */
568 if (!link -> state_object) {
02ca1b45 569 log_info ("failover: connect: no matching state.");
9f3da356 570 omapi_disconnect (c, 1);
25a69ca1 571 link -> state = dhcp_flink_disconnected;
9f3da356
TL
572 return ISC_R_INVALIDARG;
573 }
574
d50d0b82
TL
575 /* Once we have the entire message, and we've validated
576 it as best we can here, pass it to the parent. */
9f3da356
TL
577 omapi_signal ((omapi_object_t *)link -> state_object,
578 "message", link);
25a69ca1 579 link -> state = dhcp_flink_message_length_wait;
988c1ca7 580 failover_message_dereference (&link -> imsg, MDL);
25a69ca1
TL
581 /* XXX This is dangerous because we could get into a tight
582 XXX loop reading input without servicing any other stuff.
583 XXX There needs to be a way to relinquish control but
584 XXX get it back immediately if there's no other work to
585 XXX do. */
586 if ((omapi_connection_require (c, 2)) == ISC_R_SUCCESS)
587 goto next_message;
d50d0b82
TL
588 break;
589
590 default:
fa9b593d 591 log_fatal("Impossible case at %s:%d.", MDL);
d50d0b82
TL
592 break;
593 }
594 return ISC_R_SUCCESS;
595}
596
597static isc_result_t do_a_failover_option (c, link)
d9eefc5d 598 omapi_object_t *c;
d50d0b82
TL
599 dhcp_failover_link_t *link;
600{
601 u_int16_t option_code;
602 u_int16_t option_len;
d9eefc5d
TL
603 unsigned char *op;
604 unsigned op_size;
605 unsigned op_count;
606 int i;
d50d0b82
TL
607
608 if (link -> imsg_count + 2 > link -> imsg_len) {
609 log_error ("FAILOVER: message overflow at option code.");
610 return ISC_R_PROTOCOLERROR;
611 }
612
613 /* Get option code. */
614 omapi_connection_get_uint16 (c, &option_code);
615 link -> imsg_count += 2;
616
9f3da356
TL
617 if (link -> imsg_count + 2 > link -> imsg_len) {
618 log_error ("FAILOVER: message overflow at length.");
619 return ISC_R_PROTOCOLERROR;
620 }
621
d50d0b82
TL
622 /* Get option length. */
623 omapi_connection_get_uint16 (c, &option_len);
624 link -> imsg_count += 2;
625
626 if (link -> imsg_count + option_len > link -> imsg_len) {
9f3da356 627 log_error ("FAILOVER: message overflow at data.");
d50d0b82
TL
628 return ISC_R_PROTOCOLERROR;
629 }
630
631 /* If it's an unknown code, skip over it. */
632 if (option_code > FTO_MAX) {
02ca1b45
TL
633#if defined (DEBUG_FAILOVER_MESSAGES)
634 log_debug (" option code %d (%s) len %d (not recognized)",
635 option_code,
636 dhcp_failover_option_name (option_code),
637 option_len);
d50d0b82
TL
638#endif
639 omapi_connection_copyout ((unsigned char *)0, c, option_len);
d9eefc5d 640 link -> imsg_count += option_len;
d50d0b82
TL
641 return ISC_R_SUCCESS;
642 }
643
644 /* If it's the digest, do it now. */
645 if (ft_options [option_code].type == FT_DIGEST) {
646 link -> imsg_count += option_len;
647 if (link -> imsg_count != link -> imsg_len) {
648 log_error ("FAILOVER: digest not at end of message");
649 return ISC_R_PROTOCOLERROR;
650 }
02ca1b45 651#if defined (DEBUG_FAILOVER_MESSAGES)
d50d0b82
TL
652 log_debug (" option %s len %d",
653 ft_options [option_code].name, option_len);
654#endif
655 /* For now, just dump it. */
656 omapi_connection_copyout ((unsigned char *)0, c, option_len);
657 return ISC_R_SUCCESS;
658 }
659
660 /* Only accept an option once. */
d9eefc5d 661 if (link -> imsg -> options_present & ft_options [option_code].bit) {
d50d0b82
TL
662 log_error ("FAILOVER: duplicate option %s",
663 ft_options [option_code].name);
664 return ISC_R_PROTOCOLERROR;
665 }
666
667 /* Make sure the option is appropriate for this type of message.
668 Really, any option is generally allowed for any message, and the
669 cases where this is not true are too complicated to represent in
670 this way - what this code is doing is to just avoid saving the
671 value of an option we don't have any way to use, which allows
672 us to make the failover_message structure smaller. */
673 if (ft_options [option_code].bit &&
9f3da356
TL
674 !(fto_allowed [link -> imsg -> type] &
675 ft_options [option_code].bit)) {
d50d0b82 676 omapi_connection_copyout ((unsigned char *)0, c, option_len);
d9eefc5d 677 link -> imsg_count += option_len;
d50d0b82
TL
678 return ISC_R_SUCCESS;
679 }
680
681 /* Figure out how many elements, how big they are, and where
682 to store them. */
683 if (ft_options [option_code].num_present) {
684 /* If this option takes a fixed number of elements,
685 we expect the space for them to be preallocated,
686 and we can just read the data in. */
687
9f3da356 688 op = ((unsigned char *)link -> imsg) +
d9eefc5d 689 ft_options [option_code].offset;
d50d0b82
TL
690 op_size = ft_sizes [ft_options [option_code].type];
691 op_count = ft_options [option_code].num_present;
692
d9eefc5d 693 if (option_len != op_size * op_count) {
d50d0b82 694 log_error ("FAILOVER: option size (%d:%d), option %s",
d9eefc5d 695 option_len,
d50d0b82
TL
696 (ft_sizes [ft_options [option_code].type] *
697 ft_options [option_code].num_present),
698 ft_options [option_code].name);
699 return ISC_R_PROTOCOLERROR;
700 }
701 } else {
d9eefc5d 702 failover_option_t *fo;
d50d0b82
TL
703
704 /* FT_DDNS* are special - one or two bytes of status
705 followed by the client FQDN. */
706 if (ft_options [option_code].type == FT_DDNS1 ||
707 ft_options [option_code].type == FT_DDNS1) {
d9eefc5d
TL
708 ddns_fqdn_t *ddns =
709 ((ddns_fqdn_t *)
007e3ee4 710 (((char *)link -> imsg) +
d50d0b82
TL
711 ft_options [option_code].offset));
712
713 op_count = (ft_options [option_code].type == FT_DDNS1
714 ? 1 : 2);
715
716 omapi_connection_copyout (&ddns -> codes [0],
717 c, op_count);
9f3da356 718 link -> imsg_count += op_count;
d50d0b82
TL
719 if (op_count == 1)
720 ddns -> codes [1] = 0;
721 op_size = 1;
d9eefc5d 722 op_count = option_len - op_count;
d50d0b82
TL
723
724 ddns -> length = op_count;
4bd8800e 725 ddns -> data = dmalloc (op_count, MDL);
d50d0b82
TL
726 if (!ddns -> data) {
727 log_error ("FAILOVER: no memory getting%s(%d)",
728 " DNS data ", op_count);
729
730 /* Actually, NO_MEMORY, but if we lose here
731 we have to drop the connection. */
732 return ISC_R_PROTOCOLERROR;
733 }
734 omapi_connection_copyout (ddns -> data, c, op_count);
735 goto out;
736 }
d9eefc5d 737
d50d0b82
TL
738 /* A zero for num_present means that any number of
739 elements can appear, so we have to figure out how
740 many we got from the length of the option, and then
741 fill out a failover_option structure describing the
742 data. */
743 op_size = ft_sizes [ft_options [option_code].type];
744
745 /* Make sure that option data length is a multiple of the
746 size of the data type being sent. */
d9eefc5d
TL
747 if (op_size > 1 && option_len % op_size) {
748 log_error ("FAILOVER: option_len %d not %s%d",
749 option_len, "multiple of ", op_size);
d50d0b82
TL
750 return ISC_R_PROTOCOLERROR;
751 }
752
d9eefc5d 753 op_count = option_len / op_size;
d50d0b82 754
d9eefc5d 755 fo = ((failover_option_t *)
007e3ee4 756 (((char *)link -> imsg) +
d50d0b82
TL
757 ft_options [option_code].offset));
758
759 fo -> count = op_count;
4bd8800e 760 fo -> data = dmalloc (option_len, MDL);
d50d0b82
TL
761 if (!fo -> data) {
762 log_error ("FAILOVER: no memory getting %s (%d)",
763 "option data", op_count);
764
765 return ISC_R_PROTOCOLERROR;
766 }
767 op = fo -> data;
768 }
769
770 /* For single-byte message values and multi-byte values that
771 don't need swapping, just read them in all at once. */
772 if (op_size == 1 || ft_options [option_code].type == FT_IPADDR) {
773 omapi_connection_copyout ((unsigned char *)op, c, option_len);
9f3da356 774 link -> imsg_count += option_len;
d50d0b82
TL
775 goto out;
776 }
777
778 /* For values that require swapping, read them in one at a time
779 using routines that swap bytes. */
780 for (i = 0; i < op_count; i++) {
781 switch (ft_options [option_code].type) {
782 case FT_UINT32:
d9eefc5d 783 omapi_connection_get_uint32 (c, (u_int32_t *)op);
d50d0b82 784 op += 4;
9f3da356 785 link -> imsg_count += 4;
d50d0b82
TL
786 break;
787
788 case FT_UINT16:
d9eefc5d 789 omapi_connection_get_uint16 (c, (u_int16_t *)op);
d50d0b82 790 op += 2;
9f3da356 791 link -> imsg_count += 2;
d50d0b82
TL
792 break;
793
794 default:
795 /* Everything else should have been handled
796 already. */
797 log_error ("FAILOVER: option %s: bad type %d",
798 ft_options [option_code].name,
799 ft_options [option_code].type);
800 return ISC_R_PROTOCOLERROR;
801 }
802 }
803 out:
804 /* Remember that we got this option. */
d9eefc5d 805 link -> imsg -> options_present |= ft_options [option_code].bit;
d50d0b82
TL
806 return ISC_R_SUCCESS;
807}
808
809isc_result_t dhcp_failover_link_set_value (omapi_object_t *h,
810 omapi_object_t *id,
811 omapi_data_string_t *name,
812 omapi_typed_data_t *value)
813{
814 if (h -> type != omapi_type_protocol)
815 return ISC_R_INVALIDARG;
816
817 /* Never valid to set these. */
d9eefc5d 818 if (!omapi_ds_strcmp (name, "link-port") ||
d50d0b82
TL
819 !omapi_ds_strcmp (name, "link-name") ||
820 !omapi_ds_strcmp (name, "link-state"))
821 return ISC_R_NOPERM;
822
823 if (h -> inner && h -> inner -> type -> set_value)
824 return (*(h -> inner -> type -> set_value))
825 (h -> inner, id, name, value);
826 return ISC_R_NOTFOUND;
827}
828
829isc_result_t dhcp_failover_link_get_value (omapi_object_t *h,
830 omapi_object_t *id,
831 omapi_data_string_t *name,
832 omapi_value_t **value)
833{
834 dhcp_failover_link_t *link;
835
836 if (h -> type != omapi_type_protocol)
837 return ISC_R_INVALIDARG;
838 link = (dhcp_failover_link_t *)h;
839
d9eefc5d
TL
840 if (!omapi_ds_strcmp (name, "link-port")) {
841 return omapi_make_int_value (value, name,
4bd8800e 842 (int)link -> peer_port, MDL);
d50d0b82
TL
843 } else if (!omapi_ds_strcmp (name, "link-state")) {
844 if (link -> state < 0 ||
d9eefc5d 845 link -> state >= dhcp_flink_state_max)
4bd8800e
TL
846 return omapi_make_string_value (value, name,
847 "invalid link state",
848 MDL);
d50d0b82
TL
849 return omapi_make_string_value
850 (value, name,
4bd8800e 851 dhcp_flink_state_names [link -> state], MDL);
d50d0b82
TL
852 }
853
854 if (h -> inner && h -> inner -> type -> get_value)
855 return (*(h -> inner -> type -> get_value))
856 (h -> inner, id, name, value);
857 return ISC_R_NOTFOUND;
858}
859
4bd8800e
TL
860isc_result_t dhcp_failover_link_destroy (omapi_object_t *h,
861 const char *file, int line)
d50d0b82 862{
d9eefc5d 863 dhcp_failover_link_t *link;
d50d0b82
TL
864 if (h -> type != dhcp_type_failover_link)
865 return ISC_R_INVALIDARG;
d9eefc5d 866 link = (dhcp_failover_link_t *)h;
d758ad8c
TL
867
868 if (link -> peer_address)
869 option_cache_dereference (&link -> peer_address, file, line);
870 if (link -> imsg)
871 failover_message_dereference (&link -> imsg, file, line);
85a53735 872 if (link -> state_object)
d758ad8c
TL
873 dhcp_failover_state_dereference (&link -> state_object,
874 file, line);
d50d0b82
TL
875 return ISC_R_SUCCESS;
876}
877
878/* Write all the published values associated with the object through the
879 specified connection. */
880
881isc_result_t dhcp_failover_link_stuff_values (omapi_object_t *c,
882 omapi_object_t *id,
d9eefc5d 883 omapi_object_t *l)
d50d0b82
TL
884{
885 dhcp_failover_link_t *link;
d9eefc5d 886 isc_result_t status;
d50d0b82 887
d9eefc5d 888 if (l -> type != dhcp_type_failover_link)
d50d0b82 889 return ISC_R_INVALIDARG;
d9eefc5d 890 link = (dhcp_failover_link_t *)l;
d50d0b82 891
d9eefc5d 892 status = omapi_connection_put_name (c, "link-port");
d50d0b82
TL
893 if (status != ISC_R_SUCCESS)
894 return status;
d9eefc5d 895 status = omapi_connection_put_uint32 (c, sizeof (int));
d50d0b82
TL
896 if (status != ISC_R_SUCCESS)
897 return status;
d9eefc5d 898 status = omapi_connection_put_uint32 (c, link -> peer_port);
d50d0b82
TL
899 if (status != ISC_R_SUCCESS)
900 return status;
901
d50d0b82
TL
902 status = omapi_connection_put_name (c, "link-state");
903 if (status != ISC_R_SUCCESS)
904 return status;
905 if (link -> state < 0 ||
d9eefc5d 906 link -> state >= dhcp_flink_state_max)
d50d0b82
TL
907 status = omapi_connection_put_string (c, "invalid link state");
908 else
909 status = (omapi_connection_put_string
d8a417b2 910 (c, dhcp_flink_state_names [link -> state]));
d50d0b82
TL
911 if (status != ISC_R_SUCCESS)
912 return status;
913
d9eefc5d
TL
914 if (link -> inner && link -> inner -> type -> stuff_values)
915 return (*(link -> inner -> type -> stuff_values)) (c, id,
916 link -> inner);
d50d0b82
TL
917 return ISC_R_SUCCESS;
918}
919
920/* Set up a listener for the omapi protocol. The handle stored points to
921 a listener object, not a protocol object. */
922
d9eefc5d 923isc_result_t dhcp_failover_listen (omapi_object_t *h)
d50d0b82
TL
924{
925 isc_result_t status;
02ca1b45 926 dhcp_failover_listener_t *obj, *l;
d50d0b82 927 omapi_value_t *value = (omapi_value_t *)0;
85a53735
TL
928 omapi_addr_t local_addr;
929 unsigned long port;
d50d0b82
TL
930
931 status = omapi_get_value_str (h, (omapi_object_t *)0,
932 "local-port", &value);
933 if (status != ISC_R_SUCCESS)
934 return status;
d9eefc5d 935 if (!value -> value) {
4bd8800e 936 omapi_value_dereference (&value, MDL);
d9eefc5d
TL
937 return ISC_R_INVALIDARG;
938 }
d50d0b82 939
d9eefc5d 940 status = omapi_get_int_value (&port, value -> value);
4bd8800e 941 omapi_value_dereference (&value, MDL);
d50d0b82
TL
942 if (status != ISC_R_SUCCESS)
943 return status;
85a53735
TL
944 local_addr.port = port;
945
946 status = omapi_get_value_str (h, (omapi_object_t *)0,
947 "local-address", &value);
948 if (status != ISC_R_SUCCESS)
949 return status;
950 if (!value -> value) {
951 nogood:
952 omapi_value_dereference (&value, MDL);
953 return ISC_R_INVALIDARG;
954 }
955
956 if (value -> value -> type != omapi_datatype_data ||
957 value -> value -> u.buffer.len != sizeof (struct in_addr))
958 goto nogood;
959
960 memcpy (local_addr.address, value -> value -> u.buffer.value,
961 value -> value -> u.buffer.len);
962 local_addr.addrlen = value -> value -> u.buffer.len;
963 local_addr.addrtype = AF_INET;
964
965 omapi_value_dereference (&value, MDL);
d50d0b82 966
02ca1b45
TL
967 /* Are we already listening on this port and address? */
968 for (l = failover_listeners; l; l = l -> next) {
969 if (l -> address.port == local_addr.port &&
970 l -> address.addrtype == local_addr.addrtype &&
971 l -> address.addrlen == local_addr.addrlen &&
972 !memcmp (l -> address.address, local_addr.address,
973 local_addr.addrlen))
974 break;
975 }
976 /* Already listening. */
977 if (l)
978 return ISC_R_SUCCESS;
979
20916cae
TL
980 obj = (dhcp_failover_listener_t *)0;
981 status = dhcp_failover_listener_allocate (&obj, MDL);
982 if (status != ISC_R_SUCCESS)
983 return status;
02ca1b45 984 obj -> address = local_addr;
d50d0b82 985
02ca1b45 986 status = omapi_listen_addr ((omapi_object_t *)obj, &obj -> address, 1);
d50d0b82
TL
987 if (status != ISC_R_SUCCESS)
988 return status;
989
4bd8800e
TL
990 status = omapi_object_reference (&h -> outer,
991 (omapi_object_t *)obj, MDL);
d50d0b82 992 if (status != ISC_R_SUCCESS) {
20916cae 993 dhcp_failover_listener_dereference (&obj, MDL);
d50d0b82
TL
994 return status;
995 }
4bd8800e 996 status = omapi_object_reference (&obj -> inner, h, MDL);
d50d0b82 997 if (status != ISC_R_SUCCESS) {
20916cae 998 dhcp_failover_listener_dereference (&obj, MDL);
d50d0b82
TL
999 return status;
1000 }
1001
02ca1b45 1002 /* Put this listener on the list. */
d2ff2ec2
TL
1003 if (failover_listeners) {
1004 dhcp_failover_listener_reference (&obj -> next,
1005 failover_listeners, MDL);
1006 dhcp_failover_listener_dereference (&failover_listeners, MDL);
1007 }
02ca1b45
TL
1008 dhcp_failover_listener_reference (&failover_listeners, obj, MDL);
1009
20916cae 1010 return dhcp_failover_listener_dereference (&obj, MDL);
d50d0b82
TL
1011}
1012
1013/* Signal handler for protocol listener - if we get a connect signal,
1014 create a new protocol connection, otherwise pass the signal down. */
1015
1016isc_result_t dhcp_failover_listener_signal (omapi_object_t *o,
1017 const char *name, va_list ap)
1018{
1019 isc_result_t status;
1020 omapi_connection_object_t *c;
d9eefc5d
TL
1021 dhcp_failover_link_t *obj;
1022 dhcp_failover_listener_t *p;
85a53735 1023 dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
d50d0b82
TL
1024
1025 if (!o || o -> type != dhcp_type_failover_listener)
1026 return ISC_R_INVALIDARG;
d9eefc5d 1027 p = (dhcp_failover_listener_t *)o;
d50d0b82
TL
1028
1029 /* Not a signal we recognize? */
1030 if (strcmp (name, "connect")) {
1031 if (p -> inner && p -> inner -> type -> signal_handler)
1032 return (*(p -> inner -> type -> signal_handler))
1033 (p -> inner, name, ap);
1034 return ISC_R_NOTFOUND;
1035 }
1036
1037 c = va_arg (ap, omapi_connection_object_t *);
1038 if (!c || c -> type != omapi_type_connection)
1039 return ISC_R_INVALIDARG;
1040
02ca1b45
TL
1041 /* See if we can find a failover_state object that
1042 matches this connection. */
1043 for (s = failover_states; s; s = s -> next) {
1044 if (dhcp_failover_state_match
1045 (s, (u_int8_t *)&c -> remote_addr.sin_addr,
d758ad8c 1046 sizeof c -> remote_addr.sin_addr)) {
02ca1b45 1047 state = s;
d758ad8c
TL
1048 break;
1049 }
02ca1b45
TL
1050 }
1051 if (!state) {
1052 log_info ("failover: listener: no matching state");
1053 return omapi_disconnect ((omapi_object_t *)c, 1);
1054 }
1055
20916cae
TL
1056 obj = (dhcp_failover_link_t *)0;
1057 status = dhcp_failover_link_allocate (&obj, MDL);
1058 if (status != ISC_R_SUCCESS)
1059 return status;
d9eefc5d 1060 obj -> peer_port = ntohs (c -> remote_addr.sin_port);
d50d0b82 1061
4bd8800e
TL
1062 status = omapi_object_reference (&obj -> outer,
1063 (omapi_object_t *)c, MDL);
d50d0b82
TL
1064 if (status != ISC_R_SUCCESS) {
1065 lose:
20916cae 1066 dhcp_failover_link_dereference (&obj, MDL);
02ca1b45 1067 log_info ("failover: listener: picayune failure.");
d9eefc5d 1068 omapi_disconnect ((omapi_object_t *)c, 1);
d50d0b82
TL
1069 return status;
1070 }
1071
4bd8800e
TL
1072 status = omapi_object_reference (&c -> inner,
1073 (omapi_object_t *)obj, MDL);
d50d0b82
TL
1074 if (status != ISC_R_SUCCESS)
1075 goto lose;
1076
02ca1b45
TL
1077 status = dhcp_failover_state_reference (&obj -> state_object,
1078 state, MDL);
1079 if (status != ISC_R_SUCCESS)
1080 goto lose;
1081
1082 omapi_signal_in ((omapi_object_t *)obj, "connect");
1083
20916cae 1084 return dhcp_failover_link_dereference (&obj, MDL);
d50d0b82
TL
1085}
1086
1087isc_result_t dhcp_failover_listener_set_value (omapi_object_t *h,
1088 omapi_object_t *id,
1089 omapi_data_string_t *name,
1090 omapi_typed_data_t *value)
1091{
1092 if (h -> type != dhcp_type_failover_listener)
1093 return ISC_R_INVALIDARG;
1094
1095 if (h -> inner && h -> inner -> type -> set_value)
1096 return (*(h -> inner -> type -> set_value))
1097 (h -> inner, id, name, value);
1098 return ISC_R_NOTFOUND;
1099}
1100
1101isc_result_t dhcp_failover_listener_get_value (omapi_object_t *h,
1102 omapi_object_t *id,
1103 omapi_data_string_t *name,
1104 omapi_value_t **value)
1105{
1106 if (h -> type != dhcp_type_failover_listener)
1107 return ISC_R_INVALIDARG;
1108
1109 if (h -> inner && h -> inner -> type -> get_value)
1110 return (*(h -> inner -> type -> get_value))
1111 (h -> inner, id, name, value);
1112 return ISC_R_NOTFOUND;
1113}
1114
1115isc_result_t dhcp_failover_listener_destroy (omapi_object_t *h,
4bd8800e 1116 const char *file, int line)
d50d0b82 1117{
d758ad8c
TL
1118 dhcp_failover_listener_t *l;
1119
d50d0b82
TL
1120 if (h -> type != dhcp_type_failover_listener)
1121 return ISC_R_INVALIDARG;
d758ad8c
TL
1122 l = (dhcp_failover_listener_t *)h;
1123 if (l -> next)
1124 dhcp_failover_listener_dereference (&l -> next, file, line);
1125
d50d0b82
TL
1126 return ISC_R_SUCCESS;
1127}
1128
1129/* Write all the published values associated with the object through the
1130 specified connection. */
1131
1132isc_result_t dhcp_failover_listener_stuff (omapi_object_t *c,
1133 omapi_object_t *id,
1134 omapi_object_t *p)
1135{
d50d0b82
TL
1136 if (p -> type != dhcp_type_failover_listener)
1137 return ISC_R_INVALIDARG;
1138
1139 if (p -> inner && p -> inner -> type -> stuff_values)
1140 return (*(p -> inner -> type -> stuff_values)) (c, id,
1141 p -> inner);
1142 return ISC_R_SUCCESS;
1143}
1144
1145/* Set up master state machine for the failover protocol. */
1146
d9eefc5d 1147isc_result_t dhcp_failover_register (omapi_object_t *h)
d50d0b82
TL
1148{
1149 isc_result_t status;
d9eefc5d 1150 dhcp_failover_state_t *obj;
d50d0b82
TL
1151 unsigned long port;
1152 omapi_value_t *value = (omapi_value_t *)0;
1153
1154 status = omapi_get_value_str (h, (omapi_object_t *)0,
1155 "local-port", &value);
1156 if (status != ISC_R_SUCCESS)
1157 return status;
d9eefc5d 1158 if (!value -> value) {
4bd8800e 1159 omapi_value_dereference (&value, MDL);
d9eefc5d
TL
1160 return ISC_R_INVALIDARG;
1161 }
d50d0b82 1162
d9eefc5d 1163 status = omapi_get_int_value (&port, value -> value);
4bd8800e 1164 omapi_value_dereference (&value, MDL);
d50d0b82
TL
1165 if (status != ISC_R_SUCCESS)
1166 return status;
1167
20916cae
TL
1168 obj = (dhcp_failover_state_t *)0;
1169 dhcp_failover_state_allocate (&obj, MDL);
02ca1b45 1170 obj -> me.port = port;
d50d0b82
TL
1171
1172 status = omapi_listen ((omapi_object_t *)obj, port, 1);
20916cae
TL
1173 if (status != ISC_R_SUCCESS) {
1174 dhcp_failover_state_dereference (&obj, MDL);
d50d0b82 1175 return status;
20916cae 1176 }
d50d0b82
TL
1177
1178 status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj,
4bd8800e 1179 MDL);
d50d0b82 1180 if (status != ISC_R_SUCCESS) {
20916cae 1181 dhcp_failover_state_dereference (&obj, MDL);
d50d0b82
TL
1182 return status;
1183 }
4bd8800e 1184 status = omapi_object_reference (&obj -> inner, h, MDL);
20916cae 1185 dhcp_failover_state_dereference (&obj, MDL);
d50d0b82
TL
1186 return status;
1187}
1188
1189/* Signal handler for protocol state machine. */
1190
1191isc_result_t dhcp_failover_state_signal (omapi_object_t *o,
1192 const char *name, va_list ap)
1193{
1194 isc_result_t status;
d9eefc5d 1195 dhcp_failover_state_t *state;
9f3da356 1196 dhcp_failover_link_t *link;
d50d0b82
TL
1197
1198 if (!o || o -> type != dhcp_type_failover_state)
1199 return ISC_R_INVALIDARG;
d9eefc5d 1200 state = (dhcp_failover_state_t *)o;
d50d0b82
TL
1201
1202 /* Not a signal we recognize? */
9f3da356 1203 if (strcmp (name, "disconnect") &&
d50d0b82 1204 strcmp (name, "message")) {
d9eefc5d
TL
1205 if (state -> inner && state -> inner -> type -> signal_handler)
1206 return (*(state -> inner -> type -> signal_handler))
1207 (state -> inner, name, ap);
d50d0b82
TL
1208 return ISC_R_NOTFOUND;
1209 }
1210
85a53735
TL
1211 /* Handle connect signals by seeing what state we're in
1212 and potentially doing a state transition. */
9f3da356
TL
1213 if (!strcmp (name, "disconnect")) {
1214 link = va_arg (ap, dhcp_failover_link_t *);
1215
20916cae 1216 dhcp_failover_link_dereference (&state -> link_to_peer, MDL);
9f3da356 1217 dhcp_failover_state_transition (state, "disconnect");
0db87765
TL
1218 if (state -> i_am == primary) {
1219#if defined (DEBUG_FAILOVER_TIMING)
1220 log_info ("add_timeout +90 %s",
1221 "dhcp_failover_reconnect");
1222#endif
9f3da356 1223 add_timeout (cur_time + 90, dhcp_failover_reconnect,
20916cae
TL
1224 state,
1225 (tvref_t)dhcp_failover_state_reference,
1226 (tvunref_t)
1227 dhcp_failover_state_dereference);
0db87765 1228 }
9f3da356
TL
1229 } else if (!strcmp (name, "message")) {
1230 link = va_arg (ap, dhcp_failover_link_t *);
1231
1232 if (link -> imsg -> type == FTM_CONNECT) {
1233 /* If we already have a link to the peer, it must be
1234 dead, so drop it.
02ca1b45
TL
1235 XXX Is this the right thing to do?
1236 XXX Probably not - what if both peers start at
1237 XXX the same time? */
9f3da356 1238 if (state -> link_to_peer) {
02ca1b45
TL
1239 dhcp_failover_send_connectack
1240 ((omapi_object_t *)link, state,
1241 FTR_DUP_CONNECTION,
1242 "already connected");
1243 omapi_disconnect (link -> outer, 1);
1244 return ISC_R_SUCCESS;
9f3da356 1245 }
02ca1b45
TL
1246 if (!(link -> imsg -> options_present & FTB_MCLT)) {
1247 dhcp_failover_send_connectack
1248 ((omapi_object_t *)link, state,
1249 FTR_INVALID_MCLT,
1250 "no MCLT provided");
1251 omapi_disconnect (link -> outer, 1);
1252 return ISC_R_SUCCESS;
1253 }
1254
20916cae
TL
1255 dhcp_failover_link_reference (&state -> link_to_peer,
1256 link, MDL);
9f3da356 1257 status = (dhcp_failover_send_connectack
007e3ee4 1258 ((omapi_object_t *)link, state, 0, 0));
9f3da356 1259 if (status != ISC_R_SUCCESS) {
20916cae 1260 dhcp_failover_link_dereference
9f3da356 1261 (&state -> link_to_peer, MDL);
02ca1b45
TL
1262 log_info ("dhcp_failover_send_connectack: %s",
1263 isc_result_totext (status));
9f3da356
TL
1264 omapi_disconnect (link -> outer, 1);
1265 return ISC_R_SUCCESS;
1266 }
02ca1b45
TL
1267 if (link -> imsg -> options_present & FTB_MAX_UNACKED)
1268 state -> partner.max_flying_updates =
1269 link -> imsg -> max_unacked;
a55ccdd0 1270 if (link -> imsg -> options_present & FTB_RECEIVE_TIMER)
02ca1b45
TL
1271 state -> partner.max_response_delay =
1272 link -> imsg -> receive_timer;
1273 state -> mclt = link -> imsg -> mclt;
1274 dhcp_failover_send_state (state);
1275 cancel_timeout (dhcp_failover_link_startup_timeout,
1276 link);
9f3da356 1277 } else if (link -> imsg -> type == FTM_CONNECTACK) {
007e3ee4 1278 const char *errmsg;
fa9b593d 1279 char errbuf[1024];
007e3ee4 1280 int reason;
da292833
TL
1281
1282 cancel_timeout (dhcp_failover_link_startup_timeout,
1283 link);
1284
a55ccdd0
DH
1285 if (!(link->imsg->options_present &
1286 FTB_RELATIONSHIP_NAME)) {
1287 errmsg = "missing relationship-name";
1288 reason = FTR_INVALID_PARTNER;
1289 goto badconnectack;
1290 }
1291
1292 if (link->imsg->options_present & FTB_REJECT_REASON) {
1293 /* XXX: add message option to text output. */
1294 log_error ("Failover CONNECT to %s rejected: %s",
1295 state ? state->name : "unknown",
9f3da356
TL
1296 (dhcp_failover_reject_reason_print
1297 (link -> imsg -> reject_reason)));
007e3ee4 1298 /* XXX print message from peer if peer sent message. */
9f3da356
TL
1299 omapi_disconnect (link -> outer, 1);
1300 return ISC_R_SUCCESS;
1301 }
fa9b593d 1302
a55ccdd0
DH
1303 if (!dhcp_failover_state_match_by_name(state,
1304 &link->imsg->relationship_name)) {
fa9b593d
DH
1305 /* XXX: Overflow results in log truncation, safe. */
1306 snprintf(errbuf, sizeof(errbuf), "remote failover "
1307 "relationship name %.*s does not match",
d7ec175b 1308 (int)link->imsg->relationship_name.count,
fa9b593d
DH
1309 link->imsg->relationship_name.data);
1310 errmsg = errbuf;
007e3ee4
TL
1311 reason = FTR_INVALID_PARTNER;
1312 badconnectack:
fa9b593d
DH
1313 log_error("Failover CONNECTACK from %s: %s",
1314 state->name, errmsg);
9f3da356 1315 dhcp_failover_send_disconnect ((omapi_object_t *)link,
007e3ee4 1316 reason, errmsg);
9f3da356 1317 omapi_disconnect (link -> outer, 0);
6f0b9ed0 1318 return ISC_R_SUCCESS;
9f3da356 1319 }
25a69ca1
TL
1320
1321 if (state -> link_to_peer) {
007e3ee4
TL
1322 errmsg = "already connected";
1323 reason = FTR_DUP_CONNECTION;
1324 goto badconnectack;
25a69ca1 1325 }
007e3ee4 1326
d758ad8c
TL
1327 if ((cur_time > link -> imsg -> time &&
1328 cur_time - link -> imsg -> time > 60) ||
1329 (cur_time < link -> imsg -> time &&
1330 link -> imsg -> time - cur_time > 60)) {
1331 errmsg = "time offset too large";
1332 reason = FTR_TIMEMISMATCH;
1333 goto badconnectack;
1334 }
1335
20916cae
TL
1336 dhcp_failover_link_reference (&state -> link_to_peer,
1337 link, MDL);
02ca1b45 1338#if 0
fd273e02
TL
1339 /* XXX This is probably the right thing to do, but
1340 XXX for release three, to make the smallest possible
1341 XXX change, we are doing this when the peer state
1342 XXX changes instead. */
1343 if (state -> me.state == startup)
1344 dhcp_failover_set_state (state,
1345 state -> saved_state);
1346 else
02ca1b45 1347#endif
fd273e02
TL
1348 dhcp_failover_send_state (state);
1349
02ca1b45
TL
1350 if (link -> imsg -> options_present & FTB_MAX_UNACKED)
1351 state -> partner.max_flying_updates =
1352 link -> imsg -> max_unacked;
1353 if (link -> imsg -> options_present & FTB_RECEIVE_TIMER)
1354 state -> partner.max_response_delay =
1355 link -> imsg -> receive_timer;
0db87765
TL
1356#if defined (DEBUG_FAILOVER_TIMING)
1357 log_info ("add_timeout +%d %s",
1358 (int)state -> partner.max_response_delay / 3,
1359 "dhcp_failover_send_contact");
1360#endif
6f0b9ed0 1361 add_timeout (cur_time +
02ca1b45 1362 (int)state -> partner.max_response_delay / 3,
6f0b9ed0
TL
1363 dhcp_failover_send_contact, state,
1364 (tvref_t)dhcp_failover_state_reference,
1365 (tvunref_t)dhcp_failover_state_dereference);
0db87765
TL
1366#if defined (DEBUG_FAILOVER_TIMING)
1367 log_info ("add_timeout +%d %s",
1368 (int)state -> me.max_response_delay,
1369 "dhcp_failover_timeout");
1370#endif
6f0b9ed0 1371 add_timeout (cur_time +
02ca1b45 1372 (int)state -> me.max_response_delay,
6f0b9ed0
TL
1373 dhcp_failover_timeout, state,
1374 (tvref_t)dhcp_failover_state_reference,
1375 (tvunref_t)dhcp_failover_state_dereference);
9f3da356
TL
1376 } else if (link -> imsg -> type == FTM_DISCONNECT) {
1377 if (link -> imsg -> reject_reason) {
a55ccdd0
DH
1378 log_error ("Failover DISCONNECT from %s: %s",
1379 state ? state->name : "unknown",
9f3da356
TL
1380 (dhcp_failover_reject_reason_print
1381 (link -> imsg -> reject_reason)));
1382 }
1383 omapi_disconnect (link -> outer, 1);
9f3da356 1384 } else if (link -> imsg -> type == FTM_BNDUPD) {
8c8e27c5
TL
1385 dhcp_failover_process_bind_update (state,
1386 link -> imsg);
9f3da356 1387 } else if (link -> imsg -> type == FTM_BNDACK) {
8c8e27c5 1388 dhcp_failover_process_bind_ack (state, link -> imsg);
5f0c7be1
TL
1389 } else if (link -> imsg -> type == FTM_UPDREQ) {
1390 dhcp_failover_process_update_request (state,
1391 link -> imsg);
1392 } else if (link -> imsg -> type == FTM_UPDREQALL) {
1393 dhcp_failover_process_update_request_all
1394 (state, link -> imsg);
1395 } else if (link -> imsg -> type == FTM_UPDDONE) {
1396 dhcp_failover_process_update_done (state,
1397 link -> imsg);
8c8e27c5 1398 } else if (link -> imsg -> type == FTM_POOLREQ) {
2426234f 1399 dhcp_failover_pool_reqbalance(state);
8c8e27c5 1400 } else if (link -> imsg -> type == FTM_POOLRESP) {
165bce70
TL
1401 log_info ("pool response: %ld leases",
1402 (unsigned long)
8c8e27c5 1403 link -> imsg -> addresses_transferred);
02ca1b45 1404 } else if (link -> imsg -> type == FTM_STATE) {
5f0c7be1
TL
1405 dhcp_failover_peer_state_changed (state,
1406 link -> imsg);
02ca1b45 1407 }
85a53735 1408
da292833
TL
1409 /* Add a timeout so that if the partner doesn't send
1410 another message for the maximum transmit idle time
1411 plus a grace of one second, we close the
1412 connection. */
1413 if (state -> link_to_peer &&
1414 state -> link_to_peer == link &&
1415 state -> link_to_peer -> state != dhcp_flink_disconnected)
0db87765
TL
1416 {
1417#if defined (DEBUG_FAILOVER_TIMING)
1418 log_info ("add_timeout +%d %s",
1419 (int)state -> me.max_response_delay,
1420 "dhcp_failover_timeout");
1421#endif
da292833
TL
1422 add_timeout (cur_time +
1423 (int)state -> me.max_response_delay,
1424 dhcp_failover_timeout, state,
1425 (tvref_t)dhcp_failover_state_reference,
1426 (tvunref_t)dhcp_failover_state_dereference);
1427
0db87765 1428 }
da292833 1429 }
6f0b9ed0 1430
d9eefc5d
TL
1431 /* Handle all the events we care about... */
1432 return ISC_R_SUCCESS;
d50d0b82
TL
1433}
1434
85a53735
TL
1435isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *state,
1436 const char *name)
1437{
d758ad8c
TL
1438 isc_result_t status;
1439
85a53735
TL
1440 /* XXX Check these state transitions against the spec! */
1441 if (!strcmp (name, "disconnect")) {
e651f9b2 1442 if (state -> link_to_peer) {
02ca1b45 1443 log_info ("peer %s: disconnected", state -> name);
e651f9b2
TL
1444 if (state -> link_to_peer -> state_object)
1445 dhcp_failover_state_dereference
1446 (&state -> link_to_peer -> state_object, MDL);
1447 dhcp_failover_link_dereference (&state -> link_to_peer,
1448 MDL);
1449 }
1450 cancel_timeout (dhcp_failover_send_contact, state);
1451 cancel_timeout (dhcp_failover_timeout, state);
02ca1b45 1452 cancel_timeout (dhcp_failover_startup_timeout, state);
e651f9b2 1453
02ca1b45
TL
1454 switch (state -> me.state == startup ?
1455 state -> saved_state : state -> me.state) {
a55ccdd0
DH
1456 /* In these situations, we remain in the current
1457 * state, or if in startup enter those states.
1458 */
85a53735 1459 case communications_interrupted:
a55ccdd0
DH
1460 case conflict_done:
1461 case partner_down:
1462 case paused:
31bbee78 1463 case recover:
a55ccdd0
DH
1464 case recover_done:
1465 case recover_wait:
1466 case resolution_interrupted:
1467 case shut_down:
85a53735 1468 /* Already in the right state? */
02ca1b45
TL
1469 if (state -> me.state == startup)
1470 return (dhcp_failover_set_state
1471 (state, state -> saved_state));
85a53735
TL
1472 return ISC_R_SUCCESS;
1473
1474 case potential_conflict:
1475 return dhcp_failover_set_state
5f0c7be1 1476 (state, resolution_interrupted);
85a53735 1477
85a53735
TL
1478 case normal:
1479 return dhcp_failover_set_state
1480 (state, communications_interrupted);
1481
9f3da356 1482 case unknown_state:
85a53735 1483 return dhcp_failover_set_state
5f0c7be1 1484 (state, resolution_interrupted);
a55ccdd0
DH
1485
1486 default:
1487 log_fatal("Impossible case at %s:%d.", MDL);
02ca1b45 1488 break; /* can't happen. */
85a53735
TL
1489 }
1490 } else if (!strcmp (name, "connect")) {
02ca1b45 1491 switch (state -> me.state) {
85a53735 1492 case communications_interrupted:
d758ad8c
TL
1493 status = dhcp_failover_set_state (state, normal);
1494 dhcp_failover_send_updates (state);
1495 return status;
9f3da356 1496
5f0c7be1 1497 case resolution_interrupted:
9f3da356
TL
1498 return dhcp_failover_set_state (state,
1499 potential_conflict);
1500
a55ccdd0 1501 case conflict_done:
02ca1b45 1502 case partner_down:
85a53735 1503 case potential_conflict:
9f3da356 1504 case normal:
02ca1b45
TL
1505 case recover:
1506 case shut_down:
1507 case paused:
9f3da356 1508 case unknown_state:
d9539882
TL
1509 case recover_done:
1510 case startup:
fd273e02 1511 case recover_wait:
02ca1b45 1512 return dhcp_failover_send_state (state);
a55ccdd0
DH
1513
1514 default:
1515 log_fatal("Impossible case at %s:%d.", MDL);
1516 break;
9f3da356
TL
1517 }
1518 } else if (!strcmp (name, "startup")) {
02ca1b45
TL
1519 dhcp_failover_set_state (state, startup);
1520 return ISC_R_SUCCESS;
5f0c7be1 1521 } else if (!strcmp (name, "connect-timeout")) {
02ca1b45 1522 switch (state -> me.state) {
9f3da356
TL
1523 case communications_interrupted:
1524 case partner_down:
5f0c7be1 1525 case resolution_interrupted:
a55ccdd0
DH
1526 case paused:
1527 case startup:
1528 case shut_down:
1529 case conflict_done:
9f3da356
TL
1530 return ISC_R_SUCCESS;
1531
85a53735 1532 case normal:
9f3da356 1533 case recover:
a55ccdd0
DH
1534 case recover_wait:
1535 case recover_done:
1536 case unknown_state:
9f3da356
TL
1537 return dhcp_failover_set_state
1538 (state, communications_interrupted);
1539
1540 case potential_conflict:
1541 return dhcp_failover_set_state
5f0c7be1 1542 (state, resolution_interrupted);
9f3da356 1543
9f3da356 1544 default:
a55ccdd0
DH
1545 log_fatal("Impossible case at %s:%d.", MDL);
1546 break;
85a53735 1547 }
85a53735
TL
1548 }
1549 return ISC_R_INVALIDARG;
1550}
1551
5f0c7be1
TL
1552isc_result_t dhcp_failover_set_service_state (dhcp_failover_state_t *state)
1553{
02ca1b45 1554 switch (state -> me.state) {
5f0c7be1
TL
1555 case unknown_state:
1556 state -> service_state = not_responding;
1557 state -> nrr = " (my state unknown)";
1558 break;
1559
1560 case partner_down:
1561 state -> service_state = service_partner_down;
1562 state -> nrr = "";
1563 break;
1564
1565 case normal:
1566 state -> service_state = cooperating;
1567 state -> nrr = "";
1568 break;
1569
1570 case communications_interrupted:
1571 state -> service_state = not_cooperating;
1572 state -> nrr = "";
1573 break;
1574
1575 case resolution_interrupted:
1576 case potential_conflict:
a55ccdd0 1577 case conflict_done:
5f0c7be1
TL
1578 state -> service_state = not_responding;
1579 state -> nrr = " (resolving conflicts)";
1580 break;
1581
1582 case recover:
1583 state -> service_state = not_responding;
1584 state -> nrr = " (recovering)";
1585 break;
1586
1587 case shut_down:
1588 state -> service_state = not_responding;
1589 state -> nrr = " (shut down)";
1590 break;
1591
1592 case paused:
1593 state -> service_state = not_responding;
1594 state -> nrr = " (paused)";
1595 break;
1596
fd273e02
TL
1597 case recover_wait:
1598 state -> service_state = not_responding;
1599 state -> nrr = " (recover wait)";
1600 break;
1601
5f0c7be1
TL
1602 case recover_done:
1603 state -> service_state = not_responding;
1604 state -> nrr = " (recover done)";
1605 break;
02ca1b45
TL
1606
1607 case startup:
1608 state -> service_state = service_startup;
1609 state -> nrr = " (startup)";
1610 break;
a55ccdd0
DH
1611
1612 default:
1613 log_fatal("Impossible case at %s:%d.\n", MDL);
1614 break;
5f0c7be1
TL
1615 }
1616
1617 /* Some peer states can require us not to respond, even if our
1618 state doesn't. */
1619 /* XXX hm. I suspect this isn't true anymore. */
1620 if (state -> service_state != not_responding) {
02ca1b45 1621 switch (state -> partner.state) {
5f0c7be1 1622 case partner_down:
5f0c7be1 1623 state -> service_state = not_responding;
a55ccdd0 1624 state -> nrr = " (peer demands: recovering)";
5f0c7be1
TL
1625 break;
1626
1627 case potential_conflict:
a55ccdd0
DH
1628 case conflict_done:
1629 case resolution_interrupted:
5f0c7be1 1630 state -> service_state = not_responding;
a55ccdd0 1631 state -> nrr = " (peer demands: resolving conflicts)";
5f0c7be1
TL
1632 break;
1633
1634 /* Other peer states don't affect our behaviour. */
1635 default:
1636 break;
1637 }
1638 }
1639
1640 return ISC_R_SUCCESS;
1641}
1642
85a53735
TL
1643isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *state,
1644 enum failover_state new_state)
1645{
9f3da356
TL
1646 enum failover_state saved_state;
1647 TIME saved_stos;
31bbee78
TL
1648 struct pool *p;
1649 struct shared_network *s;
1650 struct lease *l;
9f3da356 1651
a55ccdd0
DH
1652 /* If we're in certain states where we're sending updates, and the peer
1653 * state changes, we need to re-schedule any pending updates just to
1654 * be on the safe side. This results in retransmission.
1655 */
02ca1b45 1656 switch (state -> me.state) {
85a53735 1657 case normal:
a55ccdd0
DH
1658 case potential_conflict:
1659 case partner_down:
85a53735 1660 if (state -> ack_queue_tail) {
25a69ca1 1661 struct lease *lp;
fd273e02 1662
25a69ca1
TL
1663 /* Zap the flags. */
1664 for (lp = state -> ack_queue_head; lp; lp = lp -> next_pending)
1665 lp -> flags = ((lp -> flags & ~ON_ACK_QUEUE) |
1666 ON_UPDATE_QUEUE);
a55ccdd0 1667
25a69ca1
TL
1668 /* Now hook the ack queue to the beginning of the update
1669 queue. */
85a53735 1670 if (state -> update_queue_head) {
fd273e02
TL
1671 lease_reference (&state -> ack_queue_tail -> next_pending,
1672 state -> update_queue_head, MDL);
1673 lease_dereference (&state -> update_queue_head, MDL);
85a53735 1674 }
20916cae
TL
1675 lease_reference (&state -> update_queue_head,
1676 state -> ack_queue_head, MDL);
da292833
TL
1677 if (!state -> update_queue_tail) {
1678#if defined (POINTER_DEBUG)
fd273e02
TL
1679 if (state -> ack_queue_tail -> next_pending) {
1680 log_error ("next pending on ack queue tail.");
1681 abort ();
1682 }
da292833 1683#endif
fd273e02
TL
1684 lease_reference (&state -> update_queue_tail,
1685 state -> ack_queue_tail, MDL);
da292833 1686 }
20916cae
TL
1687 lease_dereference (&state -> ack_queue_tail, MDL);
1688 lease_dereference (&state -> ack_queue_head, MDL);
85a53735
TL
1689 state -> cur_unacked_updates = 0;
1690 }
a55ccdd0 1691 /* We will re-queue a timeout later, if applicable. */
85a53735
TL
1692 cancel_timeout (dhcp_failover_keepalive, state);
1693 break;
fd273e02 1694
85a53735
TL
1695 default:
1696 break;
1697 }
1698
9f3da356 1699 /* Tentatively make the transition. */
02ca1b45
TL
1700 saved_state = state -> me.state;
1701 saved_stos = state -> me.stos;
d758ad8c
TL
1702
1703 /* Keep the old stos if we're going into recover_wait or if we're
1704 coming into or out of startup. */
1705 if (new_state != recover_wait && new_state != startup &&
1706 saved_state != startup)
fd273e02 1707 state -> me.stos = cur_time;
d758ad8c
TL
1708
1709 /* If we're in shutdown, peer is in partner_down, and we're moving
1710 to recover, we can skip waiting for MCLT to expire. This happens
1711 when a server is moved administratively into shutdown prior to
1712 actually shutting down. Of course, if there are any updates
1713 pending we can't actually do this. */
1714 if (new_state == recover && saved_state == shut_down &&
1715 state -> partner.state == partner_down &&
1716 !state -> update_queue_head && !state -> ack_queue_head)
1717 state -> me.stos = cur_time - state -> mclt;
1718
837d4342 1719 state -> me.state = new_state;
d758ad8c
TL
1720 if (new_state == startup && saved_state != startup)
1721 state -> saved_state = saved_state;
9f3da356 1722
d758ad8c 1723 /* If we can't record the new state, we can't make a state transition. */
9f3da356 1724 if (!write_failover_state (state) || !commit_leases ()) {
9f3da356
TL
1725 log_error ("Unable to record current failover state for %s",
1726 state -> name);
02ca1b45
TL
1727 state -> me.state = saved_state;
1728 state -> me.stos = saved_stos;
9f3da356
TL
1729 return ISC_R_IOERROR;
1730 }
1731
5f0c7be1 1732 log_info ("failover peer %s: I move from %s to %s",
9f3da356 1733 state -> name, dhcp_failover_state_name_print (saved_state),
02ca1b45 1734 dhcp_failover_state_name_print (state -> me.state));
9f3da356 1735
da292833
TL
1736 /* If we were in startup and we just left it, cancel the timeout. */
1737 if (new_state != startup && saved_state == startup)
1738 cancel_timeout (dhcp_failover_startup_timeout, state);
1739
1740 /* Set our service state. */
1741 dhcp_failover_set_service_state (state);
1742
1743 /* Tell the peer about it. */
1744 if (state -> link_to_peer)
1745 dhcp_failover_send_state (state);
1746
fd273e02
TL
1747 switch (new_state) {
1748 case normal:
808d45fd 1749 /* Upon entering normal state, the server is expected to retransmit
fc3b9c90
DH
1750 * all pending binding updates. This is a good opportunity to
1751 * rebalance the pool (potentially making new pending updates),
1752 * which also schedules the next pool rebalance.
808d45fd 1753 */
fc3b9c90 1754 dhcp_failover_pool_balance(state);
808d45fd 1755 dhcp_failover_generate_update_queue(state, 0);
616d67cb
DH
1756
1757 if (state->update_queue_tail != NULL) {
1758 dhcp_failover_send_updates(state);
1759 log_info("Sending updates to %s.", state->name);
1760 }
1761
fd273e02
TL
1762 break;
1763
1764 case potential_conflict:
1765 if (state -> i_am == primary)
1766 dhcp_failover_send_update_request (state);
1767 break;
1768
1769 case startup:
0db87765
TL
1770#if defined (DEBUG_FAILOVER_TIMING)
1771 log_info ("add_timeout +15 %s",
1772 "dhcp_failover_startup_timeout");
1773#endif
fd273e02
TL
1774 add_timeout (cur_time + 15,
1775 dhcp_failover_startup_timeout,
1776 state,
1777 (tvref_t)omapi_object_reference,
1778 (tvunref_t)
1779 omapi_object_dereference);
1780 break;
1781
1782 /* If we come back in recover_wait and there's still waiting
1783 to do, set a timeout. */
1784 case recover_wait:
0db87765
TL
1785 if (state -> me.stos + state -> mclt > cur_time) {
1786#if defined (DEBUG_FAILOVER_TIMING)
1787 log_info ("add_timeout +%d %s",
1788 (int)(cur_time -
1789 state -> me.stos + state -> mclt),
1790 "dhcp_failover_startup_timeout");
1791#endif
fd273e02
TL
1792 add_timeout ((int)(state -> me.stos + state -> mclt),
1793 dhcp_failover_recover_done,
1794 state,
1795 (tvref_t)omapi_object_reference,
1796 (tvunref_t)
1797 omapi_object_dereference);
0db87765 1798 } else
d758ad8c 1799 dhcp_failover_recover_done (state);
fd273e02
TL
1800 break;
1801
01c6e68a 1802 case recover:
a55ccdd0
DH
1803 /* XXX: We're supposed to calculate if updreq or updreqall is
1804 * needed. In theory, we should only have to updreqall if we
1805 * are positive we lost our stable storage.
1806 */
01c6e68a
TL
1807 if (state -> link_to_peer)
1808 dhcp_failover_send_update_request_all (state);
1809 break;
1810
31bbee78
TL
1811 case partner_down:
1812 /* For every expired lease, set a timeout for it to become free. */
88cd8aca
DH
1813 for (s = shared_networks; s; s = s -> next) {
1814 for (p = s -> pools; p; p = p -> next) {
31bbee78 1815 if (p -> failover_peer == state) {
88cd8aca
DH
1816 for (l = p->expired ; l ; l = l->next) {
1817 l->tsfp = state->me.stos + state->mclt;
1818 l->sort_time = (l->tsfp > l->ends) ?
1819 l->tsfp : l->ends;
1820 }
1821 if (p->expired &&
1822 (p->expired->sort_time < p->next_event_time)) {
1823
1824 p->next_event_time = p->expired->sort_time;
0db87765
TL
1825#if defined (DEBUG_FAILOVER_TIMING)
1826 log_info ("add_timeout +%d %s",
88cd8aca 1827 (int)(cur_time - p->next_event_time),
0db87765
TL
1828 "pool_timer");
1829#endif
88cd8aca
DH
1830 add_timeout(p->next_event_time, pool_timer, p,
1831 (tvref_t)pool_reference,
1832 (tvunref_t)pool_dereference);
31bbee78
TL
1833 }
1834 }
1835 }
1836 }
1837 break;
a55ccdd0 1838
31bbee78 1839
fd273e02
TL
1840 default:
1841 break;
85a53735 1842 }
9f3da356 1843
85a53735
TL
1844 return ISC_R_SUCCESS;
1845}
1846
5f0c7be1
TL
1847isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state,
1848 failover_message_t *msg)
1849{
02ca1b45
TL
1850 enum failover_state previous_state = state -> partner.state;
1851 enum failover_state new_state;
1852 int startupp;
5f0c7be1 1853
02ca1b45 1854 new_state = msg -> server_state;
a55ccdd0 1855 startupp = (msg -> server_flags & FTF_SERVER_STARTUP) ? 1 : 0;
5f0c7be1 1856
d07ccee3
TL
1857 if (state -> partner.state == new_state && state -> me.state) {
1858 switch (state -> me.state) {
1859 case startup:
fd273e02 1860 dhcp_failover_set_state (state, state -> saved_state);
d07ccee3
TL
1861 return ISC_R_SUCCESS;
1862
1863 case unknown_state:
1864 case normal:
1865 case potential_conflict:
d07ccee3
TL
1866 case recover_done:
1867 case shut_down:
1868 case paused:
1869 case recover_wait:
1870 return ISC_R_SUCCESS;
1871
1872 /* If we get a peer state change when we're
1873 disconnected, we always process it. */
1874 case partner_down:
1875 case communications_interrupted:
1876 case resolution_interrupted:
98311e4b 1877 case recover:
a55ccdd0
DH
1878 case conflict_done:
1879 break;
1880
1881 default:
1882 log_fatal("Impossible case at %s:%d.", MDL);
d07ccee3
TL
1883 break;
1884 }
fd273e02 1885 }
da292833 1886
02ca1b45 1887 state -> partner.state = new_state;
5f0c7be1
TL
1888
1889 log_info ("failover peer %s: peer moves from %s to %s",
1890 state -> name,
1891 dhcp_failover_state_name_print (previous_state),
02ca1b45 1892 dhcp_failover_state_name_print (state -> partner.state));
5f0c7be1
TL
1893
1894 if (!write_failover_state (state) || !commit_leases ()) {
1895 /* This is bad, but it's not fatal. Of course, if we
1896 can't write to the lease database, we're not going to
1897 get much done anyway. */
1898 log_error ("Unable to record current failover state for %s",
1899 state -> name);
1900 }
1901
a55ccdd0
DH
1902 /* Quickly validate the new state as being one of the 13 known
1903 * states.
1904 */
1905 switch (new_state) {
1906 case unknown_state:
1907 case startup:
1908 case normal:
1909 case communications_interrupted:
1910 case partner_down:
1911 case potential_conflict:
1912 case recover:
1913 case paused:
1914 case shut_down:
1915 case recover_done:
1916 case resolution_interrupted:
1917 case conflict_done:
1918 case recover_wait:
1919 break;
1920
1921 default:
1922 log_error("failover peer %s: Invalid state: %d", state->name,
1923 new_state);
1924 dhcp_failover_set_state(state, shut_down);
1925 return ISC_R_SUCCESS;
1926 }
1927
5f0c7be1
TL
1928 /* Do any state transitions that are required as a result of the
1929 peer's state transition. */
1930
02ca1b45
TL
1931 switch (state -> me.state == startup ?
1932 state -> saved_state : state -> me.state) {
5f0c7be1
TL
1933 case normal:
1934 switch (new_state) {
1935 case normal:
da292833
TL
1936 dhcp_failover_state_pool_check (state);
1937 break;
1938
01c6e68a
TL
1939 case partner_down:
1940 if (state -> me.state == startup)
1941 dhcp_failover_set_state (state, recover);
1942 else
1943 dhcp_failover_set_state (state,
1944 potential_conflict);
1945 break;
1946
5f0c7be1 1947 case potential_conflict:
5f0c7be1 1948 case resolution_interrupted:
a55ccdd0 1949 case conflict_done:
da292833 1950 /* None of these transitions should ever occur. */
a55ccdd0
DH
1951 log_error("Peer %s: Invalid state transition %s "
1952 "to %s.", state->name,
1953 dhcp_failover_state_name_print(previous_state),
1954 dhcp_failover_state_name_print(new_state));
da292833 1955 dhcp_failover_set_state (state, shut_down);
5f0c7be1 1956 break;
da292833 1957
a55ccdd0 1958 case recover:
5f0c7be1 1959 case shut_down:
5f0c7be1 1960 dhcp_failover_set_state (state, partner_down);
812022ff 1961 break;
5f0c7be1
TL
1962
1963 case paused:
1964 dhcp_failover_set_state (state,
1965 communications_interrupted);
1966 break;
1967
a55ccdd0
DH
1968 default:
1969 /* recover_wait, recover_done, unknown_state, startup,
1970 * communications_interrupted
1971 */
5f0c7be1
TL
1972 break;
1973 }
1974 break;
1975
1976 case recover:
1977 switch (new_state) {
1978 case recover:
755acf88
TL
1979 log_info ("failover peer %s: requesting %s",
1980 state -> name, "full update from peer");
01c6e68a
TL
1981 /* Don't send updreqall if we're really in the
1982 startup state, because that will result in two
1983 being sent. */
1984 if (state -> me.state == recover)
1985 dhcp_failover_send_update_request_all (state);
5f0c7be1 1986 break;
da292833 1987
5f0c7be1
TL
1988 case potential_conflict:
1989 case resolution_interrupted:
a55ccdd0 1990 case conflict_done:
5f0c7be1
TL
1991 case normal:
1992 dhcp_failover_set_state (state, potential_conflict);
1993 break;
1994
1995 case partner_down:
1996 case communications_interrupted:
1997 /* We're supposed to send an update request at this
1998 point. */
1999 /* XXX we don't currently have code here to do any
2000 XXX clever detection of when we should send an
2001 XXX UPDREQALL message rather than an UPDREQ
2002 XXX message. What to do, what to do? */
98311e4b
DH
2003 /* Currently when we enter recover state, no matter
2004 * the reason, we send an UPDREQALL. So, it makes
2005 * the most sense to stick to that until something
2006 * better is done.
a55ccdd0 2007 * Furthermore, we only want to send the update
98311e4b
DH
2008 * request if we are not in startup state.
2009 */
2010 if (state -> me.state == recover)
2011 dhcp_failover_send_update_request_all (state);
5f0c7be1
TL
2012 break;
2013
2014 case shut_down:
2015 /* XXX We're not explicitly told what to do in this
2016 XXX case, but this transition is consistent with
2017 XXX what is elsewhere in the draft. */
2018 dhcp_failover_set_state (state, partner_down);
2019 break;
2020
2021 /* We can't really do anything in this case. */
a55ccdd0
DH
2022 default:
2023 /* paused, recover_done, recover_wait, unknown_state,
2024 * startup.
2025 */
5f0c7be1
TL
2026 break;
2027 }
2028 break;
2029
2030 case potential_conflict:
2031 switch (new_state) {
2032 case normal:
a55ccdd0
DH
2033 /* This is an illegal transition. */
2034 log_error("Peer %s moves to normal during conflict "
2035 "resolution - panic, shutting down.",
2036 state->name);
2037 dhcp_failover_set_state(state, shut_down);
2038 break;
2039
2040 case conflict_done:
2041 if (previous_state == potential_conflict)
5f0c7be1 2042 dhcp_failover_send_update_request (state);
a55ccdd0
DH
2043 else
2044 log_error("Peer %s: Unexpected move to "
2045 "conflict-done.", state->name);
5f0c7be1
TL
2046 break;
2047
5f0c7be1 2048 case recover_done:
fd273e02 2049 case recover_wait:
5f0c7be1
TL
2050 case potential_conflict:
2051 case partner_down:
2052 case communications_interrupted:
2053 case resolution_interrupted:
2054 case paused:
2055 break;
2056
da292833
TL
2057 case recover:
2058 dhcp_failover_set_state (state, recover);
2059 break;
2060
5f0c7be1
TL
2061 case shut_down:
2062 dhcp_failover_set_state (state, partner_down);
2063 break;
2064
a55ccdd0
DH
2065 default:
2066 /* unknown_state, startup */
5f0c7be1
TL
2067 break;
2068 }
2069 break;
2070
a55ccdd0
DH
2071 case conflict_done:
2072 switch (new_state) {
2073 case normal:
2074 case shut_down:
2075 dhcp_failover_set_state(state, new_state);
2076 break;
2077
2078 default:
2079 log_fatal("Peer %s: Invalid attempt to move from %s "
2080 "to %s while local state is conflict-done.",
2081 state->name,
2082 dhcp_failover_state_name_print(previous_state),
2083 dhcp_failover_state_name_print(new_state));
2084 }
2085 break;
2086
5f0c7be1
TL
2087 case partner_down:
2088 /* Take no action if other server is starting up. */
2089 if (startupp)
2090 break;
2091
2092 switch (new_state) {
2093 /* This is where we should be. */
2094 case recover:
2036af06 2095 case recover_wait:
da292833
TL
2096 break;
2097
5f0c7be1 2098 case recover_done:
da292833 2099 dhcp_failover_set_state (state, normal);
5f0c7be1
TL
2100 break;
2101
2102 case normal:
2103 case potential_conflict:
2104 case partner_down:
2105 case communications_interrupted:
2106 case resolution_interrupted:
a55ccdd0 2107 case conflict_done:
5f0c7be1
TL
2108 dhcp_failover_set_state (state, potential_conflict);
2109 break;
2110
a55ccdd0
DH
2111 default:
2112 /* shut_down, paused, unknown_state, startup */
5f0c7be1
TL
2113 break;
2114 }
2115 break;
2116
2117 case communications_interrupted:
2118 switch (new_state) {
5f0c7be1
TL
2119 case paused:
2120 /* Stick with the status quo. */
2121 break;
2122
da292833
TL
2123 /* If we're in communications-interrupted and an
2124 amnesiac peer connects, go to the partner_down
2125 state immediately. */
2126 case recover:
2127 dhcp_failover_set_state (state, partner_down);
2128 break;
2129
5f0c7be1
TL
2130 case normal:
2131 case communications_interrupted:
2132 case recover_done:
fd273e02 2133 case recover_wait:
5f0c7be1
TL
2134 /* XXX so we don't need to do this specially in
2135 XXX the CONNECT and CONNECTACK handlers. */
d758ad8c 2136 dhcp_failover_send_updates (state);
5f0c7be1
TL
2137 dhcp_failover_set_state (state, normal);
2138 break;
2139
2140 case potential_conflict:
2141 case partner_down:
2142 case resolution_interrupted:
a55ccdd0 2143 case conflict_done:
5f0c7be1
TL
2144 dhcp_failover_set_state (state, potential_conflict);
2145 break;
2146
2147 case shut_down:
2148 dhcp_failover_set_state (state, partner_down);
2149 break;
2150
a55ccdd0
DH
2151 default:
2152 /* unknown_state, startup */
5f0c7be1
TL
2153 break;
2154 }
2155 break;
2156
2157 case resolution_interrupted:
2158 switch (new_state) {
2159 case normal:
2160 case recover:
2161 case potential_conflict:
2162 case partner_down:
2163 case communications_interrupted:
2164 case resolution_interrupted:
a55ccdd0 2165 case conflict_done:
5f0c7be1 2166 case recover_done:
fd273e02 2167 case recover_wait:
5f0c7be1
TL
2168 dhcp_failover_set_state (state, potential_conflict);
2169 break;
2170
2171 case shut_down:
2172 dhcp_failover_set_state (state, partner_down);
2173 break;
2174
a55ccdd0
DH
2175 default:
2176 /* paused, unknown_state, startup */
5f0c7be1
TL
2177 break;
2178 }
2179 break;
2180
a55ccdd0
DH
2181 /* Make no transitions while in recover_wait...just wait. */
2182 case recover_wait:
2183 break;
2184
5f0c7be1
TL
2185 case recover_done:
2186 switch (new_state) {
da292833 2187 case recover_done:
a55ccdd0
DH
2188 log_error("Both servers have entered recover-done!");
2189 case normal:
5f0c7be1
TL
2190 dhcp_failover_set_state (state, normal);
2191 break;
2192
5f0c7be1
TL
2193 case shut_down:
2194 dhcp_failover_set_state (state, partner_down);
2195 break;
2196
a55ccdd0
DH
2197 default:
2198 /* potential_conflict, partner_down,
2199 * communications_interrupted, resolution_interrupted,
2200 * paused, recover, recover_wait, unknown_state,
2201 * startup.
2202 */
5f0c7be1
TL
2203 break;
2204 }
2205 break;
2206
2207 /* We are essentially dead in the water when we're in
2208 either shut_down or paused states, and do not do any
2209 automatic state transitions. */
2210 case shut_down:
2211 case paused:
2212 break;
2213
a55ccdd0
DH
2214 /* XXX: Shouldn't this be a fatal condition? */
2215 case unknown_state:
2216 break;
2217
2218 default:
2219 log_fatal("Impossible condition at %s:%d.", MDL);
fd273e02
TL
2220 break;
2221
5f0c7be1 2222 }
02ca1b45
TL
2223
2224 /* If we didn't make a transition out of startup as a result of
2225 the peer's state change, do it now as a result of the fact that
2226 we got a state change from the peer. */
2227 if (state -> me.state == startup && state -> saved_state != startup)
2228 dhcp_failover_set_state (state, state -> saved_state);
5f0c7be1
TL
2229
2230 /* For now, just set the service state based on the peer's state
2231 if necessary. */
2232 dhcp_failover_set_service_state (state);
2233
2234 return ISC_R_SUCCESS;
2235}
2236
fc3b9c90
DH
2237/* Balance operation manual entry. */
2238static void
2239dhcp_failover_pool_balance(dhcp_failover_state_t *state)
2240{
2241 /* Cancel pending event. */
2242 cancel_timeout(dhcp_failover_pool_rebalance, state);
2243 state->sched_balance = 0;
2244
2245 dhcp_failover_pool_dobalance(state);
2246}
2247
2248/* Balance operation entry from timer event. */
2249void
2250dhcp_failover_pool_rebalance(void *failover_state)
8c8e27c5 2251{
2426234f
DH
2252 dhcp_failover_state_t *state;
2253
2254 state = (dhcp_failover_state_t *)failover_state;
2255
fc3b9c90
DH
2256 /* Clear scheduled event indicator. */
2257 state->sched_balance = 0;
2258
2426234f
DH
2259 if (dhcp_failover_pool_dobalance(state))
2260 dhcp_failover_send_updates(state);
2261}
2262
fc3b9c90
DH
2263/* Balance operation entry from POOLREQ protocol message. */
2264static void
2265dhcp_failover_pool_reqbalance(dhcp_failover_state_t *state)
2426234f
DH
2266{
2267 int queued;
2268
fc3b9c90
DH
2269 /* Cancel pending event. */
2270 cancel_timeout(dhcp_failover_pool_rebalance, state);
2271 state->sched_balance = 0;
2272
2426234f
DH
2273 queued = dhcp_failover_pool_dobalance(state);
2274
2275 dhcp_failover_send_poolresp(state, queued);
2276
2277 if (queued)
2278 dhcp_failover_send_updates(state);
2279 else
2280 log_info("peer %s: Got POOLREQ, answering negatively! "
2281 "Peer may be out of leases or database inconsistent.",
2282 state->name);
2283}
2284
2285/* Do the meat of the work common to all forms of pool rebalance. */
2286static int dhcp_failover_pool_dobalance(dhcp_failover_state_t *state)
2287{
2288 int lts, total, thresh, hold, pass;
8c8e27c5 2289 int leases_queued = 0;
2426234f 2290 int reqsent = 0;
007e3ee4
TL
2291 struct lease *lp = (struct lease *)0;
2292 struct lease *next = (struct lease *)0;
8c8e27c5
TL
2293 struct shared_network *s;
2294 struct pool *p;
007e3ee4
TL
2295 binding_state_t peer_lease_state;
2296 binding_state_t my_lease_state;
2297 struct lease **lq;
a512cc3a
DH
2298 int (*log_func)(const char *, ...);
2299 const char *result;
8c8e27c5 2300
2426234f 2301 if (state -> me.state != normal)
8c8e27c5
TL
2302 return 0;
2303
2426234f 2304 state->last_balance = cur_time;
2426234f
DH
2305
2306 for (s = shared_networks ; s ; s = s->next) {
2307 for (p = s->pools ; p ; p = p->next) {
2308 if (p->failover_peer != state)
8c8e27c5 2309 continue;
007e3ee4
TL
2310
2311 /* Right now we're giving the peer half of the free leases.
2312 If we have more leases than the peer (i.e., more than
2313 half), then the number of leases we have, less the number
2314 of leases the peer has, will be how many more leases we
2315 have than the peer has. So if we send half that number
2316 to the peer, we should be even. */
2426234f
DH
2317 if (p->failover_peer->i_am == primary) {
2318 lts = (p->free_leases - p->backup_leases) / 2;
007e3ee4
TL
2319 peer_lease_state = FTS_BACKUP;
2320 my_lease_state = FTS_FREE;
2426234f 2321 lq = &p->free;
007e3ee4 2322 } else {
2426234f 2323 lts = (p->backup_leases - p->free_leases) / 2;
007e3ee4
TL
2324 peer_lease_state = FTS_FREE;
2325 my_lease_state = FTS_BACKUP;
2426234f
DH
2326 lq = &p->backup;
2327 }
2328
2426234f
DH
2329 total = p->backup_leases + p->free_leases;
2330
2331 thresh = ((total * state->max_lease_misbalance) + 50) / 100;
2332 hold = ((total * state->max_lease_ownership) + 50) / 100;
2333
a512cc3a
DH
2334 log_info("balancing pool %lx %s total %d free %d "
2335 "backup %d lts %d max-own (+/-)%d",
2336 (unsigned long)p,
2337 (p->shared_network ?
2338 p->shared_network->name : ""), p->lease_count,
2339 p->free_leases, p->backup_leases, lts, hold);
2340
2426234f
DH
2341 /* If lts is in the negatives (we need leases) more than
2342 * negative double the thresh%, panic and send poolreq to
2343 * hopefully wake up the peer.
2344 */
2345 if (!reqsent && (lts < (thresh * -2))) {
2346 dhcp_failover_send_poolreq(state);
2347 reqsent = 1;
007e3ee4 2348 }
8c8e27c5 2349
2426234f
DH
2350 /* In the first pass, try to allocate leases to the
2351 * peer which it would normally be responsible for (if
2352 * the lease has a hardware address or client-identifier,
2353 * and the load-balance-algorithm chooses the peer to
2354 * answer that address), up to a hold% excess in the peer's
2355 * favor. In the second pass, just send the oldest (first
2356 * on the list) leases up to a hold% excess in our favor.
2357 *
2358 * This could make for additional pool rebalance
2359 * events, but preserving MAC possession should be
2360 * worth it.
2361 */
2362 pass = 0;
2363 lease_reference(&lp, *lq, MDL);
007e3ee4 2364
a512cc3a
DH
2365 /* In the case where there are 2 leases, hold is zero, and
2366 * lts is 1 if both leases are on the local server. If
2367 * there is only 1 lease, both lts and hold are zero. Let's
2368 * not play ping pong.
2426234f 2369 */
a512cc3a 2370 while (lp && (lts > (pass ? hold : -hold))) {
007e3ee4 2371 if (next)
2426234f
DH
2372 lease_dereference(&next, MDL);
2373 if (lp->next)
2374 lease_reference(&next, lp->next, MDL);
2375
2376 if (pass || peer_wants_lease(lp)) {
2377 --lts;
2378 ++leases_queued;
2379 lp->next_binding_state = peer_lease_state;
2380 lp->tstp = cur_time;
2381 lp->starts = cur_time;
2382
2383 if (!supersede_lease(lp, NULL, 0, 1, 0) ||
2384 !write_lease(lp))
2385 log_error("can't commit lease %s on "
2386 "giveaway", piaddr(lp->ip_addr));
8c8e27c5 2387 }
007e3ee4 2388
2426234f 2389 lease_dereference(&lp, MDL);
007e3ee4 2390 if (next)
2426234f
DH
2391 lease_reference(&lp, next, MDL);
2392 else if (!pass) {
2393 pass = 1;
2394 lease_reference(&lp, *lq, MDL);
2395 }
8c8e27c5 2396 }
2426234f
DH
2397
2398 if (next)
2399 lease_dereference(&next, MDL);
2400 if (lp)
2401 lease_dereference(&lp, MDL);
2402
a512cc3a
DH
2403 if (lts > thresh) {
2404 result = "IMBALANCED";
2405 log_func = log_error;
2406 } else {
2407 result = "balanced";
2408 log_func = log_info;
2409 }
2410
2411 log_func("%s pool %lx %s total %d free %d backup %d "
2412 "lts %d max-misbal %d", result, (unsigned long)p,
2413 (p->shared_network ?
2414 p->shared_network->name : ""), p->lease_count,
2415 p->free_leases, p->backup_leases, lts, thresh);
2426234f
DH
2416
2417 /* Recalculate next rebalance event timer. */
2418 dhcp_failover_pool_check(p);
8c8e27c5
TL
2419 }
2420 }
2426234f
DH
2421
2422 if (leases_queued)
2423 commit_leases();
2424
8c8e27c5
TL
2425 return leases_queued;
2426}
2427
2426234f
DH
2428/* dhcp_failover_pool_check: Called whenever FREE or BACKUP leases change
2429 * states, on both servers. Check the scheduled time to rebalance the pool
2430 * and lower it if applicable.
2431 */
2432void
2433dhcp_failover_pool_check(struct pool *pool)
8c8e27c5 2434{
2426234f
DH
2435 dhcp_failover_state_t *peer;
2436 TIME est1, est2;
8c8e27c5 2437
2426234f
DH
2438 peer = pool->failover_peer;
2439
2440 if(!peer || peer->me.state != normal)
2441 return;
8c8e27c5 2442
2426234f
DH
2443 /* Estimate the time left until lease exhaustion.
2444 * The first lease on the backup or free lists is also the oldest
2445 * lease. It is reasonable to guess that it will take at least
2446 * as much time for a pool to run out of leases, as the present
2447 * age of the oldest lease (seconds since it expired).
2448 *
2449 * Note that this isn't so sane of an assumption if the oldest
2450 * lease is a virgin (ends = 0), we wind up sending this against
2451 * the max_balance bounds check.
2452 */
2453 if(pool->free && pool->free->ends < cur_time)
2454 est1 = cur_time - pool->free->ends;
007e3ee4 2455 else
2426234f
DH
2456 est1 = 0;
2457
2458 if(pool->backup && pool->backup->ends < cur_time)
2459 est2 = cur_time - pool->backup->ends;
2460 else
2461 est2 = 0;
2462
2463 /* We don't want to schedule rebalance for when we think we'll run
2464 * out of leases, we want to schedule the rebalance for when we think
2465 * the disparity will be 'large enough' to warrant action.
2466 */
2467 est1 = ((est1 * peer->max_lease_misbalance) + 50) / 100;
2468 est2 = ((est2 * peer->max_lease_misbalance) + 50) / 100;
2469
2470 /* Guess when the local system will begin issuing POOLREQ panic
2471 * attacks because "max_lease_misbalance*2" has been exceeded.
2472 */
2473 if(peer->i_am == primary)
2474 est1 *= 2;
2475 else
2476 est2 *= 2;
2477
2478 /* Select the smallest time. */
2479 if(est1 > est2)
2480 est1 = est2;
2481
2482 /* Bounded by the maximum configured value. */
2483 if(est1 > peer->max_balance)
2484 est1 = peer->max_balance;
2485
2486 /* Project this time into the future. */
2487 est1 += cur_time;
2488
2489 /* Do not move the time down under the minimum. */
2490 est2 = peer->last_balance + peer->min_balance;
2491 if(peer->last_balance && (est1 < est2))
2492 est1 = est2;
2493
a512cc3a
DH
2494 /* Introduce a random delay. */
2495 est1 += random() % 5;
2496
2426234f
DH
2497 /* Do not move the time forward, or reset to the same time. */
2498 if(peer->sched_balance) {
2499 if (est1 >= peer->sched_balance)
2500 return;
2501
2502 /* We are about to schedule the time down, cancel the
2503 * current timeout.
2504 */
2505 cancel_timeout(dhcp_failover_pool_rebalance, peer);
8c8e27c5 2506 }
2426234f
DH
2507
2508 /* The time is different, and lower, use it. */
2509 peer->sched_balance = est1;
2510
2511#if defined(DEBUG_FAILOVER_TIMING)
2512 log_info("add_timeout +%d dhcp_failover_pool_rebalance",
2513 est1 - cur_time);
2514#endif
2515 add_timeout(est1, dhcp_failover_pool_rebalance, peer,
2516 (tvref_t)dhcp_failover_state_reference,
2517 (tvunref_t)dhcp_failover_state_dereference);
8c8e27c5
TL
2518}
2519
2520int dhcp_failover_state_pool_check (dhcp_failover_state_t *state)
2521{
8c8e27c5
TL
2522 struct shared_network *s;
2523 struct pool *p;
2524
2525 for (s = shared_networks; s; s = s -> next) {
2526 for (p = s -> pools; p; p = p -> next) {
2527 if (p -> failover_peer != state)
2528 continue;
2426234f 2529 dhcp_failover_pool_check (p);
8c8e27c5
TL
2530 }
2531 }
2532 return 0;
2533}
2534
9f3da356
TL
2535isc_result_t dhcp_failover_send_updates (dhcp_failover_state_t *state)
2536{
84fe3bd0 2537 struct lease *lp = (struct lease *)0;
9f3da356
TL
2538 isc_result_t status;
2539
8c8e27c5 2540 /* Can't update peer if we're not talking to it! */
01c6e68a 2541 if (!state -> link_to_peer)
8c8e27c5
TL
2542 return ISC_R_SUCCESS;
2543
a512cc3a
DH
2544 /* If there are acks pending, transmit them prior to potentialy
2545 * sending new updates for the same lease.
2546 */
2547 if (state->toack_queue_head != NULL)
2548 dhcp_failover_send_acks(state);
2549
02ca1b45
TL
2550 while ((state -> partner.max_flying_updates >
2551 state -> cur_unacked_updates) && state -> update_queue_head) {
25a69ca1 2552 /* Grab the head of the update queue. */
20916cae 2553 lease_reference (&lp, state -> update_queue_head, MDL);
84fe3bd0
TL
2554
2555 /* Send the update to the peer. */
2556 status = dhcp_failover_send_bind_update (state, lp);
2557 if (status != ISC_R_SUCCESS) {
20916cae 2558 lease_dereference (&lp, MDL);
84fe3bd0
TL
2559 return status;
2560 }
25a69ca1 2561 lp -> flags &= ~ON_UPDATE_QUEUE;
84fe3bd0 2562
25a69ca1
TL
2563 /* Take it off the head of the update queue and put the next
2564 item in the update queue at the head. */
20916cae 2565 lease_dereference (&state -> update_queue_head, MDL);
9f3da356 2566 if (lp -> next_pending) {
20916cae
TL
2567 lease_reference (&state -> update_queue_head,
2568 lp -> next_pending, MDL);
2569 lease_dereference (&lp -> next_pending, MDL);
9f3da356 2570 } else {
20916cae 2571 lease_dereference (&state -> update_queue_tail, MDL);
9f3da356 2572 }
84fe3bd0 2573
9f3da356 2574 if (state -> ack_queue_head) {
20916cae
TL
2575 lease_reference
2576 (&state -> ack_queue_tail -> next_pending,
2577 lp, MDL);
2578 lease_dereference (&state -> ack_queue_tail, MDL);
9f3da356 2579 } else {
20916cae 2580 lease_reference (&state -> ack_queue_head, lp, MDL);
9f3da356 2581 }
da292833
TL
2582#if defined (POINTER_DEBUG)
2583 if (lp -> next_pending) {
2584 log_error ("ack_queue_tail: lp -> next_pending");
2585 abort ();
2586 }
2587#endif
20916cae 2588 lease_reference (&state -> ack_queue_tail, lp, MDL);
25a69ca1 2589 lp -> flags |= ON_ACK_QUEUE;
20916cae 2590 lease_dereference (&lp, MDL);
9f3da356
TL
2591
2592 /* Count the object as an unacked update. */
2593 state -> cur_unacked_updates++;
2594 }
2595 return ISC_R_SUCCESS;
2596}
2597
2598/* Queue an update for a lease. Always returns 1 at this point - it's
2599 not an error for this to be called on a lease for which there's no
2600 failover peer. */
2601
007e3ee4 2602int dhcp_failover_queue_update (struct lease *lease, int immediate)
9f3da356
TL
2603{
2604 dhcp_failover_state_t *state;
2605
2606 if (!lease -> pool ||
2607 !lease -> pool -> failover_peer)
2608 return 1;
2609
25a69ca1
TL
2610 /* If it's already on the update queue, leave it there. */
2611 if (lease -> flags & ON_UPDATE_QUEUE)
2612 return 1;
2613
2614 /* Get the failover state structure for this lease. */
9f3da356 2615 state = lease -> pool -> failover_peer;
25a69ca1
TL
2616
2617 /* If it's on the ack queue, take it off. */
2618 if (lease -> flags & ON_ACK_QUEUE)
2619 dhcp_failover_ack_queue_remove (state, lease);
2620
9f3da356 2621 if (state -> update_queue_head) {
20916cae
TL
2622 lease_reference (&state -> update_queue_tail -> next_pending,
2623 lease, MDL);
2624 lease_dereference (&state -> update_queue_tail, MDL);
9f3da356 2625 } else {
20916cae 2626 lease_reference (&state -> update_queue_head, lease, MDL);
9f3da356 2627 }
294d641e 2628#if defined (POINTER_DEBUG)
da292833
TL
2629 if (lease -> next_pending) {
2630 log_error ("next pending on update queue lease.");
755acf88 2631#if defined (DEBUG_RC_HISTORY)
d758ad8c 2632 dump_rc_history (lease);
755acf88 2633#endif
da292833
TL
2634 abort ();
2635 }
294d641e 2636#endif
20916cae 2637 lease_reference (&state -> update_queue_tail, lease, MDL);
25a69ca1 2638 lease -> flags |= ON_UPDATE_QUEUE;
007e3ee4
TL
2639 if (immediate)
2640 dhcp_failover_send_updates (state);
9f3da356
TL
2641 return 1;
2642}
2643
12fd6075
TL
2644int dhcp_failover_send_acks (dhcp_failover_state_t *state)
2645{
988c1ca7
DN
2646 failover_message_t *msg = (failover_message_t *)0;
2647
2648 /* Must commit all leases prior to acking them. */
2649 if (!commit_leases ())
2650 return 0;
2651
2652 while (state -> toack_queue_head) {
2653 failover_message_reference
2654 (&msg, state -> toack_queue_head, MDL);
2655 failover_message_dereference
2656 (&state -> toack_queue_head, MDL);
2657 if (msg -> next) {
2658 failover_message_reference
2659 (&state -> toack_queue_head, msg -> next, MDL);
2660 }
2661
2662 dhcp_failover_send_bind_ack (state, msg, 0, (const char *)0);
2663
2664 failover_message_dereference (&msg, MDL);
2665 }
2666
fa0b4709
BC
2667 if (state -> toack_queue_tail)
2668 failover_message_dereference (&state -> toack_queue_tail, MDL);
988c1ca7
DN
2669 state -> pending_acks = 0;
2670
2671 return 1;
2672}
2673
12fd6075
TL
2674void dhcp_failover_toack_queue_timeout (void *vs)
2675{
988c1ca7 2676 dhcp_failover_state_t *state = vs;
0db87765
TL
2677
2678#if defined (DEBUG_FAILOVER_TIMING)
2679 log_info ("dhcp_failover_toack_queue_timeout");
2680#endif
2681
988c1ca7
DN
2682 dhcp_failover_send_acks (state);
2683}
2684
2685/* Queue an ack for a message. There is currently no way to queue a
2686 negative ack -- these need to be sent directly. */
2687
2688int dhcp_failover_queue_ack (dhcp_failover_state_t *state,
2689 failover_message_t *msg)
2690{
2691 if (state -> toack_queue_head) {
2692 failover_message_reference
2693 (&state -> toack_queue_tail -> next, msg, MDL);
2694 failover_message_dereference (&state -> toack_queue_tail, MDL);
2695 } else {
2696 failover_message_reference (&state -> toack_queue_head,
2697 msg, MDL);
2698 }
2699 failover_message_reference (&state -> toack_queue_tail, msg, MDL);
2700
2701 state -> pending_acks++;
2702
2703 /* Flush the toack queue whenever we exceed half the number of
2704 allowed unacked updates. */
2705 if (state -> pending_acks >= state -> partner.max_flying_updates / 2) {
2706 dhcp_failover_send_acks (state);
2707 }
2708
2709 /* Schedule a timeout to flush the ack queue. */
2710 if (state -> pending_acks > 0) {
0db87765
TL
2711#if defined (DEBUG_FAILOVER_TIMING)
2712 log_info ("add_timeout +2 %s",
2713 "dhcp_failover_toack_queue_timeout");
2714#endif
988c1ca7
DN
2715 add_timeout (cur_time + 2,
2716 dhcp_failover_toack_queue_timeout, state,
2717 (tvref_t)dhcp_failover_state_reference,
2718 (tvunref_t)dhcp_failover_state_dereference);
2719 }
2720
988c1ca7
DN
2721 return 1;
2722}
2723
25a69ca1
TL
2724void dhcp_failover_ack_queue_remove (dhcp_failover_state_t *state,
2725 struct lease *lease)
2726{
2727 struct lease *lp;
2728
999992c9
TL
2729 if (!(lease -> flags & ON_ACK_QUEUE))
2730 return;
2731
25a69ca1 2732 if (state -> ack_queue_head == lease) {
20916cae 2733 lease_dereference (&state -> ack_queue_head, MDL);
25a69ca1 2734 if (lease -> next_pending) {
20916cae
TL
2735 lease_reference (&state -> ack_queue_head,
2736 lease -> next_pending, MDL);
6f0b9ed0 2737 lease_dereference (&lease -> next_pending, MDL);
25a69ca1 2738 } else {
20916cae 2739 lease_dereference (&state -> ack_queue_tail, MDL);
25a69ca1 2740 }
988c1ca7
DN
2741 } else {
2742 for (lp = state -> ack_queue_head;
2743 lp && lp -> next_pending != lease;
2744 lp = lp -> next_pending)
2745 ;
2746
2747 if (!lp)
2748 return;
2749
20916cae 2750 lease_dereference (&lp -> next_pending, MDL);
6f0b9ed0 2751 if (lease -> next_pending) {
20916cae
TL
2752 lease_reference (&lp -> next_pending,
2753 lease -> next_pending, MDL);
6f0b9ed0
TL
2754 lease_dereference (&lease -> next_pending, MDL);
2755 } else {
20916cae 2756 lease_dereference (&state -> ack_queue_tail, MDL);
da292833
TL
2757 if (lp -> next_pending) {
2758 log_error ("state -> ack_queue_tail");
2759 abort ();
2760 }
20916cae 2761 lease_reference (&state -> ack_queue_tail, lp, MDL);
25a69ca1
TL
2762 }
2763 }
988c1ca7 2764
25a69ca1 2765 lease -> flags &= ~ON_ACK_QUEUE;
808d45fd
DH
2766 /* Multiple acks on one XID is an error and may cause badness. */
2767 lease->last_xid = 0;
2768 /* XXX: this violates draft-failover. We can't send another
2769 * update just because we forgot about an old one that hasn't
2770 * been acked yet.
2771 */
6f0b9ed0 2772 state -> cur_unacked_updates--;
988c1ca7
DN
2773
2774 /*
2775 * When updating leases as a result of an ack, we defer the commit
2776 * for performance reasons. When there are no more acks pending,
2777 * do a commit.
2778 */
2779 if (state -> cur_unacked_updates == 0) {
2780 commit_leases();
2781 }
25a69ca1
TL
2782}
2783
d50d0b82 2784isc_result_t dhcp_failover_state_set_value (omapi_object_t *h,
85a53735
TL
2785 omapi_object_t *id,
2786 omapi_data_string_t *name,
2787 omapi_typed_data_t *value)
d50d0b82 2788{
311dbab6
DN
2789 isc_result_t status;
2790
d50d0b82
TL
2791 if (h -> type != dhcp_type_failover_state)
2792 return ISC_R_INVALIDARG;
311dbab6
DN
2793
2794 /* This list of successful returns is completely wrong, but the
2795 fastest way to make dhcpctl do something vaguely sane when
2796 you try to change the local state. */
2797
2798 if (!omapi_ds_strcmp (name, "name")) {
2799 return ISC_R_SUCCESS;
311dbab6
DN
2800 } else if (!omapi_ds_strcmp (name, "partner-address")) {
2801 return ISC_R_SUCCESS;
2802 } else if (!omapi_ds_strcmp (name, "local-address")) {
2803 return ISC_R_SUCCESS;
2804 } else if (!omapi_ds_strcmp (name, "partner-port")) {
2805 return ISC_R_SUCCESS;
2806 } else if (!omapi_ds_strcmp (name, "local-port")) {
2807 return ISC_R_SUCCESS;
2808 } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
2809 return ISC_R_SUCCESS;
2810 } else if (!omapi_ds_strcmp (name, "mclt")) {
2811 return ISC_R_SUCCESS;
2812 } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
2813 return ISC_R_SUCCESS;
2814 } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
2815 return ISC_R_SUCCESS;
2816 } else if (!omapi_ds_strcmp (name, "partner-state")) {
2817 return ISC_R_SUCCESS;
2818 } else if (!omapi_ds_strcmp (name, "local-state")) {
7c6a9da0 2819 unsigned long l;
311dbab6
DN
2820 status = omapi_get_int_value (&l, value);
2821 if (status != ISC_R_SUCCESS)
2822 return status;
2823 return dhcp_failover_set_state ((dhcp_failover_state_t *)h, l);
2824 } else if (!omapi_ds_strcmp (name, "partner-stos")) {
2825 return ISC_R_SUCCESS;
2826 } else if (!omapi_ds_strcmp (name, "local-stos")) {
2827 return ISC_R_SUCCESS;
2828 } else if (!omapi_ds_strcmp (name, "hierarchy")) {
2829 return ISC_R_SUCCESS;
2830 } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
2831 return ISC_R_SUCCESS;
2832 } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
2833 return ISC_R_SUCCESS;
2834 } else if (!omapi_ds_strcmp (name, "skew")) {
2835 return ISC_R_SUCCESS;
2836 } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
2837 return ISC_R_SUCCESS;
2838 } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
2839 return ISC_R_SUCCESS;
2840 }
2841
d50d0b82
TL
2842 if (h -> inner && h -> inner -> type -> set_value)
2843 return (*(h -> inner -> type -> set_value))
2844 (h -> inner, id, name, value);
2845 return ISC_R_NOTFOUND;
2846}
2847
85a53735
TL
2848void dhcp_failover_keepalive (void *vs)
2849{
85a53735
TL
2850}
2851
9f3da356
TL
2852void dhcp_failover_reconnect (void *vs)
2853{
2854 dhcp_failover_state_t *state = vs;
2855 isc_result_t status;
2856
0db87765
TL
2857#if defined (DEBUG_FAILOVER_TIMING)
2858 log_info ("dhcp_failover_reconnect");
2859#endif
da292833
TL
2860 /* If we already connected the other way, let the connection
2861 recovery code initiate any retry that may be required. */
2862 if (state -> link_to_peer)
2863 return;
2864
9f3da356 2865 status = dhcp_failover_link_initiate ((omapi_object_t *)state);
f67a7be5 2866 if (status != ISC_R_SUCCESS && status != ISC_R_INCOMPLETE) {
9f3da356
TL
2867 log_info ("failover peer %s: %s", state -> name,
2868 isc_result_totext (status));
0db87765
TL
2869#if defined (DEBUG_FAILOVER_TIMING)
2870 log_info ("add_timeout +90 %s",
2871 "dhcp_failover_listener_restart");
2872#endif
9f3da356 2873 add_timeout (cur_time + 90,
20916cae
TL
2874 dhcp_failover_listener_restart, state,
2875 (tvref_t)dhcp_failover_state_reference,
2876 (tvunref_t)dhcp_failover_state_dereference);
9f3da356
TL
2877 }
2878}
2879
5f0c7be1
TL
2880void dhcp_failover_startup_timeout (void *vs)
2881{
2882 dhcp_failover_state_t *state = vs;
5f0c7be1 2883
0db87765
TL
2884#if defined (DEBUG_FAILOVER_TIMING)
2885 log_info ("dhcp_failover_startup_timeout");
2886#endif
2887
02ca1b45
TL
2888 dhcp_failover_state_transition (state, "disconnect");
2889}
2890
2891void dhcp_failover_link_startup_timeout (void *vl)
2892{
2893 dhcp_failover_link_t *link = vl;
02ca1b45
TL
2894 omapi_object_t *p;
2895
2896 for (p = (omapi_object_t *)link; p -> inner; p = p -> inner)
2897 ;
2898 for (; p; p = p -> outer)
2899 if (p -> type == omapi_type_connection)
2900 break;
2901 if (p) {
da292833 2902 log_info ("failover: link startup timeout");
02ca1b45
TL
2903 omapi_disconnect (p, 1);
2904 }
5f0c7be1
TL
2905}
2906
9f3da356
TL
2907void dhcp_failover_listener_restart (void *vs)
2908{
2909 dhcp_failover_state_t *state = vs;
2910 isc_result_t status;
2911
0db87765
TL
2912#if defined (DEBUG_FAILOVER_TIMING)
2913 log_info ("dhcp_failover_listener_restart");
2914#endif
2915
9f3da356
TL
2916 status = dhcp_failover_listen ((omapi_object_t *)state);
2917 if (status != ISC_R_SUCCESS) {
2918 log_info ("failover peer %s: %s", state -> name,
2919 isc_result_totext (status));
0db87765
TL
2920#if defined (DEBUG_FAILOVER_TIMING)
2921 log_info ("add_timeout +90 %s",
2922 "dhcp_failover_listener_restart");
2923#endif
9f3da356 2924 add_timeout (cur_time + 90,
20916cae
TL
2925 dhcp_failover_listener_restart, state,
2926 (tvref_t)dhcp_failover_state_reference,
2927 (tvunref_t)dhcp_failover_state_dereference);
9f3da356
TL
2928 }
2929}
2930
d50d0b82 2931isc_result_t dhcp_failover_state_get_value (omapi_object_t *h,
85a53735
TL
2932 omapi_object_t *id,
2933 omapi_data_string_t *name,
2934 omapi_value_t **value)
d50d0b82 2935{
85a53735
TL
2936 dhcp_failover_state_t *s;
2937 struct option_cache *oc;
2938 struct data_string ds;
2939 isc_result_t status;
2940
d50d0b82
TL
2941 if (h -> type != dhcp_type_failover_state)
2942 return ISC_R_INVALIDARG;
85a53735 2943 s = (dhcp_failover_state_t *)h;
d50d0b82 2944
85a53735
TL
2945 if (!omapi_ds_strcmp (name, "name")) {
2946 if (s -> name)
2947 return omapi_make_string_value (value,
2948 name, s -> name, MDL);
2949 return ISC_R_NOTFOUND;
2950 } else if (!omapi_ds_strcmp (name, "partner-address")) {
02ca1b45 2951 oc = s -> partner.address;
85a53735
TL
2952 getaddr:
2953 memset (&ds, 0, sizeof ds);
2954 if (!evaluate_option_cache (&ds, (struct packet *)0,
2955 (struct lease *)0,
9e383163 2956 (struct client_state *)0,
85a53735
TL
2957 (struct option_state *)0,
2958 (struct option_state *)0,
2959 &global_scope, oc, MDL)) {
2960 return ISC_R_NOTFOUND;
2961 }
2962 status = omapi_make_const_value (value,
2963 name, ds.data, ds.len, MDL);
9f3da356 2964 /* Disgusting kludge: */
02ca1b45 2965 if (oc == s -> me.address && !s -> server_identifier.len)
9f3da356 2966 data_string_copy (&s -> server_identifier, &ds, MDL);
85a53735
TL
2967 data_string_forget (&ds, MDL);
2968 return status;
2969 } else if (!omapi_ds_strcmp (name, "local-address")) {
02ca1b45 2970 oc = s -> me.address;
85a53735
TL
2971 goto getaddr;
2972 } else if (!omapi_ds_strcmp (name, "partner-port")) {
02ca1b45
TL
2973 return omapi_make_int_value (value, name,
2974 s -> partner.port, MDL);
85a53735
TL
2975 } else if (!omapi_ds_strcmp (name, "local-port")) {
2976 return omapi_make_int_value (value,
02ca1b45 2977 name, s -> me.port, MDL);
85a53735
TL
2978 } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
2979 return omapi_make_uint_value (value, name,
02ca1b45
TL
2980 s -> me.max_flying_updates,
2981 MDL);
85a53735
TL
2982 } else if (!omapi_ds_strcmp (name, "mclt")) {
2983 return omapi_make_uint_value (value, name, s -> mclt, MDL);
2984 } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
2985 return omapi_make_int_value (value, name,
2986 s -> load_balance_max_secs, MDL);
2987 } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
2988 if (s -> hba)
2989 return omapi_make_const_value (value, name,
2990 s -> hba, 32, MDL);
2991 return ISC_R_NOTFOUND;
2992 } else if (!omapi_ds_strcmp (name, "partner-state")) {
2993 return omapi_make_uint_value (value, name,
02ca1b45 2994 s -> partner.state, MDL);
85a53735
TL
2995 } else if (!omapi_ds_strcmp (name, "local-state")) {
2996 return omapi_make_uint_value (value, name,
02ca1b45 2997 s -> me.state, MDL);
85a53735
TL
2998 } else if (!omapi_ds_strcmp (name, "partner-stos")) {
2999 return omapi_make_int_value (value, name,
02ca1b45 3000 s -> partner.stos, MDL);
85a53735
TL
3001 } else if (!omapi_ds_strcmp (name, "local-stos")) {
3002 return omapi_make_int_value (value, name,
02ca1b45 3003 s -> me.stos, MDL);
85a53735
TL
3004 } else if (!omapi_ds_strcmp (name, "hierarchy")) {
3005 return omapi_make_uint_value (value, name, s -> i_am, MDL);
3006 } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
3007 return omapi_make_int_value (value, name,
3008 s -> last_packet_sent, MDL);
3009 } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
3010 return omapi_make_int_value (value, name,
3011 s -> last_timestamp_received,
3012 MDL);
3013 } else if (!omapi_ds_strcmp (name, "skew")) {
3014 return omapi_make_int_value (value, name, s -> skew, MDL);
85a53735
TL
3015 } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
3016 return omapi_make_uint_value (value, name,
02ca1b45
TL
3017 s -> me.max_response_delay,
3018 MDL);
85a53735
TL
3019 } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
3020 return omapi_make_int_value (value, name,
3021 s -> cur_unacked_updates, MDL);
3022 }
3023
d50d0b82
TL
3024 if (h -> inner && h -> inner -> type -> get_value)
3025 return (*(h -> inner -> type -> get_value))
3026 (h -> inner, id, name, value);
3027 return ISC_R_NOTFOUND;
3028}
3029
3030isc_result_t dhcp_failover_state_destroy (omapi_object_t *h,
4bd8800e 3031 const char *file, int line)
d50d0b82 3032{
85a53735
TL
3033 dhcp_failover_state_t *s;
3034
d50d0b82
TL
3035 if (h -> type != dhcp_type_failover_state)
3036 return ISC_R_INVALIDARG;
85a53735 3037 s = (dhcp_failover_state_t *)h;
d758ad8c 3038
85a53735 3039 if (s -> link_to_peer)
d758ad8c
TL
3040 dhcp_failover_link_dereference (&s -> link_to_peer, file, line);
3041 if (s -> name) {
85a53735 3042 dfree (s -> name, MDL);
d758ad8c
TL
3043 s -> name = (char *)0;
3044 }
02ca1b45 3045 if (s -> partner.address)
d758ad8c 3046 option_cache_dereference (&s -> partner.address, file, line);
02ca1b45 3047 if (s -> me.address)
d758ad8c
TL
3048 option_cache_dereference (&s -> me.address, file, line);
3049 if (s -> hba) {
3050 dfree (s -> hba, file, line);
3051 s -> hba = (u_int8_t *)0;
3052 }
3053 if (s -> update_queue_head)
3054 lease_dereference (&s -> update_queue_head, file, line);
3055 if (s -> update_queue_tail)
3056 lease_dereference (&s -> update_queue_tail, file, line);
3057 if (s -> ack_queue_head)
3058 lease_dereference (&s -> ack_queue_head, file, line);
3059 if (s -> ack_queue_tail)
3060 lease_dereference (&s -> ack_queue_tail, file, line);
3061 if (s -> send_update_done)
3062 lease_dereference (&s -> send_update_done, file, line);
3063 if (s -> toack_queue_head)
3064 failover_message_dereference (&s -> toack_queue_head,
3065 file, line);
3066 if (s -> toack_queue_tail)
3067 failover_message_dereference (&s -> toack_queue_tail,
3068 file, line);
d50d0b82
TL
3069 return ISC_R_SUCCESS;
3070}
3071
3072/* Write all the published values associated with the object through the
3073 specified connection. */
3074
3075isc_result_t dhcp_failover_state_stuff (omapi_object_t *c,
d8a417b2 3076 omapi_object_t *id,
85a53735 3077 omapi_object_t *h)
d50d0b82 3078{
85a53735
TL
3079 dhcp_failover_state_t *s;
3080 omapi_connection_object_t *conn;
3081 isc_result_t status;
d50d0b82 3082
85a53735 3083 if (c -> type != omapi_type_connection)
d50d0b82 3084 return ISC_R_INVALIDARG;
85a53735 3085 conn = (omapi_connection_object_t *)c;
d50d0b82 3086
85a53735
TL
3087 if (h -> type != dhcp_type_failover_state)
3088 return ISC_R_INVALIDARG;
3089 s = (dhcp_failover_state_t *)h;
3090
3091 status = omapi_connection_put_name (c, "name");
3092 if (status != ISC_R_SUCCESS)
3093 return status;
3094 status = omapi_connection_put_string (c, s -> name);
3095 if (status != ISC_R_SUCCESS)
3096 return status;
3097
3098 status = omapi_connection_put_name (c, "partner-address");
3099 if (status != ISC_R_SUCCESS)
3100 return status;
02ca1b45 3101 status = omapi_connection_put_uint32 (c, sizeof s -> partner.address);
85a53735
TL
3102 if (status != ISC_R_SUCCESS)
3103 return status;
02ca1b45
TL
3104 status = omapi_connection_copyin (c, (u_int8_t *)&s -> partner.address,
3105 sizeof s -> partner.address);
85a53735
TL
3106 if (status != ISC_R_SUCCESS)
3107 return status;
3108
3109 status = omapi_connection_put_name (c, "partner-port");
3110 if (status != ISC_R_SUCCESS)
3111 return status;
3112 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3113 if (status != ISC_R_SUCCESS)
3114 return status;
02ca1b45 3115 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> partner.port);
85a53735
TL
3116 if (status != ISC_R_SUCCESS)
3117 return status;
3118
3119 status = omapi_connection_put_name (c, "local-address");
3120 if (status != ISC_R_SUCCESS)
3121 return status;
02ca1b45 3122 status = omapi_connection_put_uint32 (c, sizeof s -> me.address);
85a53735
TL
3123 if (status != ISC_R_SUCCESS)
3124 return status;
02ca1b45
TL
3125 status = omapi_connection_copyin (c, (u_int8_t *)&s -> me.address,
3126 sizeof s -> me.address);
85a53735
TL
3127 if (status != ISC_R_SUCCESS)
3128 return status;
3129
3130 status = omapi_connection_put_name (c, "local-port");
3131 if (status != ISC_R_SUCCESS)
3132 return status;
3133 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3134 if (status != ISC_R_SUCCESS)
3135 return status;
02ca1b45 3136 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.port);
85a53735
TL
3137 if (status != ISC_R_SUCCESS)
3138 return status;
3139
3140 status = omapi_connection_put_name (c, "max-outstanding-updates");
3141 if (status != ISC_R_SUCCESS)
3142 return status;
3143 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3144 if (status != ISC_R_SUCCESS)
3145 return status;
02ca1b45
TL
3146 status = omapi_connection_put_uint32 (c,
3147 s -> me.max_flying_updates);
85a53735
TL
3148 if (status != ISC_R_SUCCESS)
3149 return status;
3150
3151 status = omapi_connection_put_name (c, "mclt");
3152 if (status != ISC_R_SUCCESS)
3153 return status;
3154 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3155 if (status != ISC_R_SUCCESS)
3156 return status;
3157 status = omapi_connection_put_uint32 (c, s -> mclt);
3158 if (status != ISC_R_SUCCESS)
3159 return status;
3160
3161 status = omapi_connection_put_name (c, "load-balance-max-secs");
3162 if (status != ISC_R_SUCCESS)
3163 return status;
3164 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3165 if (status != ISC_R_SUCCESS)
3166 return status;
3167 status = (omapi_connection_put_uint32
3168 (c, (u_int32_t)s -> load_balance_max_secs));
3169 if (status != ISC_R_SUCCESS)
3170 return status;
3171
3172
3173 if (s -> hba) {
3174 status = omapi_connection_put_name (c, "load-balance-hba");
3175 if (status != ISC_R_SUCCESS)
3176 return status;
3177 status = omapi_connection_put_uint32 (c, 32);
3178 if (status != ISC_R_SUCCESS)
3179 return status;
3180 status = omapi_connection_copyin (c, s -> hba, 32);
3181 if (status != ISC_R_SUCCESS)
3182 return status;
3183 }
3184
3185 status = omapi_connection_put_name (c, "partner-state");
3186 if (status != ISC_R_SUCCESS)
3187 return status;
3188 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3189 if (status != ISC_R_SUCCESS)
3190 return status;
02ca1b45 3191 status = omapi_connection_put_uint32 (c, s -> partner.state);
85a53735
TL
3192 if (status != ISC_R_SUCCESS)
3193 return status;
3194
3195 status = omapi_connection_put_name (c, "local-state");
3196 if (status != ISC_R_SUCCESS)
3197 return status;
3198 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3199 if (status != ISC_R_SUCCESS)
3200 return status;
02ca1b45 3201 status = omapi_connection_put_uint32 (c, s -> me.state);
85a53735
TL
3202 if (status != ISC_R_SUCCESS)
3203 return status;
3204
3205 status = omapi_connection_put_name (c, "partner-stos");
3206 if (status != ISC_R_SUCCESS)
3207 return status;
3208 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3209 if (status != ISC_R_SUCCESS)
3210 return status;
02ca1b45
TL
3211 status = omapi_connection_put_uint32 (c,
3212 (u_int32_t)s -> partner.stos);
85a53735
TL
3213 if (status != ISC_R_SUCCESS)
3214 return status;
3215
3216 status = omapi_connection_put_name (c, "local-stos");
3217 if (status != ISC_R_SUCCESS)
3218 return status;
3219 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3220 if (status != ISC_R_SUCCESS)
3221 return status;
02ca1b45 3222 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.stos);
85a53735
TL
3223 if (status != ISC_R_SUCCESS)
3224 return status;
3225
3226 status = omapi_connection_put_name (c, "hierarchy");
3227 if (status != ISC_R_SUCCESS)
3228 return status;
3229 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3230 if (status != ISC_R_SUCCESS)
3231 return status;
3232 status = omapi_connection_put_uint32 (c, s -> i_am);
3233 if (status != ISC_R_SUCCESS)
3234 return status;
3235
3236 status = omapi_connection_put_name (c, "last-packet-sent");
3237 if (status != ISC_R_SUCCESS)
3238 return status;
3239 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3240 if (status != ISC_R_SUCCESS)
3241 return status;
3242 status = (omapi_connection_put_uint32
3243 (c, (u_int32_t)s -> last_packet_sent));
3244 if (status != ISC_R_SUCCESS)
3245 return status;
3246
3247 status = omapi_connection_put_name (c, "last-timestamp-received");
3248 if (status != ISC_R_SUCCESS)
3249 return status;
3250 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3251 if (status != ISC_R_SUCCESS)
3252 return status;
3253 status = (omapi_connection_put_uint32
3254 (c, (u_int32_t)s -> last_timestamp_received));
3255 if (status != ISC_R_SUCCESS)
3256 return status;
3257
3258 status = omapi_connection_put_name (c, "skew");
3259 if (status != ISC_R_SUCCESS)
3260 return status;
3261 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3262 if (status != ISC_R_SUCCESS)
3263 return status;
3264 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> skew);
3265 if (status != ISC_R_SUCCESS)
3266 return status;
3267
85a53735
TL
3268 status = omapi_connection_put_name (c, "max-response-delay");
3269 if (status != ISC_R_SUCCESS)
3270 return status;
3271 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3272 if (status != ISC_R_SUCCESS)
3273 return status;
3274 status = (omapi_connection_put_uint32
02ca1b45 3275 (c, (u_int32_t)s -> me.max_response_delay));
85a53735
TL
3276 if (status != ISC_R_SUCCESS)
3277 return status;
3278
3279 status = omapi_connection_put_name (c, "cur-unacked-updates");
3280 if (status != ISC_R_SUCCESS)
3281 return status;
3282 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3283 if (status != ISC_R_SUCCESS)
3284 return status;
3285 status = (omapi_connection_put_uint32
3286 (c, (u_int32_t)s -> cur_unacked_updates));
3287 if (status != ISC_R_SUCCESS)
3288 return status;
3289
3290 if (h -> inner && h -> inner -> type -> stuff_values)
3291 return (*(h -> inner -> type -> stuff_values)) (c, id,
3292 h -> inner);
d50d0b82
TL
3293 return ISC_R_SUCCESS;
3294}
3295
d8a417b2
TL
3296isc_result_t dhcp_failover_state_lookup (omapi_object_t **sp,
3297 omapi_object_t *id,
3298 omapi_object_t *ref)
3299{
3300 omapi_value_t *tv = (omapi_value_t *)0;
3301 isc_result_t status;
3302 dhcp_failover_state_t *s;
3303
d758ad8c
TL
3304 if (!ref)
3305 return ISC_R_NOKEYS;
3306
d8a417b2
TL
3307 /* First see if we were sent a handle. */
3308 status = omapi_get_value_str (ref, id, "handle", &tv);
3309 if (status == ISC_R_SUCCESS) {
3310 status = omapi_handle_td_lookup (sp, tv -> value);
3311
4bd8800e 3312 omapi_value_dereference (&tv, MDL);
d8a417b2
TL
3313 if (status != ISC_R_SUCCESS)
3314 return status;
3315
3316 /* Don't return the object if the type is wrong. */
3317 if ((*sp) -> type != dhcp_type_failover_state) {
4bd8800e 3318 omapi_object_dereference (sp, MDL);
d8a417b2
TL
3319 return ISC_R_INVALIDARG;
3320 }
3321 }
3322
dd53dc5a 3323 /* Look the failover state up by peer name. */
01c6e68a 3324 status = omapi_get_value_str (ref, id, "name", &tv);
d8a417b2
TL
3325 if (status == ISC_R_SUCCESS) {
3326 for (s = failover_states; s; s = s -> next) {
dd53dc5a 3327 unsigned l = strlen (s -> name);
98311e4b 3328 if (l == tv -> value -> u.buffer.len &&
dd53dc5a 3329 !memcmp (s -> name,
d8a417b2
TL
3330 tv -> value -> u.buffer.value, l))
3331 break;
3332 }
4bd8800e 3333 omapi_value_dereference (&tv, MDL);
d8a417b2
TL
3334
3335 /* If we already have a lease, and it's not the same one,
3336 then the query was invalid. */
3337 if (*sp && *sp != (omapi_object_t *)s) {
4bd8800e 3338 omapi_object_dereference (sp, MDL);
d8a417b2
TL
3339 return ISC_R_KEYCONFLICT;
3340 } else if (!s) {
3341 if (*sp)
4bd8800e 3342 omapi_object_dereference (sp, MDL);
d8a417b2
TL
3343 return ISC_R_NOTFOUND;
3344 } else if (!*sp)
3345 /* XXX fix so that hash lookup itself creates
3346 XXX the reference. */
4bd8800e 3347 omapi_object_reference (sp, (omapi_object_t *)s, MDL);
d8a417b2
TL
3348 }
3349
3350 /* If we get to here without finding a lease, no valid key was
3351 specified. */
3352 if (!*sp)
3353 return ISC_R_NOKEYS;
3354 return ISC_R_SUCCESS;
3355}
3356
3357isc_result_t dhcp_failover_state_create (omapi_object_t **sp,
3358 omapi_object_t *id)
3359{
3360 return ISC_R_NOTIMPLEMENTED;
3361}
3362
3363isc_result_t dhcp_failover_state_remove (omapi_object_t *sp,
3364 omapi_object_t *id)
3365{
3366 return ISC_R_NOTIMPLEMENTED;
3367}
3368
9f3da356
TL
3369int dhcp_failover_state_match (dhcp_failover_state_t *state,
3370 u_int8_t *addr, unsigned addrlen)
3371{
9f3da356
TL
3372 struct data_string ds;
3373 int i;
3374
3375 memset (&ds, 0, sizeof ds);
3376 if (evaluate_option_cache (&ds, (struct packet *)0,
3377 (struct lease *)0,
9e383163 3378 (struct client_state *)0,
9f3da356
TL
3379 (struct option_state *)0,
3380 (struct option_state *)0,
3381 &global_scope,
02ca1b45 3382 state -> partner.address, MDL)) {
9f3da356
TL
3383 for (i = 0; i + addrlen - 1 < ds.len; i += addrlen) {
3384 if (!memcmp (&ds.data [i],
3385 addr, addrlen)) {
3386 data_string_forget (&ds, MDL);
3387 return 1;
3388 }
3389 }
3390 data_string_forget (&ds, MDL);
3391 }
3392 return 0;
3393}
3394
a55ccdd0
DH
3395int
3396dhcp_failover_state_match_by_name(state, name)
3397 dhcp_failover_state_t *state;
3398 failover_option_t *name;
3399{
3400 if ((strlen(state->name) == name->count) &&
3401 (memcmp(state->name, name->data, name->count) == 0))
3402 return 1;
3403
3404 return 0;
3405}
3406
9f3da356
TL
3407const char *dhcp_failover_reject_reason_print (int reason)
3408{
a55ccdd0
DH
3409 static char resbuf[sizeof("Undefined-255: This reason code is not defined "
3410 "in the protocol standard.")];
3411
3412 if ((reason > 0xff) || (reason < 0))
3413 return "Reason code out of range.";
3414
9f3da356
TL
3415 switch (reason) {
3416 case FTR_ILLEGAL_IP_ADDR:
3417 return "Illegal IP address (not part of any address pool).";
3418
3419 case FTR_FATAL_CONFLICT:
3420 return "Fatal conflict exists: address in use by other client.";
3421
3422 case FTR_MISSING_BINDINFO:
3423 return "Missing binding information.";
3424
3425 case FTR_TIMEMISMATCH:
3426 return "Connection rejected, time mismatch too great.";
3427
3428 case FTR_INVALID_MCLT:
3429 return "Connection rejected, invalid MCLT.";
3430
3431 case FTR_MISC_REJECT:
3432 return "Connection rejected, unknown reason.";
3433
3434 case FTR_DUP_CONNECTION:
3435 return "Connection rejected, duplicate connection.";
3436
3437 case FTR_INVALID_PARTNER:
3438 return "Connection rejected, invalid failover partner.";
3439
3440 case FTR_TLS_UNSUPPORTED:
3441 return "TLS not supported.";
3442
3443 case FTR_TLS_UNCONFIGURED:
3444 return "TLS supported but not configured.";
3445
3446 case FTR_TLS_REQUIRED:
3447 return "TLS required but not supported by partner.";
3448
3449 case FTR_DIGEST_UNSUPPORTED:
3450 return "Message digest not supported.";
3451
3452 case FTR_DIGEST_UNCONFIGURED:
3453 return "Message digest not configured.";
3454
3455 case FTR_VERSION_MISMATCH:
3456 return "Protocol version mismatch.";
3457
9f3da356
TL
3458 case FTR_OUTDATED_BIND_INFO:
3459 return "Outdated binding information.";
3460
3461 case FTR_LESS_CRIT_BIND_INFO:
3462 return "Less critical binding information.";
3463
3464 case FTR_NO_TRAFFIC:
3465 return "No traffic within sufficient time.";
3466
3467 case FTR_HBA_CONFLICT:
3468 return "Hash bucket assignment conflict.";
3469
a55ccdd0
DH
3470 case FTR_IP_NOT_RESERVED:
3471 return "IP not reserved on this server.";
3472
3473 case FTR_IP_DIGEST_FAILURE:
3474 return "Message digest failed to compare.";
3475
3476 case FTR_IP_MISSING_DIGEST:
3477 return "Missing message digest.";
3478
9f3da356 3479 case FTR_UNKNOWN:
a55ccdd0
DH
3480 return "Unknown Error.";
3481
3482 default:
3483 sprintf(resbuf, "Undefined-%d: This reason code is not defined in the "
3484 "protocol standard.", reason);
3485 return resbuf;
9f3da356
TL
3486 }
3487}
3488
3489const char *dhcp_failover_state_name_print (enum failover_state state)
3490{
3491 switch (state) {
3492 default:
3493 case unknown_state:
3494 return "unknown-state";
3495
3496 case partner_down:
3497 return "partner-down";
3498
3499 case normal:
3500 return "normal";
3501
3502 case communications_interrupted:
3503 return "communications-interrupted";
3504
5f0c7be1
TL
3505 case resolution_interrupted:
3506 return "resolution-interrupted";
9f3da356
TL
3507
3508 case potential_conflict:
3509 return "potential-conflict";
3510
3511 case recover:
3512 return "recover";
02ca1b45
TL
3513
3514 case recover_done:
3515 return "recover-done";
3516
fd273e02
TL
3517 case recover_wait:
3518 return "recover-wait";
3519
02ca1b45
TL
3520 case shut_down:
3521 return "shutdown";
3522
3523 case paused:
3524 return "paused";
3525
3526 case startup:
3527 return "startup";
3528 }
3529}
3530
d9539882 3531const char *dhcp_failover_message_name (unsigned type)
02ca1b45 3532{
a55ccdd0
DH
3533 static char messbuf[sizeof("unknown-message-255")];
3534
3535 if (type > 0xff)
3536 return "invalid-message";
3537
02ca1b45
TL
3538 switch (type) {
3539 case FTM_POOLREQ:
3540 return "pool-request";
3541
3542 case FTM_POOLRESP:
3543 return "pool-response";
3544
3545 case FTM_BNDUPD:
3546 return "bind-update";
3547
3548 case FTM_BNDACK:
3549 return "bind-ack";
3550
3551 case FTM_CONNECT:
3552 return "connect";
3553
3554 case FTM_CONNECTACK:
3555 return "connect-ack";
3556
3557 case FTM_UPDREQ:
3558 return "update-request";
3559
3560 case FTM_UPDDONE:
3561 return "update-done";
3562
3563 case FTM_UPDREQALL:
3564 return "update-request-all";
3565
3566 case FTM_STATE:
3567 return "state";
3568
3569 case FTM_CONTACT:
3570 return "contact";
3571
3572 case FTM_DISCONNECT:
3573 return "disconnect";
3574
3575 default:
a55ccdd0
DH
3576 sprintf(messbuf, "unknown-message-%u", type);
3577 return messbuf;
02ca1b45
TL
3578 }
3579}
3580
12fd6075
TL
3581const char *dhcp_failover_option_name (unsigned type)
3582{
a55ccdd0
DH
3583 static char optbuf[sizeof("unknown-option-65535")];
3584
3585 if (type > 0xffff)
3586 return "invalid-option";
3587
02ca1b45 3588 switch (type) {
a55ccdd0
DH
3589 case FTO_ADDRESSES_TRANSFERRED:
3590 return "addresses-transferred";
02ca1b45 3591
a55ccdd0 3592 case FTO_ASSIGNED_IP_ADDRESS:
02ca1b45
TL
3593 return "assigned-ip-address";
3594
a55ccdd0
DH
3595 case FTO_BINDING_STATUS:
3596 return "binding-status";
02ca1b45 3597
a55ccdd0 3598 case FTO_CLIENT_IDENTIFIER:
02ca1b45
TL
3599 return "client-identifier";
3600
a55ccdd0 3601 case FTO_CHADDR:
02ca1b45
TL
3602 return "chaddr";
3603
a55ccdd0
DH
3604 case FTO_CLTT:
3605 return "cltt";
02ca1b45 3606
a55ccdd0
DH
3607 case FTO_DDNS:
3608 return "ddns";
02ca1b45 3609
a55ccdd0
DH
3610 case FTO_DELAYED_SERVICE:
3611 return "delayed-service";
02ca1b45 3612
a55ccdd0
DH
3613 case FTO_HBA:
3614 return "hba";
02ca1b45 3615
a55ccdd0
DH
3616 case FTO_IP_FLAGS:
3617 return "ip-flags";
02ca1b45 3618
a55ccdd0 3619 case FTO_LEASE_EXPIRY:
02ca1b45
TL
3620 return "lease-expiry";
3621
a55ccdd0
DH
3622 case FTO_MAX_UNACKED:
3623 return "max-unacked";
02ca1b45 3624
a55ccdd0
DH
3625 case FTO_MCLT:
3626 return "mclt";
02ca1b45 3627
a55ccdd0
DH
3628 case FTO_MESSAGE:
3629 return "message";
02ca1b45 3630
a55ccdd0
DH
3631 case FTO_MESSAGE_DIGEST:
3632 return "message-digest";
02ca1b45 3633
a55ccdd0
DH
3634 case FTO_POTENTIAL_EXPIRY:
3635 return "potential-expiry";
02ca1b45 3636
a55ccdd0
DH
3637 case FTO_PROTOCOL_VERSION:
3638 return "protocol-version";
02ca1b45 3639
a55ccdd0
DH
3640 case FTO_RECEIVE_TIMER:
3641 return "receive-timer";
02ca1b45 3642
a55ccdd0
DH
3643 case FTO_REJECT_REASON:
3644 return "reject-reason";
02ca1b45 3645
a55ccdd0
DH
3646 case FTO_RELATIONSHIP_NAME:
3647 return "relationship-name";
02ca1b45 3648
a55ccdd0
DH
3649 case FTO_REPLY_OPTIONS:
3650 return "reply-options";
02ca1b45 3651
a55ccdd0
DH
3652 case FTO_REQUEST_OPTIONS:
3653 return "request-options";
02ca1b45 3654
a55ccdd0
DH
3655 case FTO_SERVER_FLAGS:
3656 return "server-flags";
02ca1b45 3657
a55ccdd0
DH
3658 case FTO_SERVER_STATE:
3659 return "server-state";
3660
3661 case FTO_STOS:
3662 return "stos";
02ca1b45 3663
a55ccdd0 3664 case FTO_TLS_REPLY:
02ca1b45
TL
3665 return "tls-reply";
3666
a55ccdd0
DH
3667 case FTO_TLS_REQUEST:
3668 return "tls-request";
02ca1b45 3669
a55ccdd0
DH
3670 case FTO_VENDOR_CLASS:
3671 return "vendor-class";
02ca1b45 3672
a55ccdd0
DH
3673 case FTO_VENDOR_OPTIONS:
3674 return "vendor-options";
3675
3676 default:
3677 sprintf(optbuf, "unknown-option-%u", type);
3678 return optbuf;
9f3da356
TL
3679 }
3680}
3681
8c8e27c5
TL
3682failover_option_t *dhcp_failover_option_printf (unsigned code,
3683 char *obuf,
3684 unsigned *obufix,
3685 unsigned obufmax,
007e3ee4
TL
3686 const char *fmt, ...)
3687{
8c8e27c5
TL
3688 va_list va;
3689 char tbuf [256];
3690
98311e4b
DH
3691 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3692 * It is unclear what the effects of truncation here are, or
3693 * how that condition should be handled. It seems that this
3694 * function is used for formatting messages in the failover
3695 * command channel. For now the safest thing is for
3696 * overflow-truncation to cause a fatal log.
3697 */
8c8e27c5 3698 va_start (va, fmt);
98311e4b
DH
3699 if (vsnprintf (tbuf, sizeof tbuf, fmt, va) >= sizeof tbuf)
3700 log_fatal ("%s: vsnprintf would truncate",
3701 "dhcp_failover_make_option");
8c8e27c5
TL
3702 va_end (va);
3703
3704 return dhcp_failover_make_option (code, obuf, obufix, obufmax,
3705 strlen (tbuf), tbuf);
3706}
3707
d8a417b2
TL
3708failover_option_t *dhcp_failover_make_option (unsigned code,
3709 char *obuf, unsigned *obufix,
3710 unsigned obufmax, ...)
3711{
3712 va_list va;
3713 struct failover_option_info *info;
3714 int i;
3715 unsigned size, count;
3716 unsigned val;
85a53735 3717 u_int8_t *iaddr;
98311e4b 3718 unsigned ilen = 0;
d8a417b2 3719 u_int8_t *bval;
98311e4b 3720 char *txt = NULL;
d8a417b2
TL
3721#if defined (DEBUG_FAILOVER_MESSAGES)
3722 char tbuf [256];
3723#endif
8c8e27c5 3724
d8a417b2
TL
3725 /* Note that the failover_option structure is used differently on
3726 input than on output - on input, count is an element count, and
3727 on output it's the number of bytes total in the option, including
3728 the option code and option length. */
3729 failover_option_t option, *op;
3730
3731
3732 /* Bogus option code? */
3733 if (code < 1 || code > FTO_MAX || ft_options [code].type == FT_UNDEF) {
3734 return &null_failover_option;
3735 }
3736 info = &ft_options [code];
3737
3738 va_start (va, obufmax);
3739
3740 /* Get the number of elements and the size of the buffer we need
3741 to allocate. */
3742 if (info -> type == FT_DDNS || info -> type == FT_DDNS1) {
3743 count = info -> type == FT_DDNS ? 1 : 2;
3744 size = va_arg (va, int) + count;
3745 } else {
3746 /* Find out how many items in this list. */
3747 if (info -> num_present)
3748 count = info -> num_present;
3749 else
3750 count = va_arg (va, int);
3751
3752 /* Figure out size. */
3753 switch (info -> type) {
3754 case FT_UINT8:
3755 case FT_BYTES:
3756 case FT_DIGEST:
3757 size = count;
3758 break;
3759
3760 case FT_TEXT_OR_BYTES:
3761 case FT_TEXT:
8c8e27c5
TL
3762 txt = va_arg (va, char *);
3763 size = count;
d8a417b2
TL
3764 break;
3765
3766 case FT_IPADDR:
9f3da356
TL
3767 ilen = va_arg (va, unsigned);
3768 size = count * ilen;
8c8e27c5 3769 break;
9f3da356 3770
d8a417b2
TL
3771 case FT_UINT32:
3772 size = count * 4;
3773 break;
3774
3775 case FT_UINT16:
3776 size = count * 2;
3777 break;
3778
3779 default:
3780 /* shouldn't get here. */
3781 log_fatal ("bogus type in failover_make_option: %d",
3782 info -> type);
98311e4b 3783 return &null_failover_option;
d8a417b2
TL
3784 }
3785 }
3786
3787 size += 4;
3788
3789 /* Allocate a buffer for the option. */
3790 option.count = size;
4bd8800e 3791 option.data = dmalloc (option.count, MDL);
98311e4b
DH
3792 if (!option.data) {
3793 va_end (va);
d8a417b2 3794 return &null_failover_option;
98311e4b 3795 }
d8a417b2
TL
3796
3797 /* Put in the option code and option length. */
3798 putUShort (option.data, code);
9f3da356 3799 putUShort (&option.data [2], size - 4);
d8a417b2
TL
3800
3801#if defined (DEBUG_FAILOVER_MESSAGES)
98311e4b
DH
3802 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3803 * It is unclear what the effects of truncation here are, or
3804 * how that condition should be handled. It seems that this
3805 * message may be sent over the failover command channel.
3806 * For now the safest thing is for overflow-truncation to cause
3807 * a fatal log.
3808 */
3809 if (snprintf (tbuf, sizeof tbuf, " (%s<%d>", info -> name,
3810 option.count) >= sizeof tbuf)
3811 log_fatal ("dhcp_failover_make_option: tbuf overflow");
d8a417b2
TL
3812 failover_print (obuf, obufix, obufmax, tbuf);
3813#endif
3814
3815 /* Now put in the data. */
3816 switch (info -> type) {
3817 case FT_UINT8:
3818 for (i = 0; i < count; i++) {
3819 val = va_arg (va, unsigned);
3820#if defined (DEBUG_FAILOVER_MESSAGES)
98311e4b 3821 /* %Audit% Cannot exceed 24 bytes. %2004.06.17,Safe% */
d8a417b2
TL
3822 sprintf (tbuf, " %d", val);
3823 failover_print (obuf, obufix, obufmax, tbuf);
3824#endif
3825 option.data [i + 4] = val;
3826 }
3827 break;
3828
3829 case FT_IPADDR:
3830 for (i = 0; i < count; i++) {
85a53735
TL
3831 iaddr = va_arg (va, u_int8_t *);
3832 if (ilen != 4) {
4bd8800e 3833 dfree (option.data, MDL);
d8a417b2 3834 log_error ("IP addrlen=%d, should be 4.",
85a53735 3835 ilen);
98311e4b 3836 va_end (va);
d8a417b2
TL
3837 return &null_failover_option;
3838 }
3839
3840#if defined (DEBUG_FAILOVER_MESSAGES)
98311e4b
DH
3841 /*%Audit% Cannot exceed 17 bytes. %2004.06.17,Safe%*/
3842 sprintf (tbuf, " %u.%u.%u.%u",
3843 iaddr [0], iaddr [1], iaddr [2], iaddr [3]);
d8a417b2
TL
3844 failover_print (obuf, obufix, obufmax, tbuf);
3845#endif
9f3da356 3846 memcpy (&option.data [4 + i * ilen], iaddr, ilen);
d8a417b2
TL
3847 }
3848 break;
3849
3850 case FT_UINT32:
3851 for (i = 0; i < count; i++) {
3852 val = va_arg (va, unsigned);
3853#if defined (DEBUG_FAILOVER_MESSAGES)
98311e4b 3854 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
d8a417b2
TL
3855 sprintf (tbuf, " %d", val);
3856 failover_print (obuf, obufix, obufmax, tbuf);
3857#endif
3858 putULong (&option.data [4 + i * 4], val);
3859 }
3860 break;
3861
3862 case FT_BYTES:
3863 case FT_DIGEST:
3864 bval = va_arg (va, u_int8_t *);
3865#if defined (DEBUG_FAILOVER_MESSAGES)
3866 for (i = 0; i < count; i++) {
98311e4b 3867 /* 23 bytes plus nul, safe. */
d8a417b2
TL
3868 sprintf (tbuf, " %d", bval [i]);
3869 failover_print (obuf, obufix, obufmax, tbuf);
3870 }
3871#endif
3872 memcpy (&option.data [4], bval, count);
3873 break;
3874
3875 /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
98311e4b
DH
3876 terminated. Note that the caller should be careful not
3877 to provide a format and data that amount to more than 256
3878 bytes of data, since it will cause a fatal error. */
d8a417b2
TL
3879 case FT_TEXT_OR_BYTES:
3880 case FT_TEXT:
3881#if defined (DEBUG_FAILOVER_MESSAGES)
98311e4b
DH
3882 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3883 * It is unclear what the effects of truncation here are, or
3884 * how that condition should be handled. It seems that this
3885 * function is used for formatting messages in the failover
3886 * command channel. For now the safest thing is for
3887 * overflow-truncation to cause a fatal log.
3888 */
3889 if (snprintf (tbuf, sizeof tbuf, "\"%s\"", txt) >= sizeof tbuf)
3890 log_fatal ("dhcp_failover_make_option: tbuf overflow");
d8a417b2 3891 failover_print (obuf, obufix, obufmax, tbuf);
d8a417b2 3892#endif
8c8e27c5 3893 memcpy (&option.data [4], txt, count);
d8a417b2
TL
3894 break;
3895
3896 case FT_DDNS:
3897 case FT_DDNS1:
3898 option.data [4] = va_arg (va, unsigned);
3899 if (count == 2)
3900 option.data [5] = va_arg (va, unsigned);
3901 bval = va_arg (va, u_int8_t *);
3902 memcpy (&option.data [4 + count], bval, size - count - 4);
3903#if defined (DEBUG_FAILOVER_MESSAGES)
3904 for (i = 4; i < size; i++) {
98311e4b 3905 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
d8a417b2
TL
3906 sprintf (tbuf, " %d", option.data [i]);
3907 failover_print (obuf, obufix, obufmax, tbuf);
3908 }
3909#endif
3910 break;
3911
3912 case FT_UINT16:
3913 for (i = 0; i < count; i++) {
3914 val = va_arg (va, u_int32_t);
3915#if defined (DEBUG_FAILOVER_MESSAGES)
98311e4b 3916 /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
d8a417b2
TL
3917 sprintf (tbuf, " %d", val);
3918 failover_print (obuf, obufix, obufmax, tbuf);
3919#endif
3920 putUShort (&option.data [4 + i * 2], val);
3921 }
3922 break;
3923
3924 case FT_UNDEF:
3925 default:
165bce70 3926 break;
d8a417b2
TL
3927 }
3928
0096c639 3929#if defined DEBUG_FAILOVER_MESSAGES
d8a417b2 3930 failover_print (obuf, obufix, obufmax, ")");
0096c639 3931#endif
98311e4b 3932 va_end (va);
d8a417b2
TL
3933
3934 /* Now allocate a place to store what we just set up. */
4bd8800e 3935 op = dmalloc (sizeof (failover_option_t), MDL);
d8a417b2 3936 if (!op) {
4bd8800e 3937 dfree (option.data, MDL);
d8a417b2
TL
3938 return &null_failover_option;
3939 }
3940
3941 *op = option;
3942 return op;
3943}
3944
3945/* Send a failover message header. */
3946
3947isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link,
3948 omapi_object_t *connection,
a55ccdd0 3949 int msg_type, u_int32_t xid, ...)
d8a417b2 3950{
d8a417b2
TL
3951 unsigned size = 0;
3952 int bad_option = 0;
3953 int opix = 0;
3954 va_list list;
3955 failover_option_t *option;
3956 unsigned char *opbuf;
3957 isc_result_t status = ISC_R_SUCCESS;
3958 unsigned char cbuf;
3959
3960 /* Run through the argument list once to compute the length of
3961 the option portion of the message. */
a55ccdd0 3962 va_start (list, xid);
d8a417b2 3963 while ((option = va_arg (list, failover_option_t *))) {
dd53dc5a
TL
3964 if (option != &skip_failover_option)
3965 size += option -> count;
d8a417b2
TL
3966 if (option == &null_failover_option)
3967 bad_option = 1;
3968 }
3969 va_end (list);
3970
3971 /* Allocate an option buffer, unless we got an error. */
61aaca12 3972 if (!bad_option && size) {
4bd8800e 3973 opbuf = dmalloc (size, MDL);
d8a417b2
TL
3974 if (!opbuf)
3975 status = ISC_R_NOMEMORY;
61aaca12
TL
3976 } else
3977 opbuf = (unsigned char *)0;
d8a417b2 3978
a55ccdd0 3979 va_start (list, xid);
d8a417b2 3980 while ((option = va_arg (list, failover_option_t *))) {
dd53dc5a
TL
3981 if (option == &skip_failover_option)
3982 continue;
d8a417b2
TL
3983 if (!bad_option && opbuf)
3984 memcpy (&opbuf [opix],
3985 option -> data, option -> count);
85a53735
TL
3986 if (option != &null_failover_option &&
3987 option != &skip_failover_option) {
3988 opix += option -> count;
3989 dfree (option -> data, MDL);
3990 dfree (option, MDL);
3991 }
d8a417b2 3992 }
a55ccdd0 3993 va_end(list);
d8a417b2 3994
9f3da356
TL
3995 if (bad_option)
3996 return ISC_R_INVALIDARG;
d8a417b2
TL
3997
3998 /* Now send the message header. */
3999
4000 /* Message length. */
4001 status = omapi_connection_put_uint16 (connection, size + 12);
4002 if (status != ISC_R_SUCCESS)
4003 goto err;
4004
4005 /* Message type. */
4006 cbuf = msg_type;
4007 status = omapi_connection_copyin (connection, &cbuf, 1);
4008 if (status != ISC_R_SUCCESS)
4009 goto err;
4010
4011 /* Payload offset. */
4012 cbuf = 12;
4013 status = omapi_connection_copyin (connection, &cbuf, 1);
4014 if (status != ISC_R_SUCCESS)
4015 goto err;
4016
4017 /* Current time. */
4018 status = omapi_connection_put_uint32 (connection, (u_int32_t)cur_time);
4019 if (status != ISC_R_SUCCESS)
4020 goto err;
4021
4022 /* Transaction ID. */
a55ccdd0 4023 status = omapi_connection_put_uint32(connection, xid);
d8a417b2
TL
4024 if (status != ISC_R_SUCCESS)
4025 goto err;
4026
d8a417b2 4027 /* Payload. */
61aaca12
TL
4028 if (opbuf) {
4029 status = omapi_connection_copyin (connection, opbuf, size);
4030 if (status != ISC_R_SUCCESS)
4031 goto err;
4032 dfree (opbuf, MDL);
4033 }
6f0b9ed0 4034 if (link -> state_object &&
da292833 4035 link -> state_object -> link_to_peer == link) {
0db87765
TL
4036#if defined (DEBUG_FAILOVER_TIMING)
4037 log_info ("add_timeout +%d %s",
4038 (int)(link -> state_object ->
4039 partner.max_response_delay) / 3,
4040 "dhcp_failover_send_contact");
4041#endif
6f0b9ed0 4042 add_timeout (cur_time +
02ca1b45
TL
4043 (int)(link -> state_object ->
4044 partner.max_response_delay) / 3,
6f0b9ed0
TL
4045 dhcp_failover_send_contact, link -> state_object,
4046 (tvref_t)dhcp_failover_state_reference,
4047 (tvunref_t)dhcp_failover_state_dereference);
da292833 4048 }
d8a417b2
TL
4049 return status;
4050
4051 err:
61aaca12
TL
4052 if (opbuf)
4053 dfree (opbuf, MDL);
02ca1b45 4054 log_info ("dhcp_failover_put_message: something went wrong.");
d8a417b2
TL
4055 omapi_disconnect (connection, 1);
4056 return status;
9f3da356 4057}
d8a417b2 4058
6f0b9ed0
TL
4059void dhcp_failover_timeout (void *vstate)
4060{
4061 dhcp_failover_state_t *state = vstate;
4062 dhcp_failover_link_t *link;
6f0b9ed0 4063
0db87765
TL
4064#if defined (DEBUG_FAILOVER_TIMING)
4065 log_info ("dhcp_failover_timeout");
4066#endif
4067
6f0b9ed0
TL
4068 if (!state || state -> type != dhcp_type_failover_state)
4069 return;
6f0b9ed0
TL
4070 link = state -> link_to_peer;
4071 if (!link ||
4072 !link -> outer ||
4073 link -> outer -> type != omapi_type_connection)
4074 return;
4075
4076 log_error ("timeout waiting for failover peer %s", state -> name);
4077
4078 /* If we haven't gotten a timely response, blow away the connection.
4079 This will cause the state to change automatically. */
4080 omapi_disconnect (link -> outer, 1);
4081}
4082
4083void dhcp_failover_send_contact (void *vstate)
4084{
4085 dhcp_failover_state_t *state = vstate;
4086 dhcp_failover_link_t *link;
4087 isc_result_t status;
4088
4089#if defined (DEBUG_FAILOVER_MESSAGES)
4090 char obuf [64];
4091 unsigned obufix = 0;
4092
4093# define FMA obuf, &obufix, sizeof obuf
4094 failover_print (FMA, "(contact");
4095#else
00c272ee 4096# define FMA (char *)0, (unsigned *)0, 0
6f0b9ed0
TL
4097#endif
4098
0db87765
TL
4099#if defined (DEBUG_FAILOVER_TIMING)
4100 log_info ("dhcp_failover_send_contact");
4101#endif
4102
6f0b9ed0
TL
4103 if (!state || state -> type != dhcp_type_failover_state)
4104 return;
4105 link = state -> link_to_peer;
4106 if (!link ||
4107 !link -> outer ||
4108 link -> outer -> type != omapi_type_connection)
4109 return;
4110
4111 status = (dhcp_failover_put_message
4112 (link, link -> outer,
a55ccdd0 4113 FTM_CONTACT, link->xid++,
02ca1b45
TL
4114 (failover_option_t *)0));
4115
4116#if defined (DEBUG_FAILOVER_MESSAGES)
4117 if (status != ISC_R_SUCCESS)
4118 failover_print (FMA, " (failed)");
4119 failover_print (FMA, ")");
4120 if (obufix) {
4121 log_debug ("%s", obuf);
4122 }
4123#endif
4124 return;
4125}
4126
4127isc_result_t dhcp_failover_send_state (dhcp_failover_state_t *state)
4128{
4129 dhcp_failover_link_t *link;
4130 isc_result_t status;
4131
4132#if defined (DEBUG_FAILOVER_MESSAGES)
4133 char obuf [64];
4134 unsigned obufix = 0;
4135
4136# define FMA obuf, &obufix, sizeof obuf
4137 failover_print (FMA, "(state");
4138#else
4139# define FMA (char *)0, (unsigned *)0, 0
4140#endif
4141
4142 if (!state || state -> type != dhcp_type_failover_state)
4143 return ISC_R_INVALIDARG;
4144 link = state -> link_to_peer;
4145 if (!link ||
4146 !link -> outer ||
4147 link -> outer -> type != omapi_type_connection)
4148 return ISC_R_INVALIDARG;
4149
4150 status = (dhcp_failover_put_message
4151 (link, link -> outer,
a55ccdd0 4152 FTM_STATE, link->xid++,
5f0c7be1 4153 dhcp_failover_make_option (FTO_SERVER_STATE, FMA,
02ca1b45
TL
4154 (state -> me.state == startup
4155 ? state -> saved_state
4156 : state -> me.state)),
5f0c7be1
TL
4157 dhcp_failover_make_option
4158 (FTO_SERVER_FLAGS, FMA,
4159 (state -> service_state == service_startup
a55ccdd0 4160 ? FTF_SERVER_STARTUP : 0)),
02ca1b45 4161 dhcp_failover_make_option (FTO_STOS, FMA, state -> me.stos),
6f0b9ed0
TL
4162 (failover_option_t *)0));
4163
4164#if defined (DEBUG_FAILOVER_MESSAGES)
4165 if (status != ISC_R_SUCCESS)
4166 failover_print (FMA, " (failed)");
4167 failover_print (FMA, ")");
4168 if (obufix) {
4169 log_debug ("%s", obuf);
4170 }
4171#endif
02ca1b45 4172 return ISC_R_SUCCESS;
6f0b9ed0
TL
4173}
4174
d8a417b2
TL
4175/* Send a connect message. */
4176
4177isc_result_t dhcp_failover_send_connect (omapi_object_t *l)
4178{
4179 dhcp_failover_link_t *link;
4180 dhcp_failover_state_t *state;
4181 isc_result_t status;
4182#if defined (DEBUG_FAILOVER_MESSAGES)
4183 char obuf [64];
4184 unsigned obufix = 0;
4185
4186# define FMA obuf, &obufix, sizeof obuf
4187 failover_print (FMA, "(connect");
4188#else
00c272ee 4189# define FMA (char *)0, (unsigned *)0, 0
d8a417b2
TL
4190#endif
4191
d8a417b2
TL
4192 if (!l || l -> type != dhcp_type_failover_link)
4193 return ISC_R_INVALIDARG;
4194 link = (dhcp_failover_link_t *)l;
4195 state = link -> state_object;
4196 if (!l -> outer || l -> outer -> type != omapi_type_connection)
4197 return ISC_R_INVALIDARG;
4198
02ca1b45
TL
4199 status =
4200 (dhcp_failover_put_message
4201 (link, l -> outer,
a55ccdd0
DH
4202 FTM_CONNECT, link->xid++,
4203 dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA,
4204 strlen(state->name), state->name),
02ca1b45
TL
4205 dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
4206 state -> me.max_flying_updates),
4207 dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
4208 state -> me.max_response_delay),
fe5b0fdd
DH
4209 dhcp_failover_option_printf(FTO_VENDOR_CLASS, FMA,
4210 "isc-%s", PACKAGE_VERSION),
02ca1b45
TL
4211 dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
4212 DHCP_FAILOVER_VERSION),
4213 dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
4214 0, 0),
4215 dhcp_failover_make_option (FTO_MCLT, FMA,
4216 state -> mclt),
4217 (state -> hba
4218 ? dhcp_failover_make_option (FTO_HBA, FMA, 32, state -> hba)
4219 : &skip_failover_option),
4220 (failover_option_t *)0));
4221
d8a417b2
TL
4222#if defined (DEBUG_FAILOVER_MESSAGES)
4223 if (status != ISC_R_SUCCESS)
4224 failover_print (FMA, " (failed)");
4225 failover_print (FMA, ")");
4226 if (obufix) {
4227 log_debug ("%s", obuf);
4228 }
4229#endif
4230 return status;
4231}
4232
007e3ee4
TL
4233isc_result_t dhcp_failover_send_connectack (omapi_object_t *l,
4234 dhcp_failover_state_t *state,
4235 int reason, const char *errmsg)
9f3da356
TL
4236{
4237 dhcp_failover_link_t *link;
9f3da356
TL
4238 isc_result_t status;
4239#if defined (DEBUG_FAILOVER_MESSAGES)
4240 char obuf [64];
4241 unsigned obufix = 0;
4242
4243# define FMA obuf, &obufix, sizeof obuf
4244 failover_print (FMA, "(connectack");
4245#else
00c272ee 4246# define FMA (char *)0, (unsigned *)0, 0
9f3da356
TL
4247#endif
4248
4249 if (!l || l -> type != dhcp_type_failover_link)
4250 return ISC_R_INVALIDARG;
4251 link = (dhcp_failover_link_t *)l;
9f3da356
TL
4252 if (!l -> outer || l -> outer -> type != omapi_type_connection)
4253 return ISC_R_INVALIDARG;
dd53dc5a 4254
02ca1b45
TL
4255 status =
4256 (dhcp_failover_put_message
4257 (link, l -> outer,
a55ccdd0
DH
4258 FTM_CONNECTACK, link->imsg->xid,
4259 state
4260 ? dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA,
4261 strlen(state->name), state->name)
4262 : (link->imsg->options_present & FTB_RELATIONSHIP_NAME)
4263 ? &link->imsg->relationship_name
4264 : &skip_failover_option,
4265 state
02ca1b45
TL
4266 ? dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
4267 state -> me.max_flying_updates)
a55ccdd0
DH
4268 : &skip_failover_option,
4269 state
02ca1b45
TL
4270 ? dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
4271 state -> me.max_response_delay)
a55ccdd0 4272 : &skip_failover_option,
fe5b0fdd
DH
4273 dhcp_failover_option_printf(FTO_VENDOR_CLASS, FMA,
4274 "isc-%s", PACKAGE_VERSION),
02ca1b45
TL
4275 dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
4276 DHCP_FAILOVER_VERSION),
a55ccdd0
DH
4277 (link->imsg->options_present & FTB_TLS_REQUEST)
4278 ? dhcp_failover_make_option(FTO_TLS_REPLY, FMA,
4279 0, 0)
4280 : &skip_failover_option,
4281 reason
02ca1b45
TL
4282 ? dhcp_failover_make_option (FTO_REJECT_REASON,
4283 FMA, reason)
a55ccdd0
DH
4284 : &skip_failover_option,
4285 (reason && errmsg)
02ca1b45
TL
4286 ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4287 strlen (errmsg), errmsg)
a55ccdd0 4288 : &skip_failover_option,
02ca1b45 4289 (failover_option_t *)0));
9f3da356
TL
4290
4291#if defined (DEBUG_FAILOVER_MESSAGES)
4292 if (status != ISC_R_SUCCESS)
4293 failover_print (FMA, " (failed)");
4294 failover_print (FMA, ")");
4295 if (obufix) {
4296 log_debug ("%s", obuf);
4297 }
4298#endif
4299 return status;
4300}
4301
4302isc_result_t dhcp_failover_send_disconnect (omapi_object_t *l,
4303 int reason,
4304 const char *message)
dd53dc5a
TL
4305{
4306 dhcp_failover_link_t *link;
4307 dhcp_failover_state_t *state;
4308 isc_result_t status;
4309#if defined (DEBUG_FAILOVER_MESSAGES)
4310 char obuf [64];
4311 unsigned obufix = 0;
4312
4313# define FMA obuf, &obufix, sizeof obuf
9f3da356 4314 failover_print (FMA, "(disconnect");
dd53dc5a 4315#else
00c272ee 4316# define FMA (char *)0, (unsigned *)0, 0
dd53dc5a
TL
4317#endif
4318
9f3da356
TL
4319 if (!l || l -> type != dhcp_type_failover_link)
4320 return ISC_R_INVALIDARG;
4321 link = (dhcp_failover_link_t *)l;
4322 state = link -> state_object;
4323 if (!l -> outer || l -> outer -> type != omapi_type_connection)
dd53dc5a
TL
4324 return ISC_R_INVALIDARG;
4325
8c8e27c5
TL
4326 if (!message && reason)
4327 message = dhcp_failover_reject_reason_print (reason);
4328
9f3da356
TL
4329 status = (dhcp_failover_put_message
4330 (link, l -> outer,
a55ccdd0 4331 FTM_DISCONNECT, link->xid++,
9f3da356
TL
4332 dhcp_failover_make_option (FTO_REJECT_REASON,
4333 FMA, reason),
4334 (message
8c8e27c5
TL
4335 ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4336 strlen (message), message)
4337 : &skip_failover_option),
9f3da356
TL
4338 (failover_option_t *)0));
4339
4340#if defined (DEBUG_FAILOVER_MESSAGES)
4341 if (status != ISC_R_SUCCESS)
4342 failover_print (FMA, " (failed)");
4343 failover_print (FMA, ")");
4344 if (obufix) {
4345 log_debug ("%s", obuf);
4346 }
4347#endif
4348 return status;
4349}
4350
4351/* Send a Bind Update message. */
4352
4353isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state,
4354 struct lease *lease)
4355{
4356 dhcp_failover_link_t *link;
4357 isc_result_t status;
a55ccdd0
DH
4358 int flags = 0;
4359 binding_state_t transmit_state;
9f3da356
TL
4360#if defined (DEBUG_FAILOVER_MESSAGES)
4361 char obuf [64];
4362 unsigned obufix = 0;
4363
4364# define FMA obuf, &obufix, sizeof obuf
4365 failover_print (FMA, "(bndupd");
4366#else
00c272ee 4367# define FMA (char *)0, (unsigned *)0, 0
9f3da356
TL
4368#endif
4369
4370 if (!state -> link_to_peer ||
4371 state -> link_to_peer -> type != dhcp_type_failover_link)
dd53dc5a 4372 return ISC_R_INVALIDARG;
9f3da356 4373 link = (dhcp_failover_link_t *)state -> link_to_peer;
dd53dc5a
TL
4374
4375 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4376 return ISC_R_INVALIDARG;
4377
a55ccdd0
DH
4378 transmit_state = lease->desired_binding_state;
4379 if (lease->flags & RESERVED_LEASE) {
4380 /* If we are listing an allocable (not yet ACTIVE etc) lease
4381 * as reserved, toggle to the peer's 'free state', per the
4382 * draft. This gives the peer permission to alloc it to the
4383 * chaddr/uid-named client.
4384 */
4385 if ((state->i_am == primary) && (transmit_state == FTS_FREE))
4386 transmit_state = FTS_BACKUP;
4387 else if ((state->i_am == secondary) &&
4388 (transmit_state == FTS_BACKUP))
4389 transmit_state = FTS_FREE;
4390
4391 flags |= FTF_IP_FLAG_RESERVE;
4392 }
4393 if (lease->flags & BOOTP_LEASE)
4394 flags |= FTF_IP_FLAG_BOOTP;
4395
4396 /* last_xid == 0 is illegal, seek past zero if we hit it. */
4397 if (link->xid == 0)
4398 link->xid = 1;
4399
4400 lease->last_xid = link->xid++;
4401
8c8e27c5 4402 /* Send the update. */
dd53dc5a
TL
4403 status = (dhcp_failover_put_message
4404 (link, link -> outer,
a55ccdd0 4405 FTM_BNDUPD, lease->last_xid,
dd53dc5a 4406 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
85a53735 4407 lease -> ip_addr.len,
dd53dc5a
TL
4408 lease -> ip_addr.iabuf),
4409 dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
98311e4b 4410 lease -> desired_binding_state),
dd53dc5a
TL
4411 lease -> uid_len
4412 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
25a69ca1
TL
4413 lease -> uid_len,
4414 lease -> uid)
dd53dc5a 4415 : &skip_failover_option,
8c8e27c5
TL
4416 lease -> hardware_addr.hlen
4417 ? dhcp_failover_make_option (FTO_CHADDR, FMA,
4418 lease -> hardware_addr.hlen,
4419 lease -> hardware_addr.hbuf)
4420 : &skip_failover_option,
dd53dc5a
TL
4421 dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
4422 lease -> ends),
4423 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
4424 lease -> tstp),
4425 dhcp_failover_make_option (FTO_STOS, FMA,
4426 lease -> starts),
4427 dhcp_failover_make_option (FTO_CLTT, FMA,
4428 lease -> cltt),
a55ccdd0
DH
4429 flags ? dhcp_failover_make_option(FTO_IP_FLAGS, FMA,
4430 flags) :
4431 &skip_failover_option, /* No IP_FLAGS */
dd53dc5a
TL
4432 &skip_failover_option, /* XXX DDNS */
4433 &skip_failover_option, /* XXX request options */
4434 &skip_failover_option, /* XXX reply options */
4435 (failover_option_t *)0));
4436
4437#if defined (DEBUG_FAILOVER_MESSAGES)
4438 if (status != ISC_R_SUCCESS)
4439 failover_print (FMA, " (failed)");
4440 failover_print (FMA, ")");
4441 if (obufix) {
4442 log_debug ("%s", obuf);
4443 }
4444#endif
4445 return status;
4446}
4447
8c8e27c5
TL
4448/* Send a Bind ACK message. */
4449
4450isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *state,
8c8e27c5
TL
4451 failover_message_t *msg,
4452 int reason, const char *message)
4453{
4454 dhcp_failover_link_t *link;
4455 isc_result_t status;
4456#if defined (DEBUG_FAILOVER_MESSAGES)
4457 char obuf [64];
4458 unsigned obufix = 0;
8c8e27c5
TL
4459
4460# define FMA obuf, &obufix, sizeof obuf
4461 failover_print (FMA, "(bndack");
4462#else
00c272ee 4463# define FMA (char *)0, (unsigned *)0, 0
8c8e27c5
TL
4464#endif
4465
4466 if (!state -> link_to_peer ||
4467 state -> link_to_peer -> type != dhcp_type_failover_link)
4468 return ISC_R_INVALIDARG;
4469 link = (dhcp_failover_link_t *)state -> link_to_peer;
4470
4471 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4472 return ISC_R_INVALIDARG;
4473
8c8e27c5
TL
4474 if (!message && reason)
4475 message = dhcp_failover_reject_reason_print (reason);
4476
4477 /* Send the update. */
4478 status = (dhcp_failover_put_message
4479 (link, link -> outer,
a55ccdd0 4480 FTM_BNDACK, msg->xid,
8c8e27c5 4481 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
6f0b9ed0
TL
4482 sizeof msg -> assigned_addr,
4483 &msg -> assigned_addr),
a55ccdd0 4484#ifdef DO_BNDACK_SHOULD_NOT
8c8e27c5 4485 dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
6f0b9ed0
TL
4486 msg -> binding_status),
4487 (msg -> options_present & FTB_CLIENT_IDENTIFIER)
8c8e27c5 4488 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
6f0b9ed0
TL
4489 msg -> client_identifier.count,
4490 msg -> client_identifier.data)
8c8e27c5 4491 : &skip_failover_option,
6f0b9ed0 4492 (msg -> options_present & FTB_CHADDR)
8c8e27c5 4493 ? dhcp_failover_make_option (FTO_CHADDR, FMA,
6f0b9ed0
TL
4494 msg -> chaddr.count,
4495 msg -> chaddr.data)
8c8e27c5
TL
4496 : &skip_failover_option,
4497 dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
4498 msg -> expiry),
4499 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
4500 msg -> potential_expiry),
4501 dhcp_failover_make_option (FTO_STOS, FMA,
4502 msg -> stos),
4503 dhcp_failover_make_option (FTO_CLTT, FMA,
a55ccdd0
DH
4504 msg -> cltt),
4505 ((msg->options_present & FTB_IP_FLAGS) && msg->ip_flags) ?
4506 dhcp_failover_make_option(FTO_IP_FLAGS, FMA,
4507 msg->ip_flags)
4508 : &skip_failover_option,
4509#endif /* DO_BNDACK_SHOULD_NOT */
8c8e27c5 4510 reason
a55ccdd0
DH
4511 ? dhcp_failover_make_option(FTO_REJECT_REASON, FMA, reason)
4512 : &skip_failover_option,
4513 (reason && message)
8c8e27c5
TL
4514 ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4515 strlen (message), message)
a55ccdd0
DH
4516 : &skip_failover_option,
4517#ifdef DO_BNDACK_SHOULD_NOT
8c8e27c5
TL
4518 &skip_failover_option, /* XXX DDNS */
4519 &skip_failover_option, /* XXX request options */
4520 &skip_failover_option, /* XXX reply options */
a55ccdd0 4521#endif /* DO_BNDACK_SHOULD_NOT */
8c8e27c5
TL
4522 (failover_option_t *)0));
4523
4524#if defined (DEBUG_FAILOVER_MESSAGES)
4525 if (status != ISC_R_SUCCESS)
4526 failover_print (FMA, " (failed)");
4527 failover_print (FMA, ")");
4528 if (obufix) {
4529 log_debug ("%s", obuf);
4530 }
4531#endif
4532 return status;
4533}
4534
4535isc_result_t dhcp_failover_send_poolreq (dhcp_failover_state_t *state)
4536{
4537 dhcp_failover_link_t *link;
4538 isc_result_t status;
4539#if defined (DEBUG_FAILOVER_MESSAGES)
4540 char obuf [64];
4541 unsigned obufix = 0;
4542
4543# define FMA obuf, &obufix, sizeof obuf
4544 failover_print (FMA, "(poolreq");
4545#else
00c272ee 4546# define FMA (char *)0, (unsigned *)0, 0
8c8e27c5
TL
4547#endif
4548
4549 if (!state -> link_to_peer ||
4550 state -> link_to_peer -> type != dhcp_type_failover_link)
4551 return ISC_R_INVALIDARG;
4552 link = (dhcp_failover_link_t *)state -> link_to_peer;
4553
4554 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4555 return ISC_R_INVALIDARG;
4556
4557 status = (dhcp_failover_put_message
4558 (link, link -> outer,
a55ccdd0 4559 FTM_POOLREQ, link->xid++,
8c8e27c5
TL
4560 (failover_option_t *)0));
4561
4562#if defined (DEBUG_FAILOVER_MESSAGES)
4563 if (status != ISC_R_SUCCESS)
4564 failover_print (FMA, " (failed)");
4565 failover_print (FMA, ")");
4566 if (obufix) {
4567 log_debug ("%s", obuf);
4568 }
4569#endif
4570 return status;
4571}
4572
4573isc_result_t dhcp_failover_send_poolresp (dhcp_failover_state_t *state,
4574 int leases)
4575{
4576 dhcp_failover_link_t *link;
4577 isc_result_t status;
4578#if defined (DEBUG_FAILOVER_MESSAGES)
4579 char obuf [64];
4580 unsigned obufix = 0;
4581
4582# define FMA obuf, &obufix, sizeof obuf
007e3ee4 4583 failover_print (FMA, "(poolresp");
8c8e27c5 4584#else
00c272ee 4585# define FMA (char *)0, (unsigned *)0, 0
8c8e27c5
TL
4586#endif
4587
4588 if (!state -> link_to_peer ||
4589 state -> link_to_peer -> type != dhcp_type_failover_link)
4590 return ISC_R_INVALIDARG;
4591 link = (dhcp_failover_link_t *)state -> link_to_peer;
4592
4593 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4594 return ISC_R_INVALIDARG;
4595
4596 status = (dhcp_failover_put_message
4597 (link, link -> outer,
a55ccdd0 4598 FTM_POOLRESP, link->imsg->xid,
8c8e27c5
TL
4599 dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED, FMA,
4600 leases),
4601 (failover_option_t *)0));
4602
4603#if defined (DEBUG_FAILOVER_MESSAGES)
4604 if (status != ISC_R_SUCCESS)
4605 failover_print (FMA, " (failed)");
4606 failover_print (FMA, ")");
4607 if (obufix) {
4608 log_debug ("%s", obuf);
4609 }
4610#endif
4611 return status;
4612}
4613
5f0c7be1
TL
4614isc_result_t dhcp_failover_send_update_request (dhcp_failover_state_t *state)
4615{
4616 dhcp_failover_link_t *link;
4617 isc_result_t status;
4618#if defined (DEBUG_FAILOVER_MESSAGES)
4619 char obuf [64];
4620 unsigned obufix = 0;
988c1ca7 4621
5f0c7be1
TL
4622# define FMA obuf, &obufix, sizeof obuf
4623 failover_print (FMA, "(updreq");
4624#else
4625# define FMA (char *)0, (unsigned *)0, 0
4626#endif
4627
4628 if (!state -> link_to_peer ||
4629 state -> link_to_peer -> type != dhcp_type_failover_link)
4630 return ISC_R_INVALIDARG;
4631 link = (dhcp_failover_link_t *)state -> link_to_peer;
4632
4633 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4634 return ISC_R_INVALIDARG;
4635
98311e4b
DH
4636 if (state -> curUPD)
4637 return ISC_R_ALREADYRUNNING;
4638
5f0c7be1
TL
4639 status = (dhcp_failover_put_message
4640 (link, link -> outer,
a55ccdd0 4641 FTM_UPDREQ, link->xid++,
5f0c7be1
TL
4642 (failover_option_t *)0));
4643
98311e4b
DH
4644 if (status == ISC_R_SUCCESS)
4645 state -> curUPD = FTM_UPDREQ;
4646
5f0c7be1
TL
4647#if defined (DEBUG_FAILOVER_MESSAGES)
4648 if (status != ISC_R_SUCCESS)
4649 failover_print (FMA, " (failed)");
4650 failover_print (FMA, ")");
4651 if (obufix) {
4652 log_debug ("%s", obuf);
4653 }
4654#endif
98311e4b 4655 log_info ("Sent update request message to %s", state -> name);
5f0c7be1
TL
4656 return status;
4657}
4658
4659isc_result_t dhcp_failover_send_update_request_all (dhcp_failover_state_t
4660 *state)
4661{
4662 dhcp_failover_link_t *link;
4663 isc_result_t status;
4664#if defined (DEBUG_FAILOVER_MESSAGES)
4665 char obuf [64];
4666 unsigned obufix = 0;
4667
4668# define FMA obuf, &obufix, sizeof obuf
4669 failover_print (FMA, "(updreqall");
4670#else
4671# define FMA (char *)0, (unsigned *)0, 0
4672#endif
4673
4674 if (!state -> link_to_peer ||
4675 state -> link_to_peer -> type != dhcp_type_failover_link)
4676 return ISC_R_INVALIDARG;
4677 link = (dhcp_failover_link_t *)state -> link_to_peer;
4678
4679 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4680 return ISC_R_INVALIDARG;
4681
98311e4b
DH
4682 /* If there is an UPDREQ in progress, then upgrade to UPDREQALL. */
4683 if (state -> curUPD && (state -> curUPD != FTM_UPDREQ))
4684 return ISC_R_ALREADYRUNNING;
4685
5f0c7be1
TL
4686 status = (dhcp_failover_put_message
4687 (link, link -> outer,
a55ccdd0 4688 FTM_UPDREQALL, link->xid++,
5f0c7be1
TL
4689 (failover_option_t *)0));
4690
98311e4b
DH
4691 if (status == ISC_R_SUCCESS)
4692 state -> curUPD = FTM_UPDREQALL;
4693
5f0c7be1
TL
4694#if defined (DEBUG_FAILOVER_MESSAGES)
4695 if (status != ISC_R_SUCCESS)
4696 failover_print (FMA, " (failed)");
4697 failover_print (FMA, ")");
4698 if (obufix) {
4699 log_debug ("%s", obuf);
4700 }
4701#endif
98311e4b 4702 log_info ("Sent update request all message to %s", state -> name);
5f0c7be1
TL
4703 return status;
4704}
4705
4706isc_result_t dhcp_failover_send_update_done (dhcp_failover_state_t *state)
4707{
4708 dhcp_failover_link_t *link;
4709 isc_result_t status;
4710#if defined (DEBUG_FAILOVER_MESSAGES)
4711 char obuf [64];
4712 unsigned obufix = 0;
4713
4714# define FMA obuf, &obufix, sizeof obuf
4715 failover_print (FMA, "(upddone");
4716#else
4717# define FMA (char *)0, (unsigned *)0, 0
4718#endif
4719
4720 if (!state -> link_to_peer ||
4721 state -> link_to_peer -> type != dhcp_type_failover_link)
4722 return ISC_R_INVALIDARG;
4723 link = (dhcp_failover_link_t *)state -> link_to_peer;
4724
4725 if (!link -> outer || link -> outer -> type != omapi_type_connection)
4726 return ISC_R_INVALIDARG;
4727
4728 status = (dhcp_failover_put_message
4729 (link, link -> outer,
a55ccdd0 4730 FTM_UPDDONE, state->updxid,
5f0c7be1
TL
4731 (failover_option_t *)0));
4732
4733#if defined (DEBUG_FAILOVER_MESSAGES)
4734 if (status != ISC_R_SUCCESS)
4735 failover_print (FMA, " (failed)");
4736 failover_print (FMA, ")");
4737 if (obufix) {
4738 log_debug ("%s", obuf);
4739 }
4740#endif
988c1ca7 4741
98311e4b
DH
4742 log_info ("Sent update done message to %s", state -> name);
4743
a55ccdd0
DH
4744 state->updxid--; /* Paranoia, just so it mismatches. */
4745
988c1ca7
DN
4746 /* There may be uncommitted leases at this point (since
4747 dhcp_failover_process_bind_ack() doesn't commit leases);
4748 commit the lease file. */
4749 commit_leases();
4750
5f0c7be1
TL
4751 return status;
4752}
4753
8c8e27c5
TL
4754isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *state,
4755 failover_message_t *msg)
4756{
20916cae 4757 struct lease *lt, *lease;
8c8e27c5 4758 struct iaddr ia;
20916cae
TL
4759 int reason = FTR_MISC_REJECT;
4760 const char *message;
007e3ee4 4761 int new_binding_state;
2426234f 4762 int send_to_backup = 0;
8c8e27c5
TL
4763
4764 ia.len = sizeof msg -> assigned_addr;
4765 memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
4766
20916cae 4767 lease = (struct lease *)0;
6f0b9ed0 4768 lt = (struct lease *)0;
20916cae
TL
4769 if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
4770 message = "unknown IP address";
4771 reason = FTR_ILLEGAL_IP_ADDR;
4772 goto bad;
8c8e27c5
TL
4773 }
4774
4775 /* XXX check for conflicts. */
4776
4777 /* Install the new info. */
20916cae
TL
4778 if (!lease_copy (&lt, lease, MDL)) {
4779 message = "no memory";
4780 goto bad;
4781 }
8c8e27c5
TL
4782
4783 if (msg -> options_present & FTB_CHADDR) {
98311e4b
DH
4784 if (msg->binding_status == FTS_ABANDONED) {
4785 message = "BNDUPD to ABANDONED with a CHADDR";
4786 goto bad;
4787 }
20916cae 4788 if (msg -> chaddr.count > sizeof lt -> hardware_addr.hbuf) {
88cd8aca 4789 message = "chaddr too long";
20916cae 4790 goto bad;
8c8e27c5 4791 }
20916cae
TL
4792 lt -> hardware_addr.hlen = msg -> chaddr.count;
4793 memcpy (lt -> hardware_addr.hbuf, msg -> chaddr.data,
8c8e27c5 4794 msg -> chaddr.count);
98311e4b
DH
4795 } else if (msg->binding_status == FTS_ACTIVE ||
4796 msg->binding_status == FTS_EXPIRED ||
4797 msg->binding_status == FTS_RELEASED) {
4798 message = "BNDUPD without CHADDR";
4799 goto bad;
4800 } else if (msg->binding_status == FTS_ABANDONED) {
4801 lt->hardware_addr.hlen = 0;
4802 if (lt->scope)
4803 binding_scope_dereference(&lt->scope, MDL);
4804 }
4805
4806 /* There is no explicit message content to indicate that the client
4807 * supplied no client-identifier. So if we don't hear of a value,
4808 * we discard the last one.
4809 */
4810 if (msg->options_present & FTB_CLIENT_IDENTIFIER) {
4811 if (msg->binding_status == FTS_ABANDONED) {
4812 message = "BNDUPD to ABANDONED with client-id";
4813 goto bad;
4814 }
8c8e27c5 4815
98311e4b
DH
4816 lt->uid_len = msg->client_identifier.count;
4817
4818 /* Allocate the lt->uid buffer if we haven't already, or
4819 * re-allocate the lt-uid buffer if we have one that is not
4820 * large enough. Otherwise, just use the extant buffer.
4821 */
4822 if (!lt->uid || lt->uid == lt->uid_buf ||
4823 lt->uid_len > lt->uid_max) {
4824 if (lt->uid && lt->uid != lt->uid_buf)
4825 dfree(lt->uid, MDL);
4826
4827 if (lt->uid_len > sizeof(lt->uid_buf)) {
4828 lt->uid_max = lt->uid_len;
4829 lt->uid = dmalloc(lt->uid_len, MDL);
4830 if (!lt->uid) {
4831 message = "no memory";
4832 goto bad;
4833 }
4834 } else {
4835 lt->uid_max = sizeof(lt->uid_buf);
4836 lt->uid = lt->uid_buf;
20916cae 4837 }
8c8e27c5 4838 }
20916cae
TL
4839 memcpy (lt -> uid,
4840 msg -> client_identifier.data, lt -> uid_len);
98311e4b
DH
4841 } else if (lt->uid && msg->binding_status != FTS_RESET &&
4842 msg->binding_status != FTS_FREE &&
4843 msg->binding_status != FTS_BACKUP) {
4844 if (lt->uid != lt->uid_buf)
4845 dfree (lt->uid, MDL);
4846 lt->uid = NULL;
4847 lt->uid_max = lt->uid_len = 0;
8c8e27c5 4848 }
98311e4b
DH
4849
4850 /* If the lease was expired, also remove the stale binding scope. */
4851 if (lt->scope && lt->ends < cur_time)
4852 binding_scope_dereference(&lt->scope, MDL);
4853
8c8e27c5
TL
4854 /* XXX Times may need to be adjusted based on clock skew! */
4855 if (msg -> options_present & FTB_STOS) {
20916cae 4856 lt -> starts = msg -> stos;
8c8e27c5
TL
4857 }
4858 if (msg -> options_present & FTB_LEASE_EXPIRY) {
20916cae 4859 lt -> ends = msg -> expiry;
8c8e27c5
TL
4860 }
4861 if (msg -> options_present & FTB_CLTT) {
a55ccdd0 4862 lt -> cltt = msg -> cltt;
8c8e27c5 4863 }
88cd8aca
DH
4864 if (msg->options_present & FTB_POTENTIAL_EXPIRY) {
4865 lt->atsfp = lt->tsfp = msg->potential_expiry;
8c8e27c5 4866 }
a55ccdd0
DH
4867 if (msg->options_present & FTB_IP_FLAGS) {
4868 if (msg->ip_flags & FTF_IP_FLAG_RESERVE) {
4869 if ((((state->i_am == primary) &&
4870 (lease->binding_state == FTS_FREE)) ||
4871 ((state->i_am == secondary) &&
4872 (lease->binding_state == FTS_BACKUP))) &&
4873 !(lease->flags & RESERVED_LEASE)) {
4874 message = "Address is not reserved.";
4875 reason = FTR_IP_NOT_RESERVED;
4876 goto bad;
4877 }
4878
4879 lt->flags |= RESERVED_LEASE;
4880 } else
4881 lt->flags &= ~RESERVED_LEASE;
4882
4883 if (msg->ip_flags & FTF_IP_FLAG_BOOTP) {
4884 if ((((state->i_am == primary) &&
4885 (lease->binding_state == FTS_FREE)) ||
4886 ((state->i_am == secondary) &&
4887 (lease->binding_state == FTS_BACKUP))) &&
4888 !(lease->flags & BOOTP_LEASE)) {
4889 message = "Address is not allocated to BOOTP.";
4890 goto bad;
4891 }
4892 lt->flags |= BOOTP_LEASE;
4893 } else
4894 lt->flags &= ~BOOTP_LEASE;
4895
4896 if (msg->ip_flags & ~(FTF_IP_FLAG_RESERVE | FTF_IP_FLAG_BOOTP))
4897 log_info("Unknown IP-flags set in BNDUPD (0x%x).",
4898 msg->ip_flags);
4899 } else /* Flags may only not appear if the values are zero. */
4900 lt->flags &= ~(RESERVED_LEASE | BOOTP_LEASE);
8c8e27c5
TL
4901
4902 if (msg -> options_present & FTB_BINDING_STATUS) {
d758ad8c
TL
4903#if defined (DEBUG_LEASE_STATE_TRANSITIONS)
4904 log_info ("processing state transition for %s: %s to %s",
4905 piaddr (lease -> ip_addr),
4906 binding_state_print (lease -> binding_state),
4907 binding_state_print (msg -> binding_status));
4908#endif
4909
5f0c7be1
TL
4910 /* If we're in normal state, make sure the state transition
4911 we got is valid. */
02ca1b45 4912 if (state -> me.state == normal) {
da292833
TL
4913 new_binding_state =
4914 (normal_binding_state_transition_check
31bbee78
TL
4915 (lease, state, msg -> binding_status,
4916 msg -> potential_expiry));
d758ad8c
TL
4917 /* XXX if the transition the peer asked for isn't
4918 XXX allowed, maybe we should make the transition
4919 XXX into potential-conflict at this point. */
da292833
TL
4920 } else {
4921 new_binding_state =
4922 (conflict_binding_state_transition_check
31bbee78
TL
4923 (lease, state, msg -> binding_status,
4924 msg -> potential_expiry));
da292833
TL
4925 }
4926 if (new_binding_state != msg -> binding_status) {
4927 char outbuf [100];
98311e4b
DH
4928
4929 if (snprintf (outbuf, sizeof outbuf,
da292833
TL
4930 "%s: invalid state transition: %s to %s",
4931 piaddr (lease -> ip_addr),
4932 binding_state_print (lease -> binding_state),
98311e4b
DH
4933 binding_state_print (msg -> binding_status))
4934 >= sizeof outbuf)
4935 log_fatal ("%s: impossible outbuf overflow",
4936 "dhcp_failover_process_bind_update");
4937
da292833
TL
4938 dhcp_failover_send_bind_ack (state, msg,
4939 FTR_FATAL_CONFLICT,
4940 outbuf);
4941 goto out;
8c8e27c5 4942 }
d758ad8c
TL
4943 if (new_binding_state == FTS_EXPIRED ||
4944 new_binding_state == FTS_RELEASED ||
a55ccdd0 4945 new_binding_state == FTS_RESET) {
d758ad8c 4946 lt -> next_binding_state = FTS_FREE;
2426234f
DH
4947
4948 /* Mac address affinity. Assign the lease to
4949 * BACKUP state if we are the primary and the
4950 * peer is more likely to reallocate this lease
4951 * to a returning client.
4952 */
2b9ce770
DH
4953 if ((state->i_am == primary) &&
4954 !(lt->flags & (RESERVED_LEASE | BOOTP_LEASE)))
2426234f
DH
4955 send_to_backup = peer_wants_lease(lt);
4956 } else {
d758ad8c 4957 lt -> next_binding_state = new_binding_state;
2426234f 4958 }
d758ad8c 4959 msg -> binding_status = lt -> next_binding_state;
8c8e27c5
TL
4960 }
4961
4962 /* Try to install the new information. */
988c1ca7
DN
4963 if (!supersede_lease (lease, lt, 0, 0, 0) ||
4964 !write_lease (lease)) {
20916cae
TL
4965 message = "database update failed";
4966 bad:
988c1ca7 4967 dhcp_failover_send_bind_ack (state, msg, reason, message);
2426234f 4968 goto out;
d758ad8c 4969 } else {
988c1ca7 4970 dhcp_failover_queue_ack (state, msg);
d758ad8c 4971 }
988c1ca7 4972
2426234f
DH
4973 /* If it is probably wise, assign lease to backup state if the peer
4974 * is not already hoarding leases.
4975 */
808d45fd
DH
4976 if (send_to_backup && secondary_not_hoarding(state, lease->pool)) {
4977 lease->next_binding_state = FTS_BACKUP;
4978 lease->tstp = cur_time;
4979 lease->starts = cur_time;
2426234f 4980
808d45fd
DH
4981 if (!supersede_lease(lease, NULL, 0, 1, 0) ||
4982 !write_lease(lease))
2426234f 4983 log_error("can't commit lease %s for mac addr "
808d45fd 4984 "affinity", piaddr(lease->ip_addr));
2426234f
DH
4985
4986 dhcp_failover_send_updates(state);
4987 }
4988
54bdf6c7 4989 out:
20916cae
TL
4990 if (lt)
4991 lease_dereference (&lt, MDL);
6f0b9ed0
TL
4992 if (lease)
4993 lease_dereference (&lease, MDL);
8c8e27c5 4994
9f3da356
TL
4995 return ISC_R_SUCCESS;
4996}
4997
2426234f
DH
4998/* This was hairy enough I didn't want to do it all in an if statement.
4999 *
5000 * Returns: Truth is the secondary is allowed to get more leases based upon
5001 * MAC address affinity. False otherwise.
5002 */
5003static INLINE int
5004secondary_not_hoarding(dhcp_failover_state_t *state, struct pool *p) {
5005 int total;
5006 int hold;
5007 int lts;
5008
5009 total = p->free_leases + p->backup_leases;
5010
5011 /* How many leases is one side or the other allowed to "hold"? */
5012 hold = ((total * state->max_lease_ownership) + 50) / 100;
5013
5014 /* If we were to send leases (or if the secondary were to send us
5015 * leases in the negative direction), how many would that be?
5016 */
5017 lts = (p->free_leases - p->backup_leases) / 2;
5018
5019 /* The peer is not hoarding leases if we would send them more leases
5020 * (or they would take fewer leases) than the maximum they are allowed
5021 * to hold (the negative hold).
5022 */
5023 return(lts > -hold);
5024}
5025
8c8e27c5
TL
5026isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *state,
5027 failover_message_t *msg)
5028{
6f0b9ed0
TL
5029 struct lease *lt = (struct lease *)0;
5030 struct lease *lease = (struct lease *)0;
8c8e27c5 5031 struct iaddr ia;
20916cae 5032 const char *message = "no memory";
a55ccdd0 5033 u_int32_t pot_expire;
808d45fd 5034 int send_to_backup = ISC_FALSE;
8c8e27c5
TL
5035
5036 ia.len = sizeof msg -> assigned_addr;
5037 memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
5038
20916cae
TL
5039 if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
5040 message = "no such lease";
5041 goto bad;
8c8e27c5
TL
5042 }
5043
5044 /* XXX check for conflicts. */
6f0b9ed0
TL
5045 if (msg -> options_present & FTB_REJECT_REASON) {
5046 log_error ("bind update on %s from %s rejected: %.*s",
5047 piaddr (ia), state -> name,
5048 (int)((msg -> options_present & FTB_MESSAGE)
5049 ? msg -> message.count
5050 : strlen (dhcp_failover_reject_reason_print
5051 (msg -> reject_reason))),
5052 (msg -> options_present & FTB_MESSAGE)
5053 ? (const char *)(msg -> message.data)
5054 : (dhcp_failover_reject_reason_print
5055 (msg -> reject_reason)));
5056 goto unqueue;
5057 }
8c8e27c5 5058
808d45fd
DH
5059 /* Silently discard acks for leases we did not update (or multiple
5060 * acks).
5061 */
5062 if (!lease->last_xid)
5063 goto unqueue;
5064
5065 if (lease->last_xid != msg->xid) {
5066 message = "xid mismatch";
5067 goto bad;
5068 }
5069
8c8e27c5 5070 /* XXX Times may need to be adjusted based on clock skew! */
a55ccdd0
DH
5071 if (msg->options_present & FTO_POTENTIAL_EXPIRY)
5072 pot_expire = msg->potential_expiry;
808d45fd 5073 else
a55ccdd0 5074 pot_expire = lease->tstp;
a55ccdd0 5075
808d45fd
DH
5076 /* If the lease was desired to enter a binding state, we set
5077 * such a value upon transmitting a bndupd. We do not clear it
5078 * if we receive a bndupd in the meantime (or change the state
5079 * of the lease again ourselves), but we do set binding_state
5080 * if we get a bndupd.
5081 *
5082 * So desired_binding_state tells us what we sent a bndupd for,
5083 * and binding_state tells us what we have since determined in
5084 * the meantime.
5085 */
5086 if (lease->desired_binding_state == FTS_EXPIRED ||
5087 lease->desired_binding_state == FTS_RESET ||
5088 lease->desired_binding_state == FTS_RELEASED)
a55ccdd0 5089 {
808d45fd
DH
5090 /* It is not a problem to do this directly as we call
5091 * supersede_lease immediately after: the lease is requeued
5092 * even if its sort order (tsfp) has changed.
5093 */
d084a789 5094 lease->atsfp = lease->tsfp = pot_expire;
a55ccdd0
DH
5095 if ((state->i_am == secondary) &&
5096 (lease->flags & RESERVED_LEASE))
5097 lease->next_binding_state = FTS_BACKUP;
5098 else
5099 lease->next_binding_state = FTS_FREE;
808d45fd
DH
5100 /* Clear this condition for the next go-round. */
5101 lease->desired_binding_state = lease->next_binding_state;
a55ccdd0
DH
5102 supersede_lease(lease, (struct lease *)0, 0, 0, 0);
5103 write_lease(lease);
2426234f
DH
5104
5105 /* Lease has returned to FREE state from the
5106 * transitional states. If the lease 'belongs'
5107 * to a client that would be served by the
5108 * peer, process a binding update now to send
808d45fd
DH
5109 * the lease to backup state. But not if we
5110 * think we already have.
2426234f
DH
5111 */
5112 if (state->i_am == primary &&
2b9ce770 5113 !(lease->flags & (RESERVED_LEASE | BOOTP_LEASE)) &&
808d45fd
DH
5114 peer_wants_lease(lease))
5115 send_to_backup = ISC_TRUE;
2426234f 5116
808d45fd
DH
5117 if (!send_to_backup && state->me.state == normal)
5118 commit_leases();
a55ccdd0 5119 } else {
808d45fd
DH
5120 /* XXX It could be a problem to do this directly if the lease
5121 * XXX is sorted by tsfp.
5122 */
d084a789 5123 lease->atsfp = lease->tsfp = pot_expire;
a55ccdd0
DH
5124 if (lease->desired_binding_state != lease->binding_state) {
5125 lease->next_binding_state =
5126 lease->desired_binding_state;
5127 supersede_lease(lease,
5128 (struct lease *)0, 0, 0, 0);
31bbee78 5129 }
a55ccdd0
DH
5130 write_lease(lease);
5131 /* Commit the lease only after a two-second timeout,
5132 so that if we get a bunch of acks in quick
5133 successtion (e.g., when stealing leases from the
5134 secondary), we do not do an immediate commit for
5135 each one. */
5136 add_timeout(cur_time + 2,
5137 commit_leases_timeout, (void *)0, 0, 0);
8c8e27c5
TL
5138 }
5139
6f0b9ed0 5140 unqueue:
8c8e27c5
TL
5141 dhcp_failover_ack_queue_remove (state, lease);
5142
5f0c7be1
TL
5143 /* If we are supposed to send an update done after we send
5144 this lease, go ahead and send it. */
5145 if (state -> send_update_done == lease) {
5146 lease_dereference (&state -> send_update_done, MDL);
5147 dhcp_failover_send_update_done (state);
5148 }
5149
a512cc3a 5150 /* Now that the lease is off the ack queue, consider putting it
808d45fd
DH
5151 * back on the update queue for mac address affinity.
5152 */
5153 if (send_to_backup && secondary_not_hoarding(state, lease->pool)) {
5154 lease->next_binding_state = FTS_BACKUP;
5155 lease->tstp = lease->starts = cur_time;
5156
5157 if (!supersede_lease(lease, NULL, 0, 1, 0) ||
5158 !write_lease(lease))
5159 log_error("can't commit lease %s for "
5160 "client affinity", piaddr(lease->ip_addr));
5161
5162 if (state->me.state == normal)
5163 commit_leases();
5164 }
5165
8c8e27c5
TL
5166 /* If there are updates pending, we've created space to send at
5167 least one. */
5168 dhcp_failover_send_updates (state);
5169
20916cae
TL
5170 out:
5171 lease_dereference (&lease, MDL);
5172 if (lt)
5173 lease_dereference (&lt, MDL);
5174
9f3da356 5175 return ISC_R_SUCCESS;
20916cae
TL
5176
5177 bad:
808d45fd 5178 log_info ("bind update on %s got ack from %s: %s.",
20916cae
TL
5179 piaddr (ia), state -> name, message);
5180 goto out;
9f3da356
TL
5181}
5182
5f0c7be1
TL
5183isc_result_t dhcp_failover_generate_update_queue (dhcp_failover_state_t *state,
5184 int everythingp)
5185{
5186 struct shared_network *s;
5187 struct pool *p;
28868515 5188 struct lease *l;
5f0c7be1 5189 int i;
5f0c7be1
TL
5190#define FREE_LEASES 0
5191#define ACTIVE_LEASES 1
5192#define EXPIRED_LEASES 2
5193#define ABANDONED_LEASES 3
5194#define BACKUP_LEASES 4
616d67cb
DH
5195#define RESERVED_LEASES 5
5196 struct lease **lptr[RESERVED_LEASES+1];
5f0c7be1 5197
5f0c7be1
TL
5198 /* Loop through each pool in each shared network and call the
5199 expiry routine on the pool. */
5200 for (s = shared_networks; s; s = s -> next) {
5201 for (p = s -> pools; p; p = p -> next) {
88cd8aca
DH
5202 if (p->failover_peer != state)
5203 continue;
5204
616d67cb
DH
5205 lptr[FREE_LEASES] = &p->free;
5206 lptr[ACTIVE_LEASES] = &p->active;
5207 lptr[EXPIRED_LEASES] = &p->expired;
5208 lptr[ABANDONED_LEASES] = &p->abandoned;
5209 lptr[BACKUP_LEASES] = &p->backup;
5210 lptr[RESERVED_LEASES] = &p->reserved;
5f0c7be1 5211
616d67cb 5212 for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
5f0c7be1 5213 for (l = *(lptr [i]); l; l = l -> next) {
4c7ffd58 5214 if ((l->flags & ON_QUEUE) == 0 &&
616d67cb
DH
5215 (everythingp ||
5216 (l->tstp > l->atsfp) ||
5217 (i == EXPIRED_LEASES))) {
98311e4b 5218 l -> desired_binding_state = l -> binding_state;
5f0c7be1
TL
5219 dhcp_failover_queue_update (l, 0);
5220 }
5221 }
5222 }
5223 }
5224 }
5225 return ISC_R_SUCCESS;
5226}
5227
5228isc_result_t
5229dhcp_failover_process_update_request (dhcp_failover_state_t *state,
5230 failover_message_t *msg)
5231{
616d67cb
DH
5232 if (state->send_update_done) {
5233 log_info("Received update request while old update still "
5234 "flying! Silently discarding old request.");
5235 lease_dereference(&state->send_update_done, MDL);
5236 }
5237
5f0c7be1
TL
5238 /* Generate a fresh update queue. */
5239 dhcp_failover_generate_update_queue (state, 0);
5240
a55ccdd0
DH
5241 state->updxid = msg->xid;
5242
5f0c7be1
TL
5243 /* If there's anything on the update queue (there shouldn't be
5244 anything on the ack queue), trigger an update done message
5245 when we get an ack for that lease. */
5246 if (state -> update_queue_tail) {
5247 lease_reference (&state -> send_update_done,
5248 state -> update_queue_tail, MDL);
5249 dhcp_failover_send_updates (state);
98311e4b
DH
5250 log_info ("Update request from %s: sending update",
5251 state -> name);
5f0c7be1
TL
5252 } else {
5253 /* Otherwise, there are no updates to send, so we can
5254 just send an UPDDONE message immediately. */
5255 dhcp_failover_send_update_done (state);
98311e4b
DH
5256 log_info ("Update request from %s: nothing pending",
5257 state -> name);
5f0c7be1
TL
5258 }
5259
5260 return ISC_R_SUCCESS;
5261}
5262
5263isc_result_t
5264dhcp_failover_process_update_request_all (dhcp_failover_state_t *state,
5265 failover_message_t *msg)
5266{
616d67cb
DH
5267 if (state->send_update_done) {
5268 log_info("Received update request while old update still "
5269 "flying! Silently discarding old request.");
5270 lease_dereference(&state->send_update_done, MDL);
5271 }
5272
5f0c7be1
TL
5273 /* Generate a fresh update queue that includes every lease. */
5274 dhcp_failover_generate_update_queue (state, 1);
5275
a55ccdd0
DH
5276 state->updxid = msg->xid;
5277
5f0c7be1
TL
5278 if (state -> update_queue_tail) {
5279 lease_reference (&state -> send_update_done,
5280 state -> update_queue_tail, MDL);
5281 dhcp_failover_send_updates (state);
98311e4b
DH
5282 log_info ("Update request all from %s: sending update",
5283 state -> name);
5f0c7be1
TL
5284 } else {
5285 /* This should really never happen, but it could happen
5286 on a server that currently has no leases configured. */
5287 dhcp_failover_send_update_done (state);
98311e4b
DH
5288 log_info ("Update request all from %s: nothing pending",
5289 state -> name);
5f0c7be1
TL
5290 }
5291
5292 return ISC_R_SUCCESS;
5293}
5294
5295isc_result_t
5296dhcp_failover_process_update_done (dhcp_failover_state_t *state,
fd273e02 5297 failover_message_t *msg)
5f0c7be1 5298{
755acf88
TL
5299 log_info ("failover peer %s: peer update completed.",
5300 state -> name);
5301
98311e4b
DH
5302 state -> curUPD = 0;
5303
02ca1b45 5304 switch (state -> me.state) {
5f0c7be1
TL
5305 case unknown_state:
5306 case partner_down:
5307 case normal:
5308 case communications_interrupted:
5309 case resolution_interrupted:
5310 case shut_down:
5311 case paused:
5312 case recover_done:
d9539882 5313 case startup:
fd273e02 5314 case recover_wait:
5f0c7be1
TL
5315 break; /* shouldn't happen. */
5316
5317 /* We got the UPDDONE, so we can go into normal state! */
5318 case potential_conflict:
a55ccdd0
DH
5319 if (state->partner.state == conflict_done) {
5320 if (state->i_am == secondary) {
5321 dhcp_failover_set_state (state, normal);
5322 } else {
5323 log_error("Secondary is in conflict_done "
5324 "state after conflict resolution, "
5325 "this is illegal.");
5326 dhcp_failover_set_state (state, shut_down);
5327 }
5328 } else {
5329 if (state->i_am == primary)
5330 dhcp_failover_set_state (state, conflict_done);
5331 else
5332 log_error("Spurious update-done message.");
5333 }
5334
5335 break;
5336
5337 case conflict_done:
5338 log_error("Spurious update-done message.");
5f0c7be1
TL
5339 break;
5340
5341 case recover:
37d8bfeb
TL
5342 /* Wait for MCLT to expire before moving to recover_done,
5343 except that if both peers come up in recover, there is
5344 no point in waiting for MCLT to expire - this probably
5345 indicates the initial startup of a newly-configured
5346 failover pair. */
5347 if (state -> me.stos + state -> mclt > cur_time &&
5348 state -> partner.state != recover &&
5349 state -> partner.state != recover_done) {
fd273e02 5350 dhcp_failover_set_state (state, recover_wait);
0db87765
TL
5351#if defined (DEBUG_FAILOVER_TIMING)
5352 log_info ("add_timeout +%d %s",
5353 (int)(cur_time -
5354 state -> me.stos + state -> mclt),
5355 "dhcp_failover_recover_done");
5356#endif
02ca1b45 5357 add_timeout ((int)(state -> me.stos + state -> mclt),
5f0c7be1
TL
5358 dhcp_failover_recover_done,
5359 state,
5360 (tvref_t)omapi_object_reference,
5361 (tvunref_t)
5362 omapi_object_dereference);
da292833 5363 } else
5f0c7be1
TL
5364 dhcp_failover_recover_done (state);
5365 }
5366
5367 return ISC_R_SUCCESS;
5368}
5369
5370void dhcp_failover_recover_done (void *sp)
5371{
5372 dhcp_failover_state_t *state = sp;
5373
0db87765
TL
5374#if defined (DEBUG_FAILOVER_TIMING)
5375 log_info ("dhcp_failover_recover_done");
5376#endif
5377
5f0c7be1
TL
5378 dhcp_failover_set_state (state, recover_done);
5379}
5380
d8a417b2
TL
5381#if defined (DEBUG_FAILOVER_MESSAGES)
5382/* Print hunks of failover messages, doing line breaks as appropriate.
5383 Note that this assumes syslog is being used, rather than, e.g., the
5384 Windows NT logging facility, where just dumping the whole message in
5385 one hunk would be more appropriate. */
5386
5387void failover_print (char *obuf,
5388 unsigned *obufix, unsigned obufmax, const char *s)
5389{
5390 int len = strlen (s);
5391
9f3da356
TL
5392 while (len + *obufix + 1 >= obufmax) {
5393 log_debug ("%s", obuf);
d8a417b2
TL
5394 if (!*obufix) {
5395 log_debug ("%s", s);
9f3da356 5396 *obufix = 0;
d8a417b2
TL
5397 return;
5398 }
d8a417b2 5399 *obufix = 0;
d8a417b2 5400 }
9f3da356
TL
5401 strcpy (&obuf [*obufix], s);
5402 *obufix += len;
d8a417b2
TL
5403}
5404#endif /* defined (DEBUG_FAILOVER_MESSAGES) */
d50d0b82 5405
85a53735
TL
5406/* Taken from draft-ietf-dhc-loadb-01.txt: */
5407/* A "mixing table" of 256 distinct values, in pseudo-random order. */
5408unsigned char loadb_mx_tbl[256] = {
5409 251, 175, 119, 215, 81, 14, 79, 191, 103, 49,
5410 181, 143, 186, 157, 0, 232, 31, 32, 55, 60,
5411 152, 58, 17, 237, 174, 70, 160, 144, 220, 90,
5412 57, 223, 59, 3, 18, 140, 111, 166, 203, 196,
5413 134, 243, 124, 95, 222, 179, 197, 65, 180, 48,
5414 36, 15, 107, 46, 233, 130, 165, 30, 123, 161,
5415 209, 23, 97, 16, 40, 91, 219, 61, 100, 10,
5416 210, 109, 250, 127, 22, 138, 29, 108, 244, 67,
5417 207, 9, 178, 204, 74, 98, 126, 249, 167, 116,
5418 34, 77, 193, 200, 121, 5, 20, 113, 71, 35,
5419 128, 13, 182, 94, 25, 226, 227, 199, 75, 27,
5420 41, 245, 230, 224, 43, 225, 177, 26, 155, 150,
5421 212, 142, 218, 115, 241, 73, 88, 105, 39, 114,
5422 62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
5423 154, 122, 12, 84, 82, 163, 44, 139, 228, 236,
5424 205, 242, 217, 11, 187, 146, 159, 64, 86, 239,
5425 195, 42, 106, 198, 118, 112, 184, 172, 87, 2,
5426 173, 117, 176, 229, 247, 253, 137, 185, 99, 164,
5427 102, 147, 45, 66, 231, 52, 141, 211, 194, 206,
5428 246, 238, 56, 110, 78, 248, 63, 240, 189, 93,
5429 92, 51, 53, 183, 19, 171, 72, 50, 33, 104,
5430 101, 69, 8, 252, 83, 120, 76, 135, 85, 54,
5431 202, 125, 188, 213, 96, 235, 136, 208, 162, 129,
5432 190, 132, 156, 38, 47, 1, 7, 254, 24, 4,
5433 216, 131, 89, 21, 28, 133, 37, 153, 149, 80,
5434 170, 68, 6, 169, 234, 151 };
5435
5436static unsigned char loadb_p_hash (const unsigned char *, unsigned);
5437static unsigned char loadb_p_hash (const unsigned char *key, unsigned len)
5438{
5439 unsigned char hash = len;
5440 int i;
5441 for(i = len; i > 0; )
5442 hash = loadb_mx_tbl [hash ^ (key [--i])];
5443 return hash;
5444}
5445
5446int load_balance_mine (struct packet *packet, dhcp_failover_state_t *state)
5447{
5448 struct option_cache *oc;
5449 struct data_string ds;
5450 unsigned char hbaix;
5451 int hm;
5452
5453 if (state -> load_balance_max_secs < ntohs (packet -> raw -> secs)) {
5454 return 1;
5455 }
5456
007e3ee4
TL
5457 /* If we don't have a hash bucket array, we can't tell if this
5458 one's ours, so we assume it's not. */
5459 if (!state -> hba)
5460 return 0;
5461
85a53735
TL
5462 oc = lookup_option (&dhcp_universe, packet -> options,
5463 DHO_DHCP_CLIENT_IDENTIFIER);
5464 memset (&ds, 0, sizeof ds);
5465 if (oc &&
5466 evaluate_option_cache (&ds, packet, (struct lease *)0,
9e383163 5467 (struct client_state *)0,
85a53735
TL
5468 packet -> options, (struct option_state *)0,
5469 &global_scope, oc, MDL)) {
5470 hbaix = loadb_p_hash (ds.data, ds.len);
5471 } else {
5472 hbaix = loadb_p_hash (packet -> raw -> chaddr,
5473 packet -> raw -> hlen);
5474 }
0b17f049
DH
5475
5476 hm = state->hba[(hbaix >> 3) & 0x1F] & (1 << (hbaix & 0x07));
5477
85a53735
TL
5478 if (state -> i_am == primary)
5479 return hm;
5480 else
5481 return !hm;
5482}
20916cae 5483
2426234f
DH
5484/* The inverse of load_balance_mine ("load balance theirs"). We can't
5485 * use the regular load_balance_mine() and invert it because of the case
5486 * where there might not be an HBA, and we want to indicate false here
5487 * in this case only.
5488 */
5489int
5490peer_wants_lease(struct lease *lp)
5491{
5492 dhcp_failover_state_t *state;
5493 unsigned char hbaix;
5494 int hm;
5495
5496 if (!lp->pool)
5497 return 0;
5498
5499 state = lp->pool->failover_peer;
5500
5501 if (!state || !state->hba)
5502 return 0;
5503
5504 if (lp->uid_len)
5505 hbaix = loadb_p_hash (lp->uid, lp->uid_len);
5506 else
5507 hbaix = loadb_p_hash (lp->hardware_addr.hbuf,
5508 lp->hardware_addr.hlen);
5509
5510 hm = state->hba[(hbaix >> 3) & 0x1F] & (1 << (hbaix & 0x07));
5511
5512 if (state->i_am == primary)
5513 return !hm;
5514 else
5515 return hm;
5516}
5517
da292833
TL
5518/* This deals with what to do with bind updates when
5519 we're in the normal state
5520
5521 Note that tsfp had better be set from the latest bind update
5522 _before_ this function is called! */
5523
5524binding_state_t
5525normal_binding_state_transition_check (struct lease *lease,
5526 dhcp_failover_state_t *state,
31bbee78
TL
5527 binding_state_t binding_state,
5528 u_int32_t tsfp)
007e3ee4 5529{
da292833
TL
5530 binding_state_t new_state;
5531
007e3ee4
TL
5532 /* If there is no transition, it's no problem. */
5533 if (binding_state == lease -> binding_state)
5534 return binding_state;
5535
007e3ee4
TL
5536 switch (lease -> binding_state) {
5537 case FTS_FREE:
5538 case FTS_ABANDONED:
5539 switch (binding_state) {
5540 case FTS_ACTIVE:
5541 case FTS_ABANDONED:
5542 case FTS_BACKUP:
98311e4b
DH
5543 case FTS_EXPIRED:
5544 case FTS_RELEASED:
5545 case FTS_RESET:
007e3ee4
TL
5546 /* If the lease was free, and our peer is primary,
5547 then it can make it active, or abandoned, or
5548 backup. Abandoned is treated like free in
5549 this case. */
5550 if (state -> i_am == secondary)
5551 return binding_state;
5552
d758ad8c
TL
5553 /* Otherwise, it can't legitimately do any sort of
5554 state transition. Because the lease was free,
5555 and the error has already been made, we allow the
5556 peer to change its state anyway, but log a warning
5557 message in hopes that the error will be fixed. */
007e3ee4 5558 case FTS_FREE: /* for compiler */
d758ad8c 5559 new_state = binding_state;
da292833 5560 goto out;
98311e4b
DH
5561
5562 default:
5563 log_fatal ("Impossible case at %s:%d.", MDL);
5564 return FTS_RESET;
007e3ee4
TL
5565 }
5566 case FTS_ACTIVE:
007e3ee4
TL
5567 /* The secondary can't change the state of an active
5568 lease. */
da292833 5569 if (state -> i_am == primary) {
98311e4b
DH
5570 /* Except that the client may send the DHCPRELEASE
5571 to the secondary, and we have to accept that. */
5572 if (binding_state == FTS_RELEASED)
5573 return binding_state;
da292833
TL
5574 new_state = lease -> binding_state;
5575 goto out;
5576 }
007e3ee4
TL
5577
5578 /* So this is only for transitions made by the primary: */
5579 switch (binding_state) {
5580 case FTS_FREE:
5581 case FTS_BACKUP:
5582 /* Can't set a lease to free or backup until the
5583 peer agrees that it's expired. */
31bbee78 5584 if (tsfp > cur_time) {
da292833
TL
5585 new_state = lease -> binding_state;
5586 goto out;
5587 }
007e3ee4
TL
5588 return binding_state;
5589
5590 case FTS_EXPIRED:
98311e4b
DH
5591 /* XXX 65 should be the clock skew between the peers
5592 XXX plus a fudge factor. This code will result
5593 XXX in problems if MCLT is really short or the
5594 XXX max-lease-time is really short (less than the
5595 XXX fudge factor. */
5596 if (lease -> ends - 65 > cur_time) {
da292833
TL
5597 new_state = lease -> binding_state;
5598 goto out;
5599 }
007e3ee4 5600
007e3ee4
TL
5601 case FTS_RELEASED:
5602 case FTS_ABANDONED:
5603 case FTS_RESET:
5604 case FTS_ACTIVE:
5605 return binding_state;
5606
98311e4b
DH
5607 default:
5608 log_fatal ("Impossible case at %s:%d.", MDL);
5609 return FTS_RESET;
007e3ee4 5610 }
d758ad8c 5611 break;
007e3ee4
TL
5612 case FTS_EXPIRED:
5613 switch (binding_state) {
007e3ee4 5614 case FTS_BACKUP:
d758ad8c 5615 case FTS_FREE:
007e3ee4
TL
5616 /* Can't set a lease to free or backup until the
5617 peer agrees that it's expired. */
31bbee78 5618 if (tsfp > cur_time) {
da292833
TL
5619 new_state = lease -> binding_state;
5620 goto out;
5621 }
007e3ee4
TL
5622 return binding_state;
5623
007e3ee4
TL
5624 case FTS_ACTIVE:
5625 case FTS_RELEASED:
5626 case FTS_ABANDONED:
5627 case FTS_RESET:
5628 case FTS_EXPIRED:
5629 return binding_state;
98311e4b
DH
5630
5631 default:
5632 log_fatal ("Impossible case at %s:%d.", MDL);
5633 return FTS_RESET;
007e3ee4
TL
5634 }
5635 case FTS_RELEASED:
5636 switch (binding_state) {
5637 case FTS_FREE:
5638 case FTS_BACKUP:
007e3ee4 5639
a609e69b
TL
5640 /* These are invalid state transitions - should we
5641 prevent them? */
007e3ee4
TL
5642 case FTS_EXPIRED:
5643 case FTS_ABANDONED:
5644 case FTS_RESET:
5645 case FTS_ACTIVE:
5646 case FTS_RELEASED:
5647 return binding_state;
98311e4b
DH
5648
5649 default:
5650 log_fatal ("Impossible case at %s:%d.", MDL);
5651 return FTS_RESET;
007e3ee4
TL
5652 }
5653 case FTS_RESET:
5654 switch (binding_state) {
5655 case FTS_FREE:
5656 case FTS_BACKUP:
5657 /* Can't set a lease to free or backup until the
5658 peer agrees that it's expired. */
31bbee78 5659 if (tsfp > cur_time) {
da292833
TL
5660 new_state = lease -> binding_state;
5661 goto out;
5662 }
007e3ee4
TL
5663 return binding_state;
5664
5665 case FTS_ACTIVE:
007e3ee4
TL
5666 case FTS_EXPIRED:
5667 case FTS_RELEASED:
5668 case FTS_ABANDONED:
5669 case FTS_RESET:
5670 return binding_state;
98311e4b
DH
5671
5672 default:
5673 log_fatal ("Impossible case at %s:%d.", MDL);
5674 return FTS_RESET;
007e3ee4
TL
5675 }
5676 case FTS_BACKUP:
5677 switch (binding_state) {
5678 case FTS_ACTIVE:
5679 case FTS_ABANDONED:
007e3ee4
TL
5680 case FTS_EXPIRED:
5681 case FTS_RELEASED:
5682 case FTS_RESET:
98311e4b
DH
5683 /* If the lease was in backup, and our peer
5684 is secondary, then it can make it active
5685 or abandoned. */
5686 if (state -> i_am == primary)
5687 return binding_state;
5688
5689 /* Either the primary or the secondary can
5690 reasonably move a lease from the backup
5691 state to the free state. */
5692 case FTS_FREE:
5693 return binding_state;
007e3ee4
TL
5694
5695 case FTS_BACKUP:
da292833
TL
5696 new_state = lease -> binding_state;
5697 goto out;
98311e4b
DH
5698
5699 default:
5700 log_fatal ("Impossible case at %s:%d.", MDL);
5701 return FTS_RESET;
da292833 5702 }
98311e4b
DH
5703
5704 default:
5705 log_fatal ("Impossible case at %s:%d.", MDL);
5706 return FTS_RESET;
da292833
TL
5707 }
5708 out:
5709 return new_state;
5710}
5711
5712/* Determine whether the state transition is okay when we're potentially
5713 in conflict with the peer. */
5714binding_state_t
5715conflict_binding_state_transition_check (struct lease *lease,
5716 dhcp_failover_state_t *state,
31bbee78
TL
5717 binding_state_t binding_state,
5718 u_int32_t tsfp)
da292833
TL
5719{
5720 binding_state_t new_state;
5721
5722 /* If there is no transition, it's no problem. */
5723 if (binding_state == lease -> binding_state)
5724 new_state = binding_state;
5725 else {
5726 switch (lease -> binding_state) {
5727 /* If we think the lease is not in use, then the
5728 state into which the partner put it is just fine,
5729 whatever it is. */
5730 case FTS_FREE:
5731 case FTS_ABANDONED:
5732 case FTS_EXPIRED:
5733 case FTS_RELEASED:
5734 case FTS_RESET:
5735 case FTS_BACKUP:
5736 new_state = binding_state;
5737 break;
5738
5739 /* If we think the lease *is* in use, then we're not
5740 going to take the partner's change if the partner
5741 thinks it's free. */
5742 case FTS_ACTIVE:
da292833
TL
5743 switch (binding_state) {
5744 case FTS_FREE:
5745 case FTS_BACKUP:
da292833
TL
5746 new_state = lease -> binding_state;
5747 break;
5748
5749 case FTS_EXPIRED:
88cd8aca
DH
5750 /* If we don't agree about expiry, it's
5751 * invalid. 65 should allow for max
5752 * clock skew (60) plus some fudge.
5753 * XXX: should we refetch cur_time?
5754 */
5755 if ((lease->ends - 65) > cur_time)
5756 new_state = lease->binding_state;
da292833
TL
5757 else
5758 new_state = binding_state;
5759 break;
5760
88cd8aca
DH
5761 /* RELEASED, RESET, and ABANDONED indicate
5762 * that our partner has information about
5763 * this lease that we did not witness. Our
5764 * partner wins.
5765 */
5766 case FTS_RELEASED:
5767 case FTS_RESET:
5768 case FTS_ABANDONED:
da292833
TL
5769 new_state = binding_state;
5770 break;
98311e4b
DH
5771
5772 default:
5773 log_fatal ("Impossible case at %s:%d.", MDL);
5774 return FTS_RESET;
da292833
TL
5775 }
5776 break;
98311e4b
DH
5777
5778 default:
5779 log_fatal ("Impossible case at %s:%d.", MDL);
5780 return FTS_RESET;
007e3ee4
TL
5781 }
5782 }
da292833 5783 return new_state;
007e3ee4
TL
5784}
5785
5f0c7be1
TL
5786/* We can reallocate a lease under the following circumstances:
5787
5788 (1) It belongs to us - it's FTS_FREE, and we're primary, or it's
5789 FTS_BACKUP, and we're secondary.
5790 (2) We're in partner_down, and the lease is not active, and we
5791 can be sure that the other server didn't make it active.
5792 We can only be sure that the server didn't make it active
5793 when we are in the partner_down state and one of the following
5794 two conditions holds:
5795 (a) in the case that the time sent from the peer is earlier than
5796 the time we entered the partner_down state, at least MCLT has
5797 gone by since we entered partner_down, or
5798 (b) in the case that the time sent from the peer is later than
5799 the time when we entered partner_down, the current time is
5800 later than the time sent from the peer by at least MCLT. */
5801
5802int lease_mine_to_reallocate (struct lease *lease)
007e3ee4
TL
5803{
5804 dhcp_failover_state_t *peer;
5805
88cd8aca
DH
5806 if (lease && lease->pool &&
5807 (peer = lease->pool->failover_peer)) {
5808 switch (lease->binding_state) {
5f0c7be1 5809 case FTS_ACTIVE:
88cd8aca 5810 /* ACTIVE leases may not be reallocated. */
5f0c7be1
TL
5811 return 0;
5812
5813 case FTS_FREE:
88cd8aca
DH
5814 case FTS_ABANDONED:
5815 /* FREE leases may only be allocated by the primary,
5816 * unless the secondary is acting in partner_down
5817 * state and stos+mclt or tsfp+mclt has expired,
5818 * whichever is greater.
5819 *
5820 * ABANDONED are treated the same as FREE for all
5821 * purposes here. Note that servers will only try
5822 * for ABANDONED leases as a last resort anyway.
5823 */
5f0c7be1
TL
5824 if (peer -> i_am == primary)
5825 return 1;
5f0c7be1 5826
88cd8aca
DH
5827 return(peer->service_state == service_partner_down &&
5828 ((lease->tsfp < peer->me.stos) ?
5829 (peer->me.stos + peer->mclt < cur_time) :
5830 (lease->tsfp + peer->mclt < cur_time)));
5831
5f0c7be1
TL
5832 case FTS_RESET:
5833 case FTS_RELEASED:
5834 case FTS_EXPIRED:
88cd8aca
DH
5835 /* These three lease states go onto the 'expired'
5836 * queue. Upon entry into partner-down state, this
5837 * queue of leases has their tsfp values modified
5838 * to equal stos+mclt, the point at which the server
5839 * is allowed to remove them from these transitional
5840 * states without an acknowledgement.
5841 *
5842 * Note that although tsfp has been possibly extended
5843 * past the actual tsfp we received from the peer, we
5844 * don't have to take any special action. Since tsfp
5845 * is now in the past (or now), we can guarantee that
5846 * this server will only allocate a lease time equal
5847 * to MCLT, rather than a TSFP-optimal lease, which is
5848 * the only danger for a lease in one of these states.
5849 */
5850 return((peer->service_state == service_partner_down) &&
5851 (lease->tsfp < cur_time));
5852
5f0c7be1 5853 case FTS_BACKUP:
88cd8aca
DH
5854 /* Only the secondary may allocate BACKUP leases,
5855 * unless in partner_down state in which case at
5856 * least TSFP+MCLT or STOS+MCLT must have expired,
5857 * whichever is greater.
5858 */
5859 if (peer->i_am == secondary)
5f0c7be1 5860 return 1;
88cd8aca
DH
5861
5862 return((peer->service_state == service_partner_down) &&
5863 ((lease->tsfp < peer->me.stos) ?
5864 (peer->me.stos + peer->mclt < cur_time) :
5865 (lease->tsfp + peer->mclt < cur_time)));
5866
5867 default:
5868 /* All lease states appear above. */
5869 log_fatal("Impossible case at %s:%d.", MDL);
5870 break;
007e3ee4 5871 }
5f0c7be1 5872 return 0;
007e3ee4 5873 }
d588b01f 5874 if (lease)
88cd8aca
DH
5875 return(lease->binding_state == FTS_FREE ||
5876 lease->binding_state == FTS_BACKUP);
d588b01f
TL
5877 else
5878 return 0;
007e3ee4
TL
5879}
5880
988c1ca7
DN
5881static isc_result_t failover_message_reference (failover_message_t **mp,
5882 failover_message_t *m,
5883 const char *file, int line)
5884{
5885 *mp = m;
5886 m -> refcnt++;
5887 return ISC_R_SUCCESS;
5888}
5889
5890static isc_result_t failover_message_dereference (failover_message_t **mp,
5891 const char *file, int line)
5892{
d758ad8c
TL
5893 failover_message_t *m;
5894 m = (*mp);
5895 m -> refcnt--;
5896 if (m -> refcnt == 0) {
5897 if (m -> next)
5898 failover_message_dereference (&m -> next,
5899 file, line);
5900 if (m -> chaddr.data)
5901 dfree (m -> chaddr.data, file, line);
5902 if (m -> client_identifier.data)
5903 dfree (m -> client_identifier.data, file, line);
5904 if (m -> hba.data)
5905 dfree (m -> hba.data, file, line);
5906 if (m -> message.data)
5907 dfree (m -> message.data, file, line);
5908 if (m -> reply_options.data)
5909 dfree (m -> reply_options.data, file, line);
5910 if (m -> request_options.data)
5911 dfree (m -> request_options.data, file, line);
5912 if (m -> vendor_class.data)
5913 dfree (m -> vendor_class.data, file, line);
5914 if (m -> vendor_options.data)
5915 dfree (m -> vendor_options.data, file, line);
5916 if (m -> ddns.data)
5917 dfree (m -> ddns.data, file, line);
5918 dfree (*mp, file, line);
988c1ca7 5919 }
fa0b4709 5920 *mp = 0;
988c1ca7
DN
5921 return ISC_R_SUCCESS;
5922}
5923
20916cae
TL
5924OMAPI_OBJECT_ALLOC (dhcp_failover_state, dhcp_failover_state_t,
5925 dhcp_type_failover_state)
5926OMAPI_OBJECT_ALLOC (dhcp_failover_listener, dhcp_failover_listener_t,
5927 dhcp_type_failover_listener)
5928OMAPI_OBJECT_ALLOC (dhcp_failover_link, dhcp_failover_link_t,
5929 dhcp_type_failover_link)
c68d2a87 5930#endif /* defined (FAILOVER_PROTOCOL) */
da292833 5931
da292833
TL
5932const char *binding_state_print (enum failover_state state)
5933{
5934 switch (state) {
5935 case FTS_FREE:
5936 return "free";
5937 break;
5938
5939 case FTS_ACTIVE:
5940 return "active";
5941 break;
5942
5943 case FTS_EXPIRED:
5944 return "expired";
5945 break;
5946
5947 case FTS_RELEASED:
5948 return "released";
5949 break;
5950
5951 case FTS_ABANDONED:
5952 return "abandoned";
5953 break;
5954
5955 case FTS_RESET:
5956 return "reset";
5957 break;
5958
5959 case FTS_BACKUP:
5960 return "backup";
5961 break;
5962
da292833
TL
5963 default:
5964 return "unknown";
5965 break;
5966 }
5967}