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