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