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