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