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