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