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