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