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