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