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