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