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