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