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