ISC DHCP  4.3.2
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-2014 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 (p -> failover_peer == state) {
1949  for (l = p->expired ; l ; l = l->next) {
1950  l->tsfp = state->me.stos + state->mclt;
1951  l->sort_time = (l->tsfp > l->ends) ?
1952  l->tsfp : l->ends;
1953  }
1954  if (p->expired &&
1955  (p->expired->sort_time < p->next_event_time)) {
1956 
1958 #if defined (DEBUG_FAILOVER_TIMING)
1959  log_info ("add_timeout +%d %s",
1960  (int)(cur_time - p->next_event_time),
1961  "pool_timer");
1962 #endif
1963  tv.tv_sec = p->next_event_time;
1964  tv.tv_usec = 0;
1965  add_timeout(&tv, pool_timer, p,
1966  (tvref_t)pool_reference,
1967  (tvunref_t)pool_dereference);
1968  }
1969  }
1970  }
1971  }
1972  break;
1973 
1974 
1975  default:
1976  break;
1977  }
1978 
1980 
1981  return ISC_R_SUCCESS;
1982 }
1983 
1984 isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state,
1985  failover_message_t *msg)
1986 {
1987  enum failover_state previous_state = state -> partner.state;
1988  enum failover_state new_state;
1989  int startupp;
1990 
1991  new_state = msg -> server_state;
1992  startupp = (msg -> server_flags & FTF_SERVER_STARTUP) ? 1 : 0;
1993 
1994  if (state -> partner.state == new_state && state -> me.state) {
1995  switch (state -> me.state) {
1996  case startup:
1997  /*
1998  * If we have a peer state we must be connected.
1999  * If so we should move to potential_conflict
2000  * instead of resolution_interrupted, otherwise
2001  * back to whereever we were before we stopped.
2002  */
2003  if (state->saved_state == resolution_interrupted)
2006  else
2008  state->saved_state);
2009  return ISC_R_SUCCESS;
2010 
2011  case unknown_state:
2012  case normal:
2013  case potential_conflict:
2014  case recover_done:
2015  case shut_down:
2016  case paused:
2017  case recover_wait:
2018  return ISC_R_SUCCESS;
2019 
2020  /* If we get a peer state change when we're
2021  disconnected, we always process it. */
2022  case partner_down:
2025  case recover:
2026  case conflict_done:
2027  break;
2028 
2029  default:
2030  log_fatal("Impossible case at %s:%d.", MDL);
2031  break;
2032  }
2033  }
2034 
2035  state -> partner.state = new_state;
2036  state -> partner.stos = cur_time;
2037 
2038  log_info ("failover peer %s: peer moves from %s to %s",
2039  state -> name,
2040  dhcp_failover_state_name_print (previous_state),
2041  dhcp_failover_state_name_print (state -> partner.state));
2042 
2043  /* If both servers are now normal log it */
2044  if ((state->me.state == normal) && (state->partner.state == normal))
2045  log_info("failover peer %s: Both servers normal", state->name);
2046 
2047  if (!write_failover_state (state) || !commit_leases ()) {
2048  /* This is bad, but it's not fatal. Of course, if we
2049  can't write to the lease database, we're not going to
2050  get much done anyway. */
2051  log_error ("Unable to record current failover state for %s",
2052  state -> name);
2053  }
2054 
2055  /* Quickly validate the new state as being one of the 13 known
2056  * states.
2057  */
2058  switch (new_state) {
2059  case unknown_state:
2060  case startup:
2061  case normal:
2063  case partner_down:
2064  case potential_conflict:
2065  case recover:
2066  case paused:
2067  case shut_down:
2068  case recover_done:
2070  case conflict_done:
2071  case recover_wait:
2072  break;
2073 
2074  default:
2075  log_error("failover peer %s: Invalid state: %d", state->name,
2076  new_state);
2078  return ISC_R_SUCCESS;
2079  }
2080 
2081  /* Do any state transitions that are required as a result of the
2082  peer's state transition. */
2083 
2084  switch (state -> me.state == startup ?
2085  state -> saved_state : state -> me.state) {
2086  case normal:
2087  switch (new_state) {
2088  case normal:
2090  break;
2091 
2092  case partner_down:
2093  if (state -> me.state == startup)
2095  else
2096  dhcp_failover_set_state (state,
2098  break;
2099 
2100  case potential_conflict:
2102  case conflict_done:
2103  /* None of these transitions should ever occur. */
2104  log_error("Peer %s: Invalid state transition %s "
2105  "to %s.", state->name,
2106  dhcp_failover_state_name_print(previous_state),
2107  dhcp_failover_state_name_print(new_state));
2109  break;
2110 
2111  case recover:
2112  case shut_down:
2114  break;
2115 
2116  case paused:
2117  dhcp_failover_set_state (state,
2119  break;
2120 
2121  default:
2122  /* recover_wait, recover_done, unknown_state, startup,
2123  * communications_interrupted
2124  */
2125  break;
2126  }
2127  break;
2128 
2129  case recover:
2130  switch (new_state) {
2131  case recover:
2132  log_info ("failover peer %s: requesting %s",
2133  state -> name, "full update from peer");
2134  /* Don't send updreqall if we're really in the
2135  startup state, because that will result in two
2136  being sent. */
2137  if (state -> me.state == recover)
2139  break;
2140 
2141  case potential_conflict:
2143  case conflict_done:
2144  case normal:
2146  break;
2147 
2148  case partner_down:
2150  /* We're supposed to send an update request at this
2151  point. */
2152  /* XXX we don't currently have code here to do any
2153  XXX clever detection of when we should send an
2154  XXX UPDREQALL message rather than an UPDREQ
2155  XXX message. What to do, what to do? */
2156  /* Currently when we enter recover state, no matter
2157  * the reason, we send an UPDREQALL. So, it makes
2158  * the most sense to stick to that until something
2159  * better is done.
2160  * Furthermore, we only want to send the update
2161  * request if we are not in startup state.
2162  */
2163  if (state -> me.state == recover)
2165  break;
2166 
2167  case shut_down:
2168  /* XXX We're not explicitly told what to do in this
2169  XXX case, but this transition is consistent with
2170  XXX what is elsewhere in the draft. */
2172  break;
2173 
2174  /* We can't really do anything in this case. */
2175  default:
2176  /* paused, recover_done, recover_wait, unknown_state,
2177  * startup.
2178  */
2179  break;
2180  }
2181  break;
2182 
2183  case potential_conflict:
2184  switch (new_state) {
2185  case normal:
2186  /* This is an illegal transition. */
2187  log_error("Peer %s moves to normal during conflict "
2188  "resolution - panic, shutting down.",
2189  state->name);
2191  break;
2192 
2193  case conflict_done:
2194  if (previous_state == potential_conflict)
2196  else
2197  log_error("Peer %s: Unexpected move to "
2198  "conflict-done.", state->name);
2199  break;
2200 
2201  case recover_done:
2202  case recover_wait:
2203  case potential_conflict:
2204  case partner_down:
2207  case paused:
2208  break;
2209 
2210  case recover:
2212  break;
2213 
2214  case shut_down:
2216  break;
2217 
2218  default:
2219  /* unknown_state, startup */
2220  break;
2221  }
2222  break;
2223 
2224  case conflict_done:
2225  switch (new_state) {
2226  case normal:
2227  case shut_down:
2228  dhcp_failover_set_state(state, new_state);
2229  break;
2230 
2231  case potential_conflict:
2233  /*
2234  * This can happen when the connection is lost and
2235  * recovered after the primary has moved to
2236  * conflict-done but the secondary is still in
2237  * potential-conflict. In that case, we have to
2238  * remain in conflict-done.
2239  */
2240  break;
2241 
2242  default:
2243  log_fatal("Peer %s: Invalid attempt to move from %s "
2244  "to %s while local state is conflict-done.",
2245  state->name,
2246  dhcp_failover_state_name_print(previous_state),
2247  dhcp_failover_state_name_print(new_state));
2248  }
2249  break;
2250 
2251  case partner_down:
2252  /* Take no action if other server is starting up. */
2253  if (startupp)
2254  break;
2255 
2256  switch (new_state) {
2257  /* This is where we should be. */
2258  case recover:
2259  case recover_wait:
2260  break;
2261 
2262  case recover_done:
2264  break;
2265 
2266  case normal:
2267  case potential_conflict:
2268  case partner_down:
2271  case conflict_done:
2273  break;
2274 
2275  default:
2276  /* shut_down, paused, unknown_state, startup */
2277  break;
2278  }
2279  break;
2280 
2282  switch (new_state) {
2283  case paused:
2284  /* Stick with the status quo. */
2285  break;
2286 
2287  /* If we're in communications-interrupted and an
2288  amnesic peer connects, go to the partner_down
2289  state immediately. */
2290  case recover:
2292  break;
2293 
2294  case normal:
2296  case recover_done:
2297  case recover_wait:
2298  /* XXX so we don't need to do this specially in
2299  XXX the CONNECT and CONNECTACK handlers. */
2302  break;
2303 
2304  case potential_conflict:
2305  case partner_down:
2307  case conflict_done:
2309  break;
2310 
2311  case shut_down:
2313  break;
2314 
2315  default:
2316  /* unknown_state, startup */
2317  break;
2318  }
2319  break;
2320 
2322  switch (new_state) {
2323  case normal:
2324  case recover:
2325  case potential_conflict:
2326  case partner_down:
2329  case conflict_done:
2330  case recover_done:
2331  case recover_wait:
2333  break;
2334 
2335  case shut_down:
2337  break;
2338 
2339  default:
2340  /* paused, unknown_state, startup */
2341  break;
2342  }
2343  break;
2344 
2345  /* Make no transitions while in recover_wait...just wait. */
2346  case recover_wait:
2347  break;
2348 
2349  case recover_done:
2350  switch (new_state) {
2351  case recover_done:
2352  log_error("Both servers have entered recover-done!");
2353  /* Fall through and tranistion to normal anyway */
2354 
2355  case normal:
2357  break;
2358 
2359  case shut_down:
2361  break;
2362 
2363  default:
2364  /* potential_conflict, partner_down,
2365  * communications_interrupted, resolution_interrupted,
2366  * paused, recover, recover_wait, unknown_state,
2367  * startup.
2368  */
2369  break;
2370  }
2371  break;
2372 
2373  /* We are essentially dead in the water when we're in
2374  either shut_down or paused states, and do not do any
2375  automatic state transitions. */
2376  case shut_down:
2377  case paused:
2378  break;
2379 
2380  /* XXX: Shouldn't this be a fatal condition? */
2381  case unknown_state:
2382  break;
2383 
2384  default:
2385  log_fatal("Impossible condition at %s:%d.", MDL);
2386  break;
2387 
2388  }
2389 
2390  /* If we didn't make a transition out of startup as a result of
2391  the peer's state change, do it now as a result of the fact that
2392  we got a state change from the peer. */
2393  if (state -> me.state == startup && state -> saved_state != startup)
2394  dhcp_failover_set_state (state, state -> saved_state);
2395 
2396  /* For now, just set the service state based on the peer's state
2397  if necessary. */
2399 
2400  return ISC_R_SUCCESS;
2401 }
2402 
2403 /*
2404  * Balance operation manual entry; startup, entrance to normal state. No
2405  * sense sending a POOLREQ at this stage; the peer is likely about to schedule
2406  * their own rebalance event upon entering normal themselves.
2407  */
2408 static void
2409 dhcp_failover_pool_balance(dhcp_failover_state_t *state)
2410 {
2411  /* Cancel pending event. */
2413  state->sched_balance = 0;
2414 
2415  dhcp_failover_pool_dobalance(state, NULL);
2416 }
2417 
2418 /*
2419  * Balance operation entry from timer event. Once per timer interval is
2420  * the only time we want to emit POOLREQs (asserting an interrupt in our
2421  * peer).
2422  */
2423 void
2425 {
2426  dhcp_failover_state_t *state;
2427  isc_boolean_t sendreq = ISC_FALSE;
2428 
2429  state = (dhcp_failover_state_t *)failover_state;
2430 
2431  /* Clear scheduled event indicator. */
2432  state->sched_balance = 0;
2433 
2434  if (dhcp_failover_pool_dobalance(state, &sendreq))
2436 
2437  if (sendreq)
2439 }
2440 
2441 /*
2442  * Balance operation entry from POOLREQ protocol message. Do not permit a
2443  * POOLREQ to send back a POOLREQ. Ping pong.
2444  */
2445 static void
2446 dhcp_failover_pool_reqbalance(dhcp_failover_state_t *state)
2447 {
2448  int queued;
2449 
2450  /* Cancel pending event. */
2452  state->sched_balance = 0;
2453 
2454  queued = dhcp_failover_pool_dobalance(state, NULL);
2455 
2456  dhcp_failover_send_poolresp(state, queued);
2457 
2458  if (queued)
2460  else
2461  log_info("peer %s: Got POOLREQ, answering negatively! "
2462  "Peer may be out of leases or database inconsistent.",
2463  state->name);
2464 }
2465 
2466 /*
2467  * Do the meat of the work common to all forms of pool rebalance. If the
2468  * caller deems it appropriate to transmit POOLREQ messages, it can use the
2469  * sendreq pointer to pass in the address of a FALSE value which this function
2470  * will conditionally turn TRUE if a POOLREQ is determined to be necessary.
2471  * A NULL value may be passed, in which case no action is taken.
2472  */
2473 static int
2474 dhcp_failover_pool_dobalance(dhcp_failover_state_t *state,
2475  isc_boolean_t *sendreq)
2476 {
2477  int lts, total, thresh, hold, panic, pass;
2478  int leases_queued = 0;
2479  struct lease *lp = (struct lease *)0;
2480  struct lease *next = (struct lease *)0;
2481  struct shared_network *s;
2482  struct pool *p;
2483  binding_state_t peer_lease_state;
2484  /* binding_state_t my_lease_state; */
2485  /* XXX Why is this my_lease_state never used? */
2486  struct lease **lq;
2487  int (*log_func)(const char *, ...);
2488  const char *result, *reqlog;
2489 
2490  if (state -> me.state != normal)
2491  return 0;
2492 
2494 
2495  state->last_balance = cur_time;
2496 
2497  for (s = shared_networks ; s ; s = s->next) {
2498  for (p = s->pools ; p ; p = p->next) {
2499  if (p->failover_peer != state)
2500  continue;
2501 
2502  /* Right now we're giving the peer half of the free leases.
2503  If we have more leases than the peer (i.e., more than
2504  half), then the number of leases we have, less the number
2505  of leases the peer has, will be how many more leases we
2506  have than the peer has. So if we send half that number
2507  to the peer, we should be even. */
2508  if (p->failover_peer->i_am == primary) {
2509  lts = (p->free_leases - p->backup_leases) / 2;
2510  peer_lease_state = FTS_BACKUP;
2511  /* my_lease_state = FTS_FREE; */
2512  lq = &p->free;
2513  } else {
2514  lts = (p->backup_leases - p->free_leases) / 2;
2515  peer_lease_state = FTS_FREE;
2516  /* my_lease_state = FTS_BACKUP; */
2517  lq = &p->backup;
2518  }
2519 
2520  total = p->backup_leases + p->free_leases;
2521 
2522  thresh = ((total * state->max_lease_misbalance) + 50) / 100;
2523  hold = ((total * state->max_lease_ownership) + 50) / 100;
2524 
2525  /*
2526  * If we need leases (so lts is negative) more than negative
2527  * double the thresh%, panic and send poolreq to hopefully wake
2528  * up the peer (but more likely the db is inconsistent). But,
2529  * if this comes out zero, switch to -1 so that the POOLREQ is
2530  * sent on lts == -2 rather than right away at -1.
2531  *
2532  * Note that we do not subtract -1 from panic all the time
2533  * because thresh% and hold% may come out to the same number,
2534  * and that is correct operation...where thresh% and hold% are
2535  * both -1, we want to send poolreq when lts reaches -3. So,
2536  * "-3 < -2", lts < panic.
2537  */
2538  panic = thresh * -2;
2539 
2540  if (panic == 0)
2541  panic = -1;
2542 
2543  if ((sendreq != NULL) && (lts < panic)) {
2544  reqlog = " (requesting peer rebalance!)";
2545  *sendreq = ISC_TRUE;
2546  } else
2547  reqlog = "";
2548 
2549  log_info("balancing pool %lx %s total %d free %d "
2550  "backup %d lts %d max-own (+/-)%d%s",
2551  (unsigned long)p,
2552  (p->shared_network ?
2553  p->shared_network->name : ""), p->lease_count,
2554  p->free_leases, p->backup_leases, lts, hold,
2555  reqlog);
2556 
2557  /* In the first pass, try to allocate leases to the
2558  * peer which it would normally be responsible for (if
2559  * the lease has a hardware address or client-identifier,
2560  * and the load-balance-algorithm chooses the peer to
2561  * answer that address), up to a hold% excess in the peer's
2562  * favor. In the second pass, just send the oldest (first
2563  * on the list) leases up to a hold% excess in our favor.
2564  *
2565  * This could make for additional pool rebalance
2566  * events, but preserving MAC possession should be
2567  * worth it.
2568  */
2569  pass = 0;
2570  lease_reference(&lp, *lq, MDL);
2571 
2572  while (lp) {
2573  if (next)
2574  lease_dereference(&next, MDL);
2575  if (lp->next)
2576  lease_reference(&next, lp->next, MDL);
2577 
2578  /*
2579  * Stop if the pool is 'balanced enough.'
2580  *
2581  * The pool is balanced enough if:
2582  *
2583  * 1) We're on the first run through and the peer has
2584  * its fair share of leases already (lts reaches
2585  * -hold).
2586  * 2) We're on the second run through, we are shifting
2587  * never-used leases, and there is a perfectly even
2588  * balance (lts reaches zero).
2589  * 3) Second run through, we are shifting previously
2590  * used leases, and the local system has its fair
2591  * share but no more (lts reaches hold).
2592  *
2593  * Note that this is implemented below in 3,2,1 order.
2594  */
2595  if (pass) {
2596  if (lp->ends) {
2597  if (lts <= hold)
2598  break;
2599  } else {
2600  if (lts <= 0)
2601  break;
2602  }
2603  } else if (lts <= -hold)
2604  break;
2605 
2606  if (pass || peer_wants_lease(lp)) {
2607  --lts;
2608  ++leases_queued;
2609  lp->next_binding_state = peer_lease_state;
2610  lp->tstp = cur_time;
2611  lp->starts = cur_time;
2612 
2613  if (!supersede_lease(lp, NULL, 0, 1, 0, 0) ||
2614  !write_lease(lp))
2615  log_error("can't commit lease %s on "
2616  "giveaway", piaddr(lp->ip_addr));
2617  }
2618 
2619  lease_dereference(&lp, MDL);
2620  if (next)
2621  lease_reference(&lp, next, MDL);
2622  else if (!pass) {
2623  pass = 1;
2624  lease_reference(&lp, *lq, MDL);
2625  }
2626  }
2627 
2628  if (next)
2629  lease_dereference(&next, MDL);
2630  if (lp)
2631  lease_dereference(&lp, MDL);
2632 
2633  if (lts > thresh) {
2634  result = "IMBALANCED";
2635  log_func = log_error;
2636  } else {
2637  result = "balanced";
2638  log_func = log_info;
2639  }
2640 
2641  log_func("%s pool %lx %s total %d free %d backup %d "
2642  "lts %d max-misbal %d", result, (unsigned long)p,
2643  (p->shared_network ?
2644  p->shared_network->name : ""), p->lease_count,
2645  p->free_leases, p->backup_leases, lts, thresh);
2646 
2647  /* Recalculate next rebalance event timer. */
2649  }
2650  }
2651 
2652  if (leases_queued)
2653  commit_leases();
2654 
2656 
2657  return leases_queued;
2658 }
2659 
2660 /* dhcp_failover_pool_check: Called whenever FREE or BACKUP leases change
2661  * states, on both servers. Check the scheduled time to rebalance the pool
2662  * and lower it if applicable.
2663  */
2664 void
2666 {
2667  dhcp_failover_state_t *peer;
2668  TIME est1, est2;
2669  struct timeval tv;
2670 
2671  peer = pool->failover_peer;
2672 
2673  if(!peer || peer->me.state != normal)
2674  return;
2675 
2676  /* Estimate the time left until lease exhaustion.
2677  * The first lease on the backup or free lists is also the oldest
2678  * lease. It is reasonable to guess that it will take at least
2679  * as much time for a pool to run out of leases, as the present
2680  * age of the oldest lease (seconds since it expired).
2681  *
2682  * Note that this isn't so sane of an assumption if the oldest
2683  * lease is a virgin (ends = 0), we wind up sending this against
2684  * the max_balance bounds check.
2685  */
2686  if(pool->free && pool->free->ends < cur_time)
2687  est1 = cur_time - pool->free->ends;
2688  else
2689  est1 = 0;
2690 
2691  if(pool->backup && pool->backup->ends < cur_time)
2692  est2 = cur_time - pool->backup->ends;
2693  else
2694  est2 = 0;
2695 
2696  /* We don't want to schedule rebalance for when we think we'll run
2697  * out of leases, we want to schedule the rebalance for when we think
2698  * the disparity will be 'large enough' to warrant action.
2699  */
2700  est1 = ((est1 * peer->max_lease_misbalance) + 50) / 100;
2701  est2 = ((est2 * peer->max_lease_misbalance) + 50) / 100;
2702 
2703  /* Guess when the local system will begin issuing POOLREQ panic
2704  * attacks because "max_lease_misbalance*2" has been exceeded.
2705  */
2706  if(peer->i_am == primary)
2707  est1 *= 2;
2708  else
2709  est2 *= 2;
2710 
2711  /* Select the smallest time. */
2712  if(est1 > est2)
2713  est1 = est2;
2714 
2715  /* Bounded by the maximum configured value. */
2716  if(est1 > peer->max_balance)
2717  est1 = peer->max_balance;
2718 
2719  /* Project this time into the future. */
2720  est1 += cur_time;
2721 
2722  /* Do not move the time down under the minimum. */
2723  est2 = peer->last_balance + peer->min_balance;
2724  if(peer->last_balance && (est1 < est2))
2725  est1 = est2;
2726 
2727  /* Introduce a random delay. */
2728  est1 += random() % 5;
2729 
2730  /* Do not move the time forward, or reset to the same time. */
2731  if(peer->sched_balance) {
2732  if (est1 >= peer->sched_balance)
2733  return;
2734 
2735  /* We are about to schedule the time down, cancel the
2736  * current timeout.
2737  */
2739  }
2740 
2741  /* The time is different, and lower, use it. */
2742  peer->sched_balance = est1;
2743 
2744 #if defined(DEBUG_FAILOVER_TIMING)
2745  log_info("add_timeout +%d dhcp_failover_pool_rebalance",
2746  (int)(est1 - cur_time));
2747 #endif
2748  tv.tv_sec = est1;
2749  tv.tv_usec = 0;
2751  (tvref_t)dhcp_failover_state_reference,
2752  (tvunref_t)dhcp_failover_state_dereference);
2753 }
2754 
2755 int dhcp_failover_state_pool_check (dhcp_failover_state_t *state)
2756 {
2757  struct shared_network *s;
2758  struct pool *p;
2759 
2760  for (s = shared_networks; s; s = s -> next) {
2761  for (p = s -> pools; p; p = p -> next) {
2762  if (p -> failover_peer != state)
2763  continue;
2765  }
2766  }
2767  return 0;
2768 }
2769 
2770 isc_result_t dhcp_failover_send_updates (dhcp_failover_state_t *state)
2771 {
2772  struct lease *lp = (struct lease *)0;
2773  isc_result_t status;
2774 
2775  /* Can't update peer if we're not talking to it! */
2776  if (!state -> link_to_peer)
2777  return ISC_R_SUCCESS;
2778 
2779  /* If there are acks pending, transmit them prior to potentially
2780  * sending new updates for the same lease.
2781  */
2782  if (state->toack_queue_head != NULL)
2783  dhcp_failover_send_acks(state);
2784 
2785  while ((state -> partner.max_flying_updates >
2786  state -> cur_unacked_updates) && state -> update_queue_head) {
2787  /* Grab the head of the update queue. */
2788  lease_reference (&lp, state -> update_queue_head, MDL);
2789 
2790  /* Send the update to the peer. */
2791  status = dhcp_failover_send_bind_update (state, lp);
2792  if (status != ISC_R_SUCCESS) {
2793  lease_dereference (&lp, MDL);
2794  return status;
2795  }
2796  lp -> flags &= ~ON_UPDATE_QUEUE;
2797 
2798  /* Take it off the head of the update queue and put the next
2799  item in the update queue at the head. */
2800  lease_dereference (&state -> update_queue_head, MDL);
2801  if (lp -> next_pending) {
2802  lease_reference (&state -> update_queue_head,
2803  lp -> next_pending, MDL);
2804  lease_dereference (&lp -> next_pending, MDL);
2805  } else {
2806  lease_dereference (&state -> update_queue_tail, MDL);
2807  }
2808 
2809  if (state -> ack_queue_head) {
2810  lease_reference
2811  (&state -> ack_queue_tail -> next_pending,
2812  lp, MDL);
2813  lease_dereference (&state -> ack_queue_tail, MDL);
2814  } else {
2815  lease_reference (&state -> ack_queue_head, lp, MDL);
2816  }
2817 #if defined (POINTER_DEBUG)
2818  if (lp -> next_pending) {
2819  log_error ("ack_queue_tail: lp -> next_pending");
2820  abort ();
2821  }
2822 #endif
2823  lease_reference (&state -> ack_queue_tail, lp, MDL);
2824  lp -> flags |= ON_ACK_QUEUE;
2825  lease_dereference (&lp, MDL);
2826 
2827  /* Count the object as an unacked update. */
2828  state -> cur_unacked_updates++;
2829  }
2830  return ISC_R_SUCCESS;
2831 }
2832 
2833 /* Queue an update for a lease. Always returns 1 at this point - it's
2834  not an error for this to be called on a lease for which there's no
2835  failover peer. */
2836 
2837 int dhcp_failover_queue_update (struct lease *lease, int immediate)
2838 {
2839  dhcp_failover_state_t *state;
2840 
2841  if (!lease -> pool ||
2842  !lease -> pool -> failover_peer)
2843  return 1;
2844 
2845  /* If it's already on the update queue, leave it there. */
2846  if (lease -> flags & ON_UPDATE_QUEUE)
2847  return 1;
2848 
2849  /* Get the failover state structure for this lease. */
2850  state = lease -> pool -> failover_peer;
2851 
2852  /* If it's on the ack queue, take it off. */
2853  if (lease -> flags & ON_ACK_QUEUE)
2854  dhcp_failover_ack_queue_remove (state, lease);
2855 
2856  if (state -> update_queue_head) {
2857  lease_reference (&state -> update_queue_tail -> next_pending,
2858  lease, MDL);
2859  lease_dereference (&state -> update_queue_tail, MDL);
2860  } else {
2861  lease_reference (&state -> update_queue_head, lease, MDL);
2862  }
2863 #if defined (POINTER_DEBUG)
2864  if (lease -> next_pending) {
2865  log_error ("next pending on update queue lease.");
2866 #if defined (DEBUG_RC_HISTORY)
2867  dump_rc_history (lease);
2868 #endif
2869  abort ();
2870  }
2871 #endif
2872  lease_reference (&state -> update_queue_tail, lease, MDL);
2873  lease -> flags |= ON_UPDATE_QUEUE;
2874  if (immediate)
2876  return 1;
2877 }
2878 
2879 int dhcp_failover_send_acks (dhcp_failover_state_t *state)
2880 {
2881  failover_message_t *msg = (failover_message_t *)0;
2882 
2883  /* Must commit all leases prior to acking them. */
2884  if (!commit_leases ())
2885  return 0;
2886 
2887  while (state -> toack_queue_head) {
2888  failover_message_reference
2889  (&msg, state -> toack_queue_head, MDL);
2890  failover_message_dereference
2891  (&state -> toack_queue_head, MDL);
2892  if (msg -> next) {
2893  failover_message_reference
2894  (&state -> toack_queue_head, msg -> next, MDL);
2895  }
2896 
2897  dhcp_failover_send_bind_ack (state, msg, 0, (const char *)0);
2898 
2899  failover_message_dereference (&msg, MDL);
2900  }
2901 
2902  if (state -> toack_queue_tail)
2903  failover_message_dereference (&state -> toack_queue_tail, MDL);
2904  state -> pending_acks = 0;
2905 
2906  return 1;
2907 }
2908 
2909 void dhcp_failover_toack_queue_timeout (void *vs)
2910 {
2911  dhcp_failover_state_t *state = vs;
2912 
2913 #if defined (DEBUG_FAILOVER_TIMING)
2914  log_info ("dhcp_failover_toack_queue_timeout");
2915 #endif
2916 
2917  dhcp_failover_send_acks (state);
2918 }
2919 
2920 /* Queue an ack for a message. There is currently no way to queue a
2921  negative ack -- these need to be sent directly. */
2922 
2923 int dhcp_failover_queue_ack (dhcp_failover_state_t *state,
2924  failover_message_t *msg)
2925 {
2926  struct timeval tv;
2927 
2928  if (state -> toack_queue_head) {
2929  failover_message_reference
2930  (&state -> toack_queue_tail -> next, msg, MDL);
2931  failover_message_dereference (&state -> toack_queue_tail, MDL);
2932  } else {
2933  failover_message_reference (&state -> toack_queue_head,
2934  msg, MDL);
2935  }
2936  failover_message_reference (&state -> toack_queue_tail, msg, MDL);
2937 
2938  state -> pending_acks++;
2939 
2940  /* Flush the toack queue whenever we exceed half the number of
2941  allowed unacked updates. */
2942  if (state -> pending_acks >= state -> partner.max_flying_updates / 2) {
2943  dhcp_failover_send_acks (state);
2944  }
2945 
2946  /* Schedule a timeout to flush the ack queue. */
2947  if (state -> pending_acks > 0) {
2948 #if defined (DEBUG_FAILOVER_TIMING)
2949  log_info ("add_timeout +2 %s",
2950  "dhcp_failover_toack_queue_timeout");
2951 #endif
2952  tv . tv_sec = cur_time + 2;
2953  tv . tv_usec = 0;
2954  add_timeout (&tv,
2956  (tvref_t)dhcp_failover_state_reference,
2957  (tvunref_t)dhcp_failover_state_dereference);
2958  }
2959 
2960  return 1;
2961 }
2962 
2963 void dhcp_failover_ack_queue_remove (dhcp_failover_state_t *state,
2964  struct lease *lease)
2965 {
2966  struct lease *lp;
2967 
2968  if (!(lease -> flags & ON_ACK_QUEUE))
2969  return;
2970 
2971  if (state -> ack_queue_head == lease) {
2972  lease_dereference (&state -> ack_queue_head, MDL);
2973  if (lease -> next_pending) {
2974  lease_reference (&state -> ack_queue_head,
2975  lease -> next_pending, MDL);
2976  lease_dereference (&lease -> next_pending, MDL);
2977  } else {
2978  lease_dereference (&state -> ack_queue_tail, MDL);
2979  }
2980  } else {
2981  for (lp = state -> ack_queue_head;
2982  lp && lp -> next_pending != lease;
2983  lp = lp -> next_pending)
2984  ;
2985 
2986  if (!lp)
2987  return;
2988 
2989  lease_dereference (&lp -> next_pending, MDL);
2990  if (lease -> next_pending) {
2991  lease_reference (&lp -> next_pending,
2992  lease -> next_pending, MDL);
2993  lease_dereference (&lease -> next_pending, MDL);
2994  } else {
2995  lease_dereference (&state -> ack_queue_tail, MDL);
2996  if (lp -> next_pending) {
2997  log_error ("state -> ack_queue_tail");
2998  abort ();
2999  }
3000  lease_reference (&state -> ack_queue_tail, lp, MDL);
3001  }
3002  }
3003 
3004  lease -> flags &= ~ON_ACK_QUEUE;
3005  /* Multiple acks on one XID is an error and may cause badness. */
3006  lease->last_xid = 0;
3007  /* XXX: this violates draft-failover. We can't send another
3008  * update just because we forgot about an old one that hasn't
3009  * been acked yet.
3010  */
3011  state -> cur_unacked_updates--;
3012 
3013  /*
3014  * When updating leases as a result of an ack, we defer the commit
3015  * for performance reasons. When there are no more acks pending,
3016  * do a commit.
3017  */
3018  if (state -> cur_unacked_updates == 0) {
3019  commit_leases();
3020  }
3021 }
3022 
3024  omapi_object_t *id,
3025  omapi_data_string_t *name,
3026  omapi_typed_data_t *value)
3027 {
3028  isc_result_t status;
3029 
3030  if (h -> type != dhcp_type_failover_state)
3031  return DHCP_R_INVALIDARG;
3032 
3033  /* This list of successful returns is completely wrong, but the
3034  fastest way to make dhcpctl do something vaguely sane when
3035  you try to change the local state. */
3036 
3037  if (!omapi_ds_strcmp (name, "name")) {
3038  return ISC_R_SUCCESS;
3039  } else if (!omapi_ds_strcmp (name, "partner-address")) {
3040  return ISC_R_SUCCESS;
3041  } else if (!omapi_ds_strcmp (name, "local-address")) {
3042  return ISC_R_SUCCESS;
3043  } else if (!omapi_ds_strcmp (name, "partner-port")) {
3044  return ISC_R_SUCCESS;
3045  } else if (!omapi_ds_strcmp (name, "local-port")) {
3046  return ISC_R_SUCCESS;
3047  } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
3048  return ISC_R_SUCCESS;
3049  } else if (!omapi_ds_strcmp (name, "mclt")) {
3050  return ISC_R_SUCCESS;
3051  } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
3052  return ISC_R_SUCCESS;
3053  } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
3054  return ISC_R_SUCCESS;
3055  } else if (!omapi_ds_strcmp (name, "partner-state")) {
3056  return ISC_R_SUCCESS;
3057  } else if (!omapi_ds_strcmp (name, "local-state")) {
3058  unsigned long l;
3059  status = omapi_get_int_value (&l, value);
3060  if (status != ISC_R_SUCCESS)
3061  return status;
3062  return dhcp_failover_set_state ((dhcp_failover_state_t *)h, l);
3063  } else if (!omapi_ds_strcmp (name, "partner-stos")) {
3064  return ISC_R_SUCCESS;
3065  } else if (!omapi_ds_strcmp (name, "local-stos")) {
3066  return ISC_R_SUCCESS;
3067  } else if (!omapi_ds_strcmp (name, "hierarchy")) {
3068  return ISC_R_SUCCESS;
3069  } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
3070  return ISC_R_SUCCESS;
3071  } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
3072  return ISC_R_SUCCESS;
3073  } else if (!omapi_ds_strcmp (name, "skew")) {
3074  return ISC_R_SUCCESS;
3075  } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
3076  return ISC_R_SUCCESS;
3077  } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
3078  return ISC_R_SUCCESS;
3079  }
3080 
3081  if (h -> inner && h -> inner -> type -> set_value)
3082  return (*(h -> inner -> type -> set_value))
3083  (h -> inner, id, name, value);
3084  return ISC_R_NOTFOUND;
3085 }
3086 
3087 void dhcp_failover_keepalive (void *vs)
3088 {
3089 }
3090 
3091 void dhcp_failover_reconnect (void *vs)
3092 {
3093  dhcp_failover_state_t *state = vs;
3094  isc_result_t status;
3095  struct timeval tv;
3096 
3097 #if defined (DEBUG_FAILOVER_TIMING)
3098  log_info ("dhcp_failover_reconnect");
3099 #endif
3100  /* If we already connected the other way, let the connection
3101  recovery code initiate any retry that may be required. */
3102  if (state -> link_to_peer)
3103  return;
3104 
3105  status = dhcp_failover_link_initiate ((omapi_object_t *)state);
3106  if (status != ISC_R_SUCCESS && status != DHCP_R_INCOMPLETE) {
3107  log_info ("failover peer %s: %s", state -> name,
3108  isc_result_totext (status));
3109 #if defined (DEBUG_FAILOVER_TIMING)
3110  log_info("add_timeout +90 dhcp_failover_reconnect");
3111 #endif
3112  tv . tv_sec = cur_time + 90;
3113  tv . tv_usec = 0;
3115  (tvref_t)dhcp_failover_state_reference,
3116  (tvunref_t)dhcp_failover_state_dereference);
3117  }
3118 }
3119 
3120 void dhcp_failover_startup_timeout (void *vs)
3121 {
3122  dhcp_failover_state_t *state = vs;
3123 
3124 #if defined (DEBUG_FAILOVER_TIMING)
3125  log_info ("dhcp_failover_startup_timeout");
3126 #endif
3127 
3128  dhcp_failover_state_transition (state, "disconnect");
3129 }
3130 
3131 void dhcp_failover_link_startup_timeout (void *vl)
3132 {
3133  dhcp_failover_link_t *link = vl;
3134  omapi_object_t *p;
3135 
3136  for (p = (omapi_object_t *)link; p -> inner; p = p -> inner)
3137  ;
3138  for (; p; p = p -> outer)
3139  if (p -> type == omapi_type_connection)
3140  break;
3141  if (p) {
3142  log_info ("failover: link startup timeout");
3143  omapi_disconnect (p, 1);
3144  }
3145 }
3146 
3147 void dhcp_failover_listener_restart (void *vs)
3148 {
3149  dhcp_failover_state_t *state = vs;
3150  isc_result_t status;
3151  struct timeval tv;
3152 
3153 #if defined (DEBUG_FAILOVER_TIMING)
3154  log_info ("dhcp_failover_listener_restart");
3155 #endif
3156 
3157  status = dhcp_failover_listen ((omapi_object_t *)state);
3158  if (status != ISC_R_SUCCESS) {
3159  log_info ("failover peer %s: %s", state -> name,
3160  isc_result_totext (status));
3161 #if defined (DEBUG_FAILOVER_TIMING)
3162  log_info ("add_timeout +90 %s",
3163  "dhcp_failover_listener_restart");
3164 #endif
3165  tv . tv_sec = cur_time + 90;
3166  tv . tv_usec = 0;
3167  add_timeout (&tv,
3169  (tvref_t)dhcp_failover_state_reference,
3170  (tvunref_t)dhcp_failover_state_dereference);
3171  }
3172 }
3173 
3174 void
3176 {
3177  dhcp_failover_state_t *state = vs;
3178 
3179 #if defined (DEBUG_FAILOVER_TIMING)
3180  log_info("dhcp_failover_auto_partner_down");
3181 #endif
3182 
3184 }
3185 
3187  omapi_object_t *id,
3188  omapi_data_string_t *name,
3189  omapi_value_t **value)
3190 {
3191  dhcp_failover_state_t *s;
3192  struct option_cache *oc;
3193  struct data_string ds;
3194  isc_result_t status;
3195 
3196  if (h -> type != dhcp_type_failover_state)
3197  return DHCP_R_INVALIDARG;
3198  s = (dhcp_failover_state_t *)h;
3199 
3200  if (!omapi_ds_strcmp (name, "name")) {
3201  if (s -> name)
3202  return omapi_make_string_value (value,
3203  name, s -> name, MDL);
3204  return ISC_R_NOTFOUND;
3205  } else if (!omapi_ds_strcmp (name, "partner-address")) {
3206  oc = s -> partner.address;
3207  getaddr:
3208  memset (&ds, 0, sizeof ds);
3209  if (!evaluate_option_cache (&ds, (struct packet *)0,
3210  (struct lease *)0,
3211  (struct client_state *)0,
3212  (struct option_state *)0,
3213  (struct option_state *)0,
3214  &global_scope, oc, MDL)) {
3215  return ISC_R_NOTFOUND;
3216  }
3217  status = omapi_make_const_value (value,
3218  name, ds.data, ds.len, MDL);
3219  /* Disgusting kludge: */
3220  if (oc == s -> me.address && !s -> server_identifier.len)
3221  data_string_copy (&s -> server_identifier, &ds, MDL);
3222  data_string_forget (&ds, MDL);
3223  return status;
3224  } else if (!omapi_ds_strcmp (name, "local-address")) {
3225  oc = s -> me.address;
3226  goto getaddr;
3227  } else if (!omapi_ds_strcmp (name, "partner-port")) {
3228  return omapi_make_int_value (value, name,
3229  s -> partner.port, MDL);
3230  } else if (!omapi_ds_strcmp (name, "local-port")) {
3231  return omapi_make_int_value (value,
3232  name, s -> me.port, MDL);
3233  } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
3234  return omapi_make_uint_value (value, name,
3235  s -> me.max_flying_updates,
3236  MDL);
3237  } else if (!omapi_ds_strcmp (name, "mclt")) {
3238  return omapi_make_uint_value (value, name, s -> mclt, MDL);
3239  } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
3240  return omapi_make_int_value (value, name,
3241  s -> load_balance_max_secs, MDL);
3242  } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
3243  if (s -> hba)
3244  return omapi_make_const_value (value, name,
3245  s -> hba, 32, MDL);
3246  return ISC_R_NOTFOUND;
3247  } else if (!omapi_ds_strcmp (name, "partner-state")) {
3248  return omapi_make_uint_value (value, name,
3249  s -> partner.state, MDL);
3250  } else if (!omapi_ds_strcmp (name, "local-state")) {
3251  return omapi_make_uint_value (value, name,
3252  s -> me.state, MDL);
3253  } else if (!omapi_ds_strcmp (name, "partner-stos")) {
3254  return omapi_make_int_value (value, name,
3255  s -> partner.stos, MDL);
3256  } else if (!omapi_ds_strcmp (name, "local-stos")) {
3257  return omapi_make_int_value (value, name,
3258  s -> me.stos, MDL);
3259  } else if (!omapi_ds_strcmp (name, "hierarchy")) {
3260  return omapi_make_uint_value (value, name, s -> i_am, MDL);
3261  } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
3262  return omapi_make_int_value (value, name,
3263  s -> last_packet_sent, MDL);
3264  } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
3265  return omapi_make_int_value (value, name,
3266  s -> last_timestamp_received,
3267  MDL);
3268  } else if (!omapi_ds_strcmp (name, "skew")) {
3269  return omapi_make_int_value (value, name, s -> skew, MDL);
3270  } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
3271  return omapi_make_uint_value (value, name,
3272  s -> me.max_response_delay,
3273  MDL);
3274  } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
3275  return omapi_make_int_value (value, name,
3276  s -> cur_unacked_updates, MDL);
3277  }
3278 
3279  if (h -> inner && h -> inner -> type -> get_value)
3280  return (*(h -> inner -> type -> get_value))
3281  (h -> inner, id, name, value);
3282  return ISC_R_NOTFOUND;
3283 }
3284 
3286  const char *file, int line)
3287 {
3288  dhcp_failover_state_t *s;
3289 
3290  if (h -> type != dhcp_type_failover_state)
3291  return DHCP_R_INVALIDARG;
3292  s = (dhcp_failover_state_t *)h;
3293 
3294  if (s -> link_to_peer)
3295  dhcp_failover_link_dereference (&s -> link_to_peer, file, line);
3296  if (s -> name) {
3297  dfree (s -> name, MDL);
3298  s -> name = (char *)0;
3299  }
3300  if (s -> partner.address)
3301  option_cache_dereference (&s -> partner.address, file, line);
3302  if (s -> me.address)
3303  option_cache_dereference (&s -> me.address, file, line);
3304  if (s -> hba) {
3305  dfree (s -> hba, file, line);
3306  s -> hba = (u_int8_t *)0;
3307  }
3308  if (s -> update_queue_head)
3309  lease_dereference (&s -> update_queue_head, file, line);
3310  if (s -> update_queue_tail)
3311  lease_dereference (&s -> update_queue_tail, file, line);
3312  if (s -> ack_queue_head)
3313  lease_dereference (&s -> ack_queue_head, file, line);
3314  if (s -> ack_queue_tail)
3315  lease_dereference (&s -> ack_queue_tail, file, line);
3316  if (s -> send_update_done)
3317  lease_dereference (&s -> send_update_done, file, line);
3318  if (s -> toack_queue_head)
3319  failover_message_dereference (&s -> toack_queue_head,
3320  file, line);
3321  if (s -> toack_queue_tail)
3322  failover_message_dereference (&s -> toack_queue_tail,
3323  file, line);
3324  return ISC_R_SUCCESS;
3325 }
3326 
3327 /* Write all the published values associated with the object through the
3328  specified connection. */
3329 
3330 isc_result_t dhcp_failover_state_stuff (omapi_object_t *c,
3331  omapi_object_t *id,
3332  omapi_object_t *h)
3333 {
3334  /* In this function c should be a (omapi_connection_object_t *) */
3335 
3336  dhcp_failover_state_t *s;
3337  isc_result_t status;
3338 
3339  if (c -> type != omapi_type_connection)
3340  return DHCP_R_INVALIDARG;
3341 
3342  if (h -> type != dhcp_type_failover_state)
3343  return DHCP_R_INVALIDARG;
3344  s = (dhcp_failover_state_t *)h;
3345 
3346  status = omapi_connection_put_name (c, "name");
3347  if (status != ISC_R_SUCCESS)
3348  return status;
3349  status = omapi_connection_put_string (c, s -> name);
3350  if (status != ISC_R_SUCCESS)
3351  return status;
3352 
3353  status = omapi_connection_put_name (c, "partner-address");
3354  if (status != ISC_R_SUCCESS)
3355  return status;
3356  status = omapi_connection_put_uint32 (c, sizeof s -> partner.address);
3357  if (status != ISC_R_SUCCESS)
3358  return status;
3359  status = omapi_connection_copyin (c, (u_int8_t *)&s -> partner.address,
3360  sizeof s -> partner.address);
3361  if (status != ISC_R_SUCCESS)
3362  return status;
3363 
3364  status = omapi_connection_put_name (c, "partner-port");
3365  if (status != ISC_R_SUCCESS)
3366  return status;
3367  status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3368  if (status != ISC_R_SUCCESS)
3369  return status;
3370  status = omapi_connection_put_uint32 (c, (u_int32_t)s -> partner.port);
3371  if (status != ISC_R_SUCCESS)
3372  return status;
3373 
3374  status = omapi_connection_put_name (c, "local-address");
3375  if (status != ISC_R_SUCCESS)
3376  return status;
3377  status = omapi_connection_put_uint32 (c, sizeof s -> me.address);
3378  if (status != ISC_R_SUCCESS)
3379  return status;
3380  status = omapi_connection_copyin (c, (u_int8_t *)&s -> me.address,
3381  sizeof s -> me.address);
3382  if (status != ISC_R_SUCCESS)
3383  return status;
3384 
3385  status = omapi_connection_put_name (c, "local-port");
3386  if (status != ISC_R_SUCCESS)
3387  return status;
3388  status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3389  if (status != ISC_R_SUCCESS)
3390  return status;
3391  status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.port);
3392  if (status != ISC_R_SUCCESS)
3393  return status;
3394 
3395  status = omapi_connection_put_name (c, "max-outstanding-updates");
3396  if (status != ISC_R_SUCCESS)
3397  return status;
3398  status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3399  if (status != ISC_R_SUCCESS)
3400  return status;
3401  status = omapi_connection_put_uint32 (c,
3402  s -> me.max_flying_updates);
3403  if (status != ISC_R_SUCCESS)
3404  return status;
3405 
3406  status = omapi_connection_put_name (c, "mclt");
3407  if (status != ISC_R_SUCCESS)
3408  return status;
3409  status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3410  if (status != ISC_R_SUCCESS)
3411  return status;
3412  status = omapi_connection_put_uint32 (c, s -> mclt);
3413  if (status != ISC_R_SUCCESS)
3414  return status;
3415 
3416  status = omapi_connection_put_name (c, "load-balance-max-secs");
3417  if (status != ISC_R_SUCCESS)
3418  return status;
3419  status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3420  if (status != ISC_R_SUCCESS)
3421  return status;
3422  status = (omapi_connection_put_uint32
3423  (c, (u_int32_t)s -> load_balance_max_secs));
3424  if (status != ISC_R_SUCCESS)
3425  return status;
3426 
3427 
3428  if (s -> hba) {
3429  status = omapi_connection_put_name (c, "load-balance-hba");
3430  if (status != ISC_R_SUCCESS)
3431  return status;
3432  status = omapi_connection_put_uint32 (c, 32);
3433  if (status != ISC_R_SUCCESS)
3434  return status;
3435  status = omapi_connection_copyin (c, s -> hba, 32);
3436  if (status != ISC_R_SUCCESS)
3437  return status;
3438  }
3439 
3440  status = omapi_connection_put_name (c, "partner-state");
3441  if (status != ISC_R_SUCCESS)
3442  return status;
3443  status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3444  if (status != ISC_R_SUCCESS)
3445  return status;
3446  status = omapi_connection_put_uint32 (c, s -> partner.state);
3447  if (status != ISC_R_SUCCESS)
3448  return status;
3449 
3450  status = omapi_connection_put_name (c, "local-state");
3451  if (status != ISC_R_SUCCESS)
3452  return status;
3453  status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3454  if (status != ISC_R_SUCCESS)
3455  return status;
3456  status = omapi_connection_put_uint32 (c, s -> me.state);
3457  if (status != ISC_R_SUCCESS)
3458  return status;
3459 
3460  status = omapi_connection_put_name (c, "partner-stos");
3461  if (status != ISC_R_SUCCESS)
3462  return status;
3463  status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3464  if (status != ISC_R_SUCCESS)
3465  return status;
3466  status = omapi_connection_put_uint32 (c,
3467  (u_int32_t)s -> partner.stos);
3468  if (status != ISC_R_SUCCESS)
3469  return status;
3470 
3471  status = omapi_connection_put_name (c, "local-stos");
3472  if (status != ISC_R_SUCCESS)
3473  return status;
3474  status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3475  if (status != ISC_R_SUCCESS)
3476  return status;
3477  status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.stos);
3478  if (status != ISC_R_SUCCESS)
3479  return status;
3480 
3481  status = omapi_connection_put_name (c, "hierarchy");
3482  if (status != ISC_R_SUCCESS)
3483  return status;
3484  status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3485  if (status != ISC_R_SUCCESS)
3486  return status;
3487  status = omapi_connection_put_uint32 (c, s -> i_am);
3488  if (status != ISC_R_SUCCESS)
3489  return status;
3490 
3491  status = omapi_connection_put_name (c, "last-packet-sent");
3492  if (status != ISC_R_SUCCESS)
3493  return status;
3494  status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3495  if (status != ISC_R_SUCCESS)
3496  return status;
3497  status = (omapi_connection_put_uint32
3498  (c, (u_int32_t)s -> last_packet_sent));
3499  if (status != ISC_R_SUCCESS)
3500  return status;
3501 
3502  status = omapi_connection_put_name (c, "last-timestamp-received");
3503  if (status != ISC_R_SUCCESS)
3504  return status;
3505  status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3506  if (status != ISC_R_SUCCESS)
3507  return status;
3508  status = (omapi_connection_put_uint32
3509  (c, (u_int32_t)s -> last_timestamp_received));
3510  if (status != ISC_R_SUCCESS)
3511  return status;
3512 
3513  status = omapi_connection_put_name (c, "skew");
3514  if (status != ISC_R_SUCCESS)
3515  return status;
3516  status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3517  if (status != ISC_R_SUCCESS)
3518  return status;
3519  status = omapi_connection_put_uint32 (c, (u_int32_t)s -> skew);
3520  if (status != ISC_R_SUCCESS)
3521  return status;
3522 
3523  status = omapi_connection_put_name (c, "max-response-delay");
3524  if (status != ISC_R_SUCCESS)
3525  return status;
3526  status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3527  if (status != ISC_R_SUCCESS)
3528  return status;
3529  status = (omapi_connection_put_uint32
3530  (c, (u_int32_t)s -> me.max_response_delay));
3531  if (status != ISC_R_SUCCESS)
3532  return status;
3533 
3534  status = omapi_connection_put_name (c, "cur-unacked-updates");
3535  if (status != ISC_R_SUCCESS)
3536  return status;
3537  status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3538  if (status != ISC_R_SUCCESS)
3539  return status;
3540  status = (omapi_connection_put_uint32
3541  (c, (u_int32_t)s -> cur_unacked_updates));
3542  if (status != ISC_R_SUCCESS)
3543  return status;
3544 
3545  if (h -> inner && h -> inner -> type -> stuff_values)
3546  return (*(h -> inner -> type -> stuff_values)) (c, id,
3547  h -> inner);
3548  return ISC_R_SUCCESS;
3549 }
3550 
3551 isc_result_t dhcp_failover_state_lookup (omapi_object_t **sp,
3552  omapi_object_t *id,
3553  omapi_object_t *ref)
3554 {
3555  omapi_value_t *tv = (omapi_value_t *)0;
3556  isc_result_t status;
3557  dhcp_failover_state_t *s;
3558 
3559  if (!ref)
3560  return DHCP_R_NOKEYS;
3561 
3562  /* First see if we were sent a handle. */
3563  status = omapi_get_value_str (ref, id, "handle", &tv);
3564  if (status == ISC_R_SUCCESS) {
3565  status = omapi_handle_td_lookup (sp, tv -> value);
3566 
3568  if (status != ISC_R_SUCCESS)
3569  return status;
3570 
3571  /* Don't return the object if the type is wrong. */
3572  if ((*sp) -> type != dhcp_type_failover_state) {
3574  return DHCP_R_INVALIDARG;
3575  }
3576  }
3577 
3578  /* Look the failover state up by peer name. */
3579  status = omapi_get_value_str (ref, id, "name", &tv);
3580  if (status == ISC_R_SUCCESS) {
3581  for (s = failover_states; s; s = s -> next) {
3582  unsigned l = strlen (s -> name);
3583  if (l == tv -> value -> u.buffer.len &&
3584  !memcmp (s -> name,
3585  tv -> value -> u.buffer.value, l))
3586  break;
3587  }
3589 
3590  /* If we already have a lease, and it's not the same one,
3591  then the query was invalid. */
3592  if (*sp && *sp != (omapi_object_t *)s) {
3594  return DHCP_R_KEYCONFLICT;
3595  } else if (!s) {
3596  if (*sp)
3598  return ISC_R_NOTFOUND;
3599  } else if (!*sp)
3600  /* XXX fix so that hash lookup itself creates
3601  XXX the reference. */
3603  }
3604 
3605  /* If we get to here without finding a lease, no valid key was
3606  specified. */
3607  if (!*sp)
3608  return DHCP_R_NOKEYS;
3609  return ISC_R_SUCCESS;
3610 }
3611 
3612 isc_result_t dhcp_failover_state_create (omapi_object_t **sp,
3613  omapi_object_t *id)
3614 {
3615  return ISC_R_NOTIMPLEMENTED;
3616 }
3617 
3618 isc_result_t dhcp_failover_state_remove (omapi_object_t *sp,
3619  omapi_object_t *id)
3620 {
3621  return ISC_R_NOTIMPLEMENTED;
3622 }
3623 
3624 int dhcp_failover_state_match (dhcp_failover_state_t *state,
3625  u_int8_t *addr, unsigned addrlen)
3626 {
3627  struct data_string ds;
3628  int i;
3629 
3630  memset (&ds, 0, sizeof ds);
3631  if (evaluate_option_cache (&ds, (struct packet *)0,
3632  (struct lease *)0,
3633  (struct client_state *)0,
3634  (struct option_state *)0,
3635  (struct option_state *)0,
3636  &global_scope,
3637  state -> partner.address, MDL)) {
3638  for (i = 0; i + addrlen - 1 < ds.len; i += addrlen) {
3639  if (!memcmp (&ds.data [i],
3640  addr, addrlen)) {
3641  data_string_forget (&ds, MDL);
3642  return 1;
3643  }
3644  }
3645  data_string_forget (&ds, MDL);
3646  }
3647  return 0;
3648 }
3649 
3650 int
3652  dhcp_failover_state_t *state;
3653  failover_option_t *name;
3654 {
3655  if ((strlen(state->name) == name->count) &&
3656  (memcmp(state->name, name->data, name->count) == 0))
3657  return 1;
3658 
3659  return 0;
3660 }
3661 
3662 const char *dhcp_failover_reject_reason_print (int reason)
3663 {
3664  static char resbuf[sizeof("Undefined-255: This reason code is not defined "
3665  "in the protocol standard.")];
3666 
3667  if ((reason > 0xff) || (reason < 0))
3668  return "Reason code out of range.";
3669 
3670  switch (reason) {
3671  case FTR_ILLEGAL_IP_ADDR:
3672  return "Illegal IP address (not part of any address pool).";
3673 
3674  case FTR_FATAL_CONFLICT:
3675  return "Fatal conflict exists: address in use by other client.";
3676 
3677  case FTR_MISSING_BINDINFO:
3678  return "Missing binding information.";
3679 
3680  case FTR_TIMEMISMATCH:
3681  return "Connection rejected, time mismatch too great.";
3682 
3683  case FTR_INVALID_MCLT:
3684  return "Connection rejected, invalid MCLT.";
3685 
3686  case FTR_MISC_REJECT:
3687  return "Connection rejected, unknown reason.";
3688 
3689  case FTR_DUP_CONNECTION:
3690  return "Connection rejected, duplicate connection.";
3691 
3692  case FTR_INVALID_PARTNER:
3693  return "Connection rejected, invalid failover partner.";
3694 
3695  case FTR_TLS_UNSUPPORTED:
3696  return "TLS not supported.";
3697 
3698  case FTR_TLS_UNCONFIGURED:
3699  return "TLS supported but not configured.";
3700 
3701  case FTR_TLS_REQUIRED:
3702  return "TLS required but not supported by partner.";
3703 
3704  case FTR_DIGEST_UNSUPPORTED:
3705  return "Message digest not supported.";
3706 
3707  case FTR_DIGEST_UNCONFIGURED:
3708  return "Message digest not configured.";
3709 
3710  case FTR_VERSION_MISMATCH:
3711  return "Protocol version mismatch.";
3712 
3713  case FTR_OUTDATED_BIND_INFO:
3714  return "Outdated binding information.";
3715 
3716  case FTR_LESS_CRIT_BIND_INFO:
3717  return "Less critical binding information.";
3718 
3719  case FTR_NO_TRAFFIC:
3720  return "No traffic within sufficient time.";
3721 
3722  case FTR_HBA_CONFLICT:
3723  return "Hash bucket assignment conflict.";
3724 
3725  case FTR_IP_NOT_RESERVED:
3726  return "IP not reserved on this server.";
3727 
3728  case FTR_IP_DIGEST_FAILURE:
3729  return "Message digest failed to compare.";
3730 
3731  case FTR_IP_MISSING_DIGEST:
3732  return "Missing message digest.";
3733 
3734  case FTR_UNKNOWN:
3735  return "Unknown Error.";
3736 
3737  default:
3738  sprintf(resbuf, "Undefined-%d: This reason code is not defined in the "
3739  "protocol standard.", reason);
3740  return resbuf;
3741  }
3742 }
3743 
3744 const char *dhcp_failover_state_name_print (enum failover_state state)
3745 {
3746  switch (state) {
3747  default:
3748  case unknown_state:
3749  return "unknown-state";
3750 
3751  case partner_down:
3752  return "partner-down";
3753 
3754  case normal:
3755  return "normal";
3756 
3757  case conflict_done:
3758  return "conflict-done";
3759 
3761  return "communications-interrupted";
3762 
3764  return "resolution-interrupted";
3765 
3766  case potential_conflict:
3767  return "potential-conflict";
3768 
3769  case recover:
3770  return "recover";
3771 
3772  case recover_done:
3773  return "recover-done";
3774 
3775  case recover_wait:
3776  return "recover-wait";
3777 
3778  case shut_down:
3779  return "shutdown";
3780 
3781  case paused:
3782  return "paused";
3783 
3784  case startup:
3785  return "startup";
3786  }
3787 }
3788 
3789 const char *dhcp_failover_message_name (unsigned type)
3790 {
3791  static char messbuf[sizeof("unknown-message-255")];
3792 
3793  if (type > 0xff)
3794  return "invalid-message";
3795 
3796  switch (type) {
3797  case FTM_POOLREQ:
3798  return "pool-request";
3799 
3800  case FTM_POOLRESP:
3801  return "pool-response";
3802 
3803  case FTM_BNDUPD:
3804  return "bind-update";
3805 
3806  case FTM_BNDACK:
3807  return "bind-ack";
3808 
3809  case FTM_CONNECT:
3810  return "connect";
3811 
3812  case FTM_CONNECTACK:
3813  return "connect-ack";
3814 
3815  case FTM_UPDREQ:
3816  return "update-request";
3817 
3818  case FTM_UPDDONE:
3819  return "update-done";
3820 
3821  case FTM_UPDREQALL:
3822  return "update-request-all";
3823 
3824  case FTM_STATE:
3825  return "state";
3826 
3827  case FTM_CONTACT:
3828  return "contact";
3829 
3830  case FTM_DISCONNECT:
3831  return "disconnect";
3832 
3833  default:
3834  sprintf(messbuf, "unknown-message-%u", type);
3835  return messbuf;
3836  }
3837 }
3838 
3839 const char *dhcp_failover_option_name (unsigned type)
3840 {
3841  static char optbuf[sizeof("unknown-option-65535")];
3842 
3843  if (type > 0xffff)
3844  return "invalid-option";
3845 
3846  switch (type) {
3847  case FTO_ADDRESSES_TRANSFERRED:
3848  return "addresses-transferred";
3849 
3850  case FTO_ASSIGNED_IP_ADDRESS:
3851  return "assigned-ip-address";
3852 
3853  case FTO_BINDING_STATUS:
3854  return "binding-status";
3855 
3856  case FTO_CLIENT_IDENTIFIER:
3857  return "client-identifier";
3858 
3859  case FTO_CHADDR:
3860  return "chaddr";
3861 
3862  case FTO_CLTT:
3863  return "cltt";
3864 
3865  case FTO_DDNS:
3866  return "ddns";
3867 
3868  case FTO_DELAYED_SERVICE:
3869  return "delayed-service";
3870 
3871  case FTO_HBA:
3872  return "hba";
3873 
3874  case FTO_IP_FLAGS:
3875  return "ip-flags";
3876 
3877  case FTO_LEASE_EXPIRY:
3878  return "lease-expiry";
3879 
3880  case FTO_MAX_UNACKED:
3881  return "max-unacked";
3882 
3883  case FTO_MCLT:
3884  return "mclt";
3885 
3886  case FTO_MESSAGE:
3887  return "message";
3888 
3889  case FTO_MESSAGE_DIGEST:
3890  return "message-digest";
3891 
3892  case FTO_POTENTIAL_EXPIRY:
3893  return "potential-expiry";
3894 
3895  case FTO_PROTOCOL_VERSION:
3896  return "protocol-version";
3897 
3898  case FTO_RECEIVE_TIMER:
3899  return "receive-timer";
3900 
3901  case FTO_REJECT_REASON:
3902  return "reject-reason";
3903 
3904  case FTO_RELATIONSHIP_NAME:
3905  return "relationship-name";
3906 
3907  case FTO_REPLY_OPTIONS:
3908  return "reply-options";
3909 
3910  case FTO_REQUEST_OPTIONS:
3911  return "request-options";
3912 
3913  case FTO_SERVER_FLAGS:
3914  return "server-flags";
3915 
3916  case FTO_SERVER_STATE:
3917  return "server-state";
3918 
3919  case FTO_STOS:
3920  return "stos";
3921 
3922  case FTO_TLS_REPLY:
3923  return "tls-reply";
3924 
3925  case FTO_TLS_REQUEST:
3926  return "tls-request";
3927 
3928  case FTO_VENDOR_CLASS:
3929  return "vendor-class";
3930 
3931  case FTO_VENDOR_OPTIONS:
3932  return "vendor-options";
3933 
3934  default:
3935  sprintf(optbuf, "unknown-option-%u", type);
3936  return optbuf;
3937  }
3938 }
3939 
3940 failover_option_t *dhcp_failover_option_printf (unsigned code,
3941  char *obuf,
3942  unsigned *obufix,
3943  unsigned obufmax,
3944  const char *fmt, ...)
3945 {
3946  va_list va;
3947  char tbuf [256];
3948 
3949  /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3950  * It is unclear what the effects of truncation here are, or
3951  * how that condition should be handled. It seems that this
3952  * function is used for formatting messages in the failover
3953  * command channel. For now the safest thing is for
3954  * overflow-truncation to cause a fatal log.
3955  */
3956  va_start (va, fmt);
3957  if (vsnprintf (tbuf, sizeof tbuf, fmt, va) >= sizeof tbuf)
3958  log_fatal ("%s: vsnprintf would truncate",
3959  "dhcp_failover_make_option");
3960  va_end (va);
3961 
3962  return dhcp_failover_make_option (code, obuf, obufix, obufmax,
3963  strlen (tbuf), tbuf);
3964 }
3965 
3966 failover_option_t *dhcp_failover_make_option (unsigned code,
3967  char *obuf, unsigned *obufix,
3968  unsigned obufmax, ...)
3969 {
3970  va_list va;
3971  struct failover_option_info *info;
3972  int i;
3973  unsigned size, count;
3974  unsigned val;
3975  u_int8_t *iaddr;
3976  unsigned ilen = 0;
3977  u_int8_t *bval;
3978  char *txt = NULL;
3979 #if defined (DEBUG_FAILOVER_MESSAGES)
3980  char tbuf [256];
3981 #endif
3982 
3983  /* Note that the failover_option structure is used differently on
3984  input than on output - on input, count is an element count, and
3985  on output it's the number of bytes total in the option, including
3986  the option code and option length. */
3987  failover_option_t option, *op;
3988 
3989 
3990  /* Bogus option code? */
3991  if (code < 1 || code > FTO_MAX || ft_options [code].type == FT_UNDEF) {
3992  return &null_failover_option;
3993  }
3994  info = &ft_options [code];
3995 
3996  va_start (va, obufmax);
3997 
3998  /* Get the number of elements and the size of the buffer we need
3999  to allocate. */
4000  if (info -> type == FT_DDNS || info -> type == FT_DDNS1) {
4001  count = info -> type == FT_DDNS ? 1 : 2;
4002  size = va_arg (va, int) + count;
4003  } else {
4004  /* Find out how many items in this list. */
4005  if (info -> num_present)
4006  count = info -> num_present;
4007  else
4008  count = va_arg (va, int);
4009 
4010  /* Figure out size. */
4011  switch (info -> type) {
4012  case FT_UINT8:
4013  case FT_BYTES:
4014  case FT_DIGEST:
4015  size = count;
4016  break;
4017 
4018  case FT_TEXT_OR_BYTES:
4019  case FT_TEXT:
4020  txt = va_arg (va, char *);
4021  size = count;
4022  break;
4023 
4024  case FT_IPADDR:
4025  ilen = va_arg (va, unsigned);
4026  size = count * ilen;
4027  break;
4028 
4029  case FT_UINT32:
4030  size = count * 4;
4031  break;
4032 
4033  case FT_UINT16:
4034  size = count * 2;
4035  break;
4036 
4037  default:
4038  /* shouldn't get here. */
4039  log_fatal ("bogus type in failover_make_option: %d",
4040  info -> type);
4041  return &null_failover_option;
4042  }
4043  }
4044 
4045  size += 4;
4046 
4047  /* Allocate a buffer for the option. */
4048  option.count = size;
4049  option.data = dmalloc (option.count, MDL);
4050  if (!option.data) {
4051  va_end (va);
4052  return &null_failover_option;
4053  }
4054 
4055  /* Put in the option code and option length. */
4056  putUShort (option.data, code);
4057  putUShort (&option.data [2], size - 4);
4058 
4059 #if defined (DEBUG_FAILOVER_MESSAGES)
4060  /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
4061  * It is unclear what the effects of truncation here are, or
4062  * how that condition should be handled. It seems that this
4063  * message may be sent over the failover command channel.
4064  * For now the safest thing is for overflow-truncation to cause
4065  * a fatal log.
4066  */
4067  if (snprintf (tbuf, sizeof tbuf, " (%s<%d>", info -> name,
4068  option.count) >= sizeof tbuf)
4069  log_fatal ("dhcp_failover_make_option: tbuf overflow");
4070  failover_print (obuf, obufix, obufmax, tbuf);
4071 #endif
4072 
4073  /* Now put in the data. */
4074  switch (info -> type) {
4075  case FT_UINT8:
4076  for (i = 0; i < count; i++) {
4077  val = va_arg (va, unsigned);
4078 #if defined (DEBUG_FAILOVER_MESSAGES)
4079  /* %Audit% Cannot exceed 24 bytes. %2004.06.17,Safe% */
4080  sprintf (tbuf, " %d", val);
4081  failover_print (obuf, obufix, obufmax, tbuf);
4082 #endif
4083  option.data [i + 4] = val;
4084  }
4085  break;
4086 
4087  case FT_IPADDR:
4088  for (i = 0; i < count; i++) {
4089  iaddr = va_arg (va, u_int8_t *);
4090  if (ilen != 4) {
4091  dfree (option.data, MDL);
4092  log_error ("IP addrlen=%d, should be 4.",
4093  ilen);
4094  va_end (va);
4095  return &null_failover_option;
4096  }
4097 
4098 #if defined (DEBUG_FAILOVER_MESSAGES)
4099  /*%Audit% Cannot exceed 17 bytes. %2004.06.17,Safe%*/
4100  sprintf (tbuf, " %u.%u.%u.%u",
4101  iaddr [0], iaddr [1], iaddr [2], iaddr [3]);
4102  failover_print (obuf, obufix, obufmax, tbuf);
4103 #endif
4104  memcpy (&option.data [4 + i * ilen], iaddr, ilen);
4105  }
4106  break;
4107 
4108  case FT_UINT32:
4109  for (i = 0; i < count; i++) {
4110  val = va_arg (va, unsigned);
4111 #if defined (DEBUG_FAILOVER_MESSAGES)
4112  /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
4113  sprintf (tbuf, " %d", val);
4114  failover_print (obuf, obufix, obufmax, tbuf);
4115 #endif
4116  putULong (&option.data [4 + i * 4], val);
4117  }
4118  break;
4119 
4120  case FT_BYTES:
4121  case FT_DIGEST:
4122  bval = va_arg (va, u_int8_t *);
4123 #if defined (DEBUG_FAILOVER_MESSAGES)
4124  for (i = 0; i < count; i++) {
4125  /* 23 bytes plus nul, safe. */
4126  sprintf (tbuf, " %d", bval [i]);
4127  failover_print (obuf, obufix, obufmax, tbuf);
4128  }
4129 #endif
4130  memcpy (&option.data [4], bval, count);
4131  break;
4132 
4133  /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
4134  terminated. Note that the caller should be careful not
4135  to provide a format and data that amount to more than 256
4136  bytes of data, since it will cause a fatal error. */
4137  case FT_TEXT_OR_BYTES:
4138  case FT_TEXT:
4139 #if defined (DEBUG_FAILOVER_MESSAGES)
4140  /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
4141  * It is unclear what the effects of truncation here are, or
4142  * how that condition should be handled. It seems that this
4143  * function is used for formatting messages in the failover
4144  * command channel. For now the safest thing is for
4145  * overflow-truncation to cause a fatal log.
4146  */
4147  if (snprintf (tbuf, sizeof tbuf, "\"%s\"", txt) >= sizeof tbuf)
4148  log_fatal ("dhcp_failover_make_option: tbuf overflow");
4149  failover_print (obuf, obufix, obufmax, tbuf);
4150 #endif
4151  memcpy (&option.data [4], txt, count);
4152  break;
4153 
4154  case FT_DDNS:
4155  case FT_DDNS1:
4156  option.data [4] = va_arg (va, unsigned);
4157  if (count == 2)
4158  option.data [5] = va_arg (va, unsigned);
4159  bval = va_arg (va, u_int8_t *);
4160  memcpy (&option.data [4 + count], bval, size - count - 4);
4161 #if defined (DEBUG_FAILOVER_MESSAGES)
4162  for (i = 4; i < size; i++) {
4163  /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
4164  sprintf (tbuf, " %d", option.data [i]);
4165  failover_print (obuf, obufix, obufmax, tbuf);
4166  }
4167 #endif
4168  break;
4169 
4170  case FT_UINT16:
4171  for (i = 0; i < count; i++) {
4172  val = va_arg (va, u_int32_t);
4173 #if defined (DEBUG_FAILOVER_MESSAGES)
4174  /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
4175  sprintf (tbuf, " %d", val);
4176  failover_print (obuf, obufix, obufmax, tbuf);
4177 #endif
4178  putUShort (&option.data [4 + i * 2], val);
4179  }
4180  break;
4181 
4182  case FT_UNDEF:
4183  default:
4184  break;
4185  }
4186 
4187 #if defined DEBUG_FAILOVER_MESSAGES
4188  failover_print (obuf, obufix, obufmax, ")");
4189 #endif
4190  va_end (va);
4191 
4192  /* Now allocate a place to store what we just set up. */
4193  op = dmalloc (sizeof (failover_option_t), MDL);
4194  if (!op) {
4195  dfree (option.data, MDL);
4196  return &null_failover_option;
4197  }
4198 
4199  *op = option;
4200  return op;
4201 }
4202 
4203 /* Send a failover message header. */
4204 
4205 isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link,
4206  omapi_object_t *connection,
4207  int msg_type, u_int32_t xid, ...)
4208 {
4209  unsigned size = 0;
4210  int bad_option = 0;
4211  int opix = 0;
4212  va_list list;
4213  failover_option_t *option;
4214  unsigned char *opbuf;
4215  isc_result_t status = ISC_R_SUCCESS;
4216  unsigned char cbuf;
4217  struct timeval tv;
4218 
4219  /* Run through the argument list once to compute the length of
4220  the option portion of the message. */
4221  va_start (list, xid);
4222  while ((option = va_arg (list, failover_option_t *))) {
4223  if (option != &skip_failover_option)
4224  size += option -> count;
4225  if (option == &null_failover_option)
4226  bad_option = 1;
4227  }
4228  va_end (list);
4229 
4230  /* Allocate an option buffer, unless we got an error. */
4231  if (!bad_option && size) {
4232  opbuf = dmalloc (size, MDL);
4233  if (!opbuf)
4234  status = ISC_R_NOMEMORY;
4235  } else
4236  opbuf = (unsigned char *)0;
4237 
4238  va_start (list, xid);
4239  while ((option = va_arg (list, failover_option_t *))) {
4240  if (option == &skip_failover_option)
4241  continue;
4242  if (!bad_option && opbuf)
4243  memcpy (&opbuf [opix],
4244  option -> data, option -> count);
4245  if (option != &null_failover_option &&
4246  option != &skip_failover_option) {
4247  opix += option -> count;
4248  dfree (option -> data, MDL);
4249  dfree (option, MDL);
4250  }
4251  }
4252  va_end(list);
4253 
4254  if (bad_option)
4255  return DHCP_R_INVALIDARG;
4256 
4257  /* Now send the message header. */
4258 
4259  /* Message length. */
4260  status = omapi_connection_put_uint16 (connection, size + 12);
4261  if (status != ISC_R_SUCCESS)
4262  goto err;
4263 
4264  /* Message type. */
4265  cbuf = msg_type;
4266  status = omapi_connection_copyin (connection, &cbuf, 1);
4267  if (status != ISC_R_SUCCESS)
4268  goto err;
4269 
4270  /* Payload offset. */
4271  cbuf = 12;
4272  status = omapi_connection_copyin (connection, &cbuf, 1);
4273  if (status != ISC_R_SUCCESS)
4274  goto err;
4275 
4276  /* Current time. */
4277  status = omapi_connection_put_uint32 (connection, (u_int32_t)cur_time);
4278  if (status != ISC_R_SUCCESS)
4279  goto err;
4280 
4281  /* Transaction ID. */
4282  status = omapi_connection_put_uint32(connection, xid);
4283  if (status != ISC_R_SUCCESS)
4284  goto err;
4285 
4286  /* Payload. */
4287  if (opbuf) {
4288  status = omapi_connection_copyin (connection, opbuf, size);
4289  if (status != ISC_R_SUCCESS)
4290  goto err;
4291  dfree (opbuf, MDL);
4292  }
4293  if (link -> state_object &&
4294  link -> state_object -> link_to_peer == link) {
4295 #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
4296  log_info ("add_timeout +%d %s",
4297  (int)(link -> state_object ->
4298  partner.max_response_delay) / 3,
4299  "dhcp_failover_send_contact");
4300 #endif
4301  tv . tv_sec = cur_time +
4302  (int)(link -> state_object ->
4303  partner.max_response_delay) / 3;
4304  tv . tv_usec = 0;
4305  add_timeout (&tv,
4306  dhcp_failover_send_contact, link -> state_object,
4307  (tvref_t)dhcp_failover_state_reference,
4308  (tvunref_t)dhcp_failover_state_dereference);
4309  }
4310  return status;
4311 
4312  err:
4313  if (opbuf)
4314  dfree (opbuf, MDL);
4315  log_info ("dhcp_failover_put_message: something went wrong.");
4316  omapi_disconnect (connection, 1);
4317  return status;
4318 }
4319 
4320 void dhcp_failover_timeout (void *vstate)
4321 {
4322  dhcp_failover_state_t *state = vstate;
4323  dhcp_failover_link_t *link;
4324 
4325 #if defined (DEBUG_FAILOVER_TIMING)
4326  log_info ("dhcp_failover_timeout");
4327 #endif
4328 
4329  if (!state || state -> type != dhcp_type_failover_state)
4330  return;
4331  link = state -> link_to_peer;
4332  if (!link ||
4333  !link -> outer ||
4334  link -> outer -> type != omapi_type_connection)
4335  return;
4336 
4337  log_error ("timeout waiting for failover peer %s", state -> name);
4338 
4339  /* If we haven't gotten a timely response, blow away the connection.
4340  This will cause the state to change automatically. */
4341  omapi_disconnect (link -> outer, 1);
4342 }
4343 
4344 void dhcp_failover_send_contact (void *vstate)
4345 {
4346  dhcp_failover_state_t *state = vstate;
4347  dhcp_failover_link_t *link;
4348  isc_result_t status;
4349 
4350 #if defined(DEBUG_FAILOVER_MESSAGES) && \
4351  defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
4352  char obuf [64];
4353  unsigned obufix = 0;
4354 
4355  failover_print(obuf, &obufix, sizeof(obuf), "(contact");
4356 #endif
4357 
4358 #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
4359  log_info ("dhcp_failover_send_contact");
4360 #endif
4361 
4362  if (!state || state -> type != dhcp_type_failover_state)
4363  return;
4364  link = state -> link_to_peer;
4365  if (!link ||
4366  !link -> outer ||
4367  link -> outer -> type != omapi_type_connection)
4368  return;
4369 
4370  status = (dhcp_failover_put_message
4371  (link, link -> outer,
4372  FTM_CONTACT, link->xid++,
4373  (failover_option_t *)0));
4374 
4375 #if defined(DEBUG_FAILOVER_MESSAGES) && \
4376  defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
4377  if (status != ISC_R_SUCCESS)
4378  failover_print(obuf, &obufix, sizeof(obuf), " (failed)");
4379  failover_print(obuf, &obufix, sizeof(obuf), ")");
4380  if (obufix) {
4381  log_debug ("%s", obuf);
4382  }
4383 #else
4384  IGNORE_UNUSED(status);
4385 #endif
4386  return;
4387 }
4388 
4389 isc_result_t dhcp_failover_send_state (dhcp_failover_state_t *state)
4390 {
4391  dhcp_failover_link_t *link;
4392  isc_result_t status;
4393 
4394 #if defined (DEBUG_FAILOVER_MESSAGES)
4395  char obuf [64];
4396  unsigned obufix = 0;
4397 
4398 # define FMA obuf, &obufix, sizeof obuf
4399  failover_print (FMA, "(state");
4400 #else
4401 # define FMA (char *)0, (unsigned *)0, 0
4402 #endif
4403 
4404  if (!state || state -> type != dhcp_type_failover_state)
4405  return DHCP_R_INVALIDARG;
4406  link = state -> link_to_peer;
4407  if (!link ||
4408  !link -> outer ||
4409  link -> outer -> type != omapi_type_connection)
4410  return DHCP_R_INVALIDARG;
4411 
4412  status = (dhcp_failover_put_message
4413  (link, link -> outer,
4414  FTM_STATE, link->xid++,
4415  dhcp_failover_make_option (FTO_SERVER_STATE, FMA,
4416  (state -> me.state == startup
4417  ? state -> saved_state
4418  : state -> me.state)),
4420  (FTO_SERVER_FLAGS, FMA,
4421  (state -> service_state == service_startup
4422  ? FTF_SERVER_STARTUP : 0)),
4423  dhcp_failover_make_option (FTO_STOS, FMA, state -> me.stos),
4424  (failover_option_t *)0));
4425 
4426 #if defined (DEBUG_FAILOVER_MESSAGES)
4427  if (status != ISC_R_SUCCESS)
4428  failover_print (FMA, " (failed)");
4429  failover_print (FMA, ")");
4430  if (obufix) {
4431  log_debug ("%s", obuf);
4432  }
4433 #else
4434  IGNORE_UNUSED(status);
4435 #endif
4436  return ISC_R_SUCCESS;
4437 }
4438 
4439 /* Send a connect message. */
4440 
4442 {
4443  dhcp_failover_link_t *link;
4444  dhcp_failover_state_t *state;
4445  isc_result_t status;
4446 #if defined (DEBUG_FAILOVER_MESSAGES)
4447  char obuf [64];
4448  unsigned obufix = 0;
4449 
4450 # define FMA obuf, &obufix, sizeof obuf
4451  failover_print (FMA, "(connect");
4452 #else
4453 # define FMA (char *)0, (unsigned *)0, 0
4454 #endif
4455 
4456  if (!l || l -> type != dhcp_type_failover_link)
4457  return DHCP_R_INVALIDARG;
4458  link = (dhcp_failover_link_t *)l;
4459  state = link -> state_object;
4460  if (!l -> outer || l -> outer -> type != omapi_type_connection)
4461  return DHCP_R_INVALIDARG;
4462 
4463  status =
4465  (link, l -> outer,
4466  FTM_CONNECT, link->xid++,
4467  dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA,
4468  strlen(state->name), state->name),
4469  dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
4470  state -> me.max_flying_updates),
4471  dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
4472  state -> me.max_response_delay),
4473  dhcp_failover_option_printf(FTO_VENDOR_CLASS, FMA,
4474  "isc-%s", PACKAGE_VERSION),
4475  dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
4476  DHCP_FAILOVER_VERSION),
4477  dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
4478  0, 0),
4479  dhcp_failover_make_option (FTO_MCLT, FMA,
4480  state -> mclt),
4481  (state -> hba
4482  ? dhcp_failover_make_option (FTO_HBA, FMA, 32, state -> hba)
4484  (failover_option_t *)0));
4485 
4486 #if defined (DEBUG_FAILOVER_MESSAGES)
4487  if (status != ISC_R_SUCCESS)
4488  failover_print (FMA, " (failed)");
4489  failover_print (FMA, ")");
4490  if (obufix) {
4491  log_debug ("%s", obuf);
4492  }
4493 #endif
4494  return status;
4495 }
4496 
4498  dhcp_failover_state_t *state,
4499  int reason, const char *errmsg)
4500 {
4501  dhcp_failover_link_t *link;
4502  isc_result_t status;
4503 #if defined (DEBUG_FAILOVER_MESSAGES)
4504  char obuf [64];
4505  unsigned obufix = 0;
4506 
4507 # define FMA obuf, &obufix, sizeof obuf
4508  failover_print (FMA, "(connectack");
4509 #else
4510 # define FMA (char *)0, (unsigned *)0, 0
4511 #endif
4512 
4513  if (!l || l -> type != dhcp_type_failover_link)
4514  return DHCP_R_INVALIDARG;
4515  link = (dhcp_failover_link_t *)l;
4516  if (!l -> outer || l -> outer -> type != omapi_type_connection)
4517  return DHCP_R_INVALIDARG;
4518 
4519  status =
4521  (link, l -> outer,
4522  FTM_CONNECTACK, link->imsg->xid,
4523  state
4524  ? dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA,
4525  strlen(state->name), state->name)
4526  : (link->imsg->options_present & FTB_RELATIONSHIP_NAME)
4527  ? dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA,
4528  link->imsg->relationship_name.count,
4529  link->imsg->relationship_name.data)
4530  : &skip_failover_option,
4531  state
4532  ? dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
4533  state -> me.max_flying_updates)
4534  : &skip_failover_option,
4535  state
4536  ? dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
4537  state -> me.max_response_delay)
4538  : &skip_failover_option,
4539  dhcp_failover_option_printf(FTO_VENDOR_CLASS, FMA,
4540  "isc-%s", PACKAGE_VERSION),
4541  dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
4542  DHCP_FAILOVER_VERSION),
4543  (link->imsg->options_present & FTB_TLS_REQUEST)
4544  ? dhcp_failover_make_option(FTO_TLS_REPLY, FMA,
4545  0, 0)
4546  : &skip_failover_option,
4547  reason
4548  ? dhcp_failover_make_option (FTO_REJECT_REASON,
4549  FMA, reason)
4550  : &skip_failover_option,
4551  (reason && errmsg)
4552  ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4553  strlen (errmsg), errmsg)
4554  : &skip_failover_option,
4555  (failover_option_t *)0));
4556 
4557 #if defined (DEBUG_FAILOVER_MESSAGES)
4558  if (status != ISC_R_SUCCESS)
4559  failover_print (FMA, " (failed)");
4560  failover_print (FMA, ")");
4561  if (obufix) {
4562  log_debug ("%s", obuf);
4563  }
4564 #endif
4565  return status;
4566 }
4567 
4569  int reason,
4570  const char *message)
4571 {
4572  dhcp_failover_link_t *link;
4573  isc_result_t status;
4574 #if defined (DEBUG_FAILOVER_MESSAGES)
4575  char obuf [64];
4576  unsigned obufix = 0;
4577 
4578 # define FMA obuf, &obufix, sizeof obuf
4579  failover_print (FMA, "(disconnect");
4580 #else
4581 # define FMA (char *)0, (unsigned *)0, 0
4582 #endif
4583 
4584  if (!l || l -> type != dhcp_type_failover_link)
4585  return DHCP_R_INVALIDARG;
4586  link = (dhcp_failover_link_t *)l;
4587  if (!l -> outer || l -> outer -> type != omapi_type_connection)
4588  return DHCP_R_INVALIDARG;
4589 
4590  if (!message && reason)
4591  message = dhcp_failover_reject_reason_print (reason);
4592 
4593  status = (dhcp_failover_put_message
4594  (link, l -> outer,
4595  FTM_DISCONNECT, link->xid++,
4596  dhcp_failover_make_option (FTO_REJECT_REASON,
4597  FMA, reason),
4598  (message
4599  ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4600  strlen (message), message)
4601  : &skip_failover_option),
4602  (failover_option_t *)0));
4603 
4604 #if defined (DEBUG_FAILOVER_MESSAGES)
4605  if (status != ISC_R_SUCCESS)
4606  failover_print (FMA, " (failed)");
4607  failover_print (FMA, ")");
4608  if (obufix) {
4609  log_debug ("%s", obuf);
4610  }
4611 #endif
4612  return status;
4613 }
4614 
4615 /* Send a Bind Update message. */
4616 
4617 isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state,
4618  struct lease *lease)
4619 {
4620  dhcp_failover_link_t *link;
4621  isc_result_t status;
4622  int flags = 0;
4623  binding_state_t transmit_state;
4624 #if defined (DEBUG_FAILOVER_MESSAGES)
4625  char obuf [64];
4626  unsigned obufix = 0;
4627 
4628 # define FMA obuf, &obufix, sizeof obuf
4629  failover_print (FMA, "(bndupd");
4630 #else
4631 # define FMA (char *)0, (unsigned *)0, 0
4632 #endif
4633 
4634  if (!state -> link_to_peer ||
4635  state -> link_to_peer -> type != dhcp_type_failover_link)
4636  return DHCP_R_INVALIDARG;
4637  link = (dhcp_failover_link_t *)state -> link_to_peer;
4638 
4639  if (!link -> outer || link -> outer -> type != omapi_type_connection)
4640  return DHCP_R_INVALIDARG;
4641 
4642  transmit_state = lease->desired_binding_state;
4643  if (lease->flags & RESERVED_LEASE) {
4644  /* If we are listing an allocable (not yet ACTIVE etc) lease
4645  * as reserved, toggle to the peer's 'free state', per the
4646  * draft. This gives the peer permission to alloc it to the
4647  * chaddr/uid-named client.
4648  */
4649  if ((state->i_am == primary) && (transmit_state == FTS_FREE))
4650  transmit_state = FTS_BACKUP;
4651  else if ((state->i_am == secondary) &&
4652  (transmit_state == FTS_BACKUP))
4653  transmit_state = FTS_FREE;
4654 
4655  flags |= FTF_IP_FLAG_RESERVE;
4656  }
4657  if (lease->flags & BOOTP_LEASE)
4658  flags |= FTF_IP_FLAG_BOOTP;
4659 
4660  /* last_xid == 0 is illegal, seek past zero if we hit it. */
4661  if (link->xid == 0)
4662  link->xid = 1;
4663 
4664  lease->last_xid = link->xid++;
4665 
4666  /*
4667  * Our very next action is to transmit a binding update relating to
4668  * this lease over the wire, and although there is a BNDACK, there is
4669  * no BNDACKACK or BNDACKACKACK...the basic issue as we send a BNDUPD,
4670  * we may not receive a BNDACK. This non-reception does not imply the
4671  * peer did not receive and process the BNDUPD. So at this point, we
4672  * must divest any state that would be dangerous to retain under the
4673  * impression the peer has been updated. Normally state changes like
4674  * this are processed in supersede_lease(), but in this case we need a
4675  * very late binding.
4676  *
4677  * In failover rules, a server is permitted to work forward in certain
4678  * directions from a given lease's state; active leases may be
4679  * extended, so forth. There is an 'optimization' in the failover
4680  * draft that permits a server to 'rewind' any work they have not
4681  * informed the peer. Since we can't know if the peer received our
4682  * update but was unable to acknowledge it, we make this change on
4683  * transmit rather than upon receiving the acknowledgement.
4684  *
4685  * XXX: Frequent lease commits are undesirable. This should hopefully
4686  * only trigger when a server is sending a lease /state change/, and
4687  * not merely an update such as with a renewal.
4688  */
4689  if (lease->rewind_binding_state != lease->binding_state) {
4690  lease->rewind_binding_state = lease->binding_state;
4691 
4692  write_lease(lease);
4693  commit_leases();
4694  }
4695 
4696  /* Send the update. */
4697  status = (dhcp_failover_put_message
4698  (link, link -> outer,
4699  FTM_BNDUPD, lease->last_xid,
4700  dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
4701  lease -> ip_addr.len,
4702  lease -> ip_addr.iabuf),
4703  dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
4704  lease -> desired_binding_state),
4705  lease -> uid_len
4706  ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
4707  lease -> uid_len,
4708  lease -> uid)
4709  : &skip_failover_option,
4710  lease -> hardware_addr.hlen
4711  ? dhcp_failover_make_option (FTO_CHADDR, FMA,
4712  lease -> hardware_addr.hlen,
4713  lease -> hardware_addr.hbuf)
4714  : &skip_failover_option,
4715  dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
4716  lease -> ends),
4717  dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
4718  lease -> tstp),
4719  dhcp_failover_make_option (FTO_STOS, FMA,
4720  lease -> starts),
4721  (lease->cltt != 0) ?
4722  dhcp_failover_make_option(FTO_CLTT, FMA, lease->cltt) :
4723  &skip_failover_option, /* No CLTT */
4724  flags ? dhcp_failover_make_option(FTO_IP_FLAGS, FMA,
4725  flags) :
4726  &skip_failover_option, /* No IP_FLAGS */
4727  &skip_failover_option, /* XXX DDNS */
4728  &skip_failover_option, /* XXX request options */
4729  &skip_failover_option, /* XXX reply options */
4730  (failover_option_t *)0));
4731 
4732 #if defined (DEBUG_FAILOVER_MESSAGES)
4733  if (status != ISC_R_SUCCESS)
4734  failover_print (FMA, " (failed)");
4735  failover_print (FMA, ")");
4736  if (obufix) {
4737  log_debug ("%s", obuf);
4738  }
4739 #endif
4740  return status;
4741 }
4742 
4743 /* Send a Bind ACK message. */
4744 
4745 isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *state,
4746  failover_message_t *msg,
4747  int reason, const char *message)
4748 {
4749  dhcp_failover_link_t *link;
4750  isc_result_t status;
4751 #if defined (DEBUG_FAILOVER_MESSAGES)
4752  char obuf [64];
4753  unsigned obufix = 0;
4754 
4755 # define FMA obuf, &obufix, sizeof obuf
4756  failover_print (FMA, "(bndack");
4757 #else
4758 # define FMA (char *)0, (unsigned *)0, 0
4759 #endif
4760 
4761  if (!state -> link_to_peer ||
4762  state -> link_to_peer -> type != dhcp_type_failover_link)
4763  return DHCP_R_INVALIDARG;
4764  link = (dhcp_failover_link_t *)state -> link_to_peer;
4765 
4766  if (!link -> outer || link -> outer -> type != omapi_type_connection)
4767  return DHCP_R_INVALIDARG;
4768 
4769  if (!message && reason)
4770  message = dhcp_failover_reject_reason_print (reason);
4771 
4772  /* Send the update. */
4773  status = (dhcp_failover_put_message
4774  (link, link -> outer,
4775  FTM_BNDACK, msg->xid,
4776  dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
4777  sizeof msg -> assigned_addr,
4778  &msg -> assigned_addr),
4779 #ifdef DO_BNDACK_SHOULD_NOT
4780  dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
4781  msg -> binding_status),
4782  (msg -> options_present & FTB_CLIENT_IDENTIFIER)
4783  ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
4784  msg -> client_identifier.count,
4785  msg -> client_identifier.data)
4786  : &skip_failover_option,
4787  (msg -> options_present & FTB_CHADDR)
4788  ? dhcp_failover_make_option (FTO_CHADDR, FMA,
4789  msg -> chaddr.count,
4790  msg -> chaddr.data)
4791  : &skip_failover_option,
4792  dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
4793  msg -> expiry),
4794  dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
4795  msg -> potential_expiry),
4796  dhcp_failover_make_option (FTO_STOS, FMA,
4797  msg -> stos),
4798  (msg->options_present & FTB_CLTT) ?
4799  dhcp_failover_make_option(FTO_CLTT, FMA, msg->cltt) :
4800  &skip_failover_option, /* No CLTT in the msg to ack. */
4801  ((msg->options_present & FTB_IP_FLAGS) && msg->ip_flags) ?
4802  dhcp_failover_make_option(FTO_IP_FLAGS, FMA,
4803  msg->ip_flags)
4804  : &skip_failover_option,
4805 #endif /* DO_BNDACK_SHOULD_NOT */
4806  reason
4807  ? dhcp_failover_make_option(FTO_REJECT_REASON, FMA, reason)
4808  : &skip_failover_option,
4809  (reason && message)
4810  ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4811  strlen (message), message)
4812  : &skip_failover_option,
4813 #ifdef DO_BNDACK_SHOULD_NOT
4814  &skip_failover_option, /* XXX DDNS */
4815  &skip_failover_option, /* XXX request options */
4816  &skip_failover_option, /* XXX reply options */
4817 #endif /* DO_BNDACK_SHOULD_NOT */
4818  (failover_option_t *)0));
4819 
4820 #if defined (DEBUG_FAILOVER_MESSAGES)
4821  if (status != ISC_R_SUCCESS)
4822  failover_print (FMA, " (failed)");
4823  failover_print (FMA, ")");
4824  if (obufix) {
4825  log_debug ("%s", obuf);
4826  }
4827 #endif
4828  return status;
4829 }
4830 
4831 isc_result_t dhcp_failover_send_poolreq (dhcp_failover_state_t *state)
4832 {
4833  dhcp_failover_link_t *link;
4834  isc_result_t status;
4835 #if defined (DEBUG_FAILOVER_MESSAGES)
4836  char obuf [64];
4837  unsigned obufix = 0;
4838 
4839 # define FMA obuf, &obufix, sizeof obuf
4840  failover_print (FMA, "(poolreq");
4841 #else
4842 # define FMA (char *)0, (unsigned *)0, 0
4843 #endif
4844 
4845  if (!state -> link_to_peer ||
4846  state -> link_to_peer -> type != dhcp_type_failover_link)
4847  return DHCP_R_INVALIDARG;
4848  link = (dhcp_failover_link_t *)state -> link_to_peer;
4849 
4850  if (!link -> outer || link -> outer -> type != omapi_type_connection)
4851  return DHCP_R_INVALIDARG;
4852 
4853  status = (dhcp_failover_put_message
4854  (link, link -> outer,
4855  FTM_POOLREQ, link->xid++,
4856  (failover_option_t *)0));
4857 
4858 #if defined (DEBUG_FAILOVER_MESSAGES)
4859  if (status != ISC_R_SUCCESS)
4860  failover_print (FMA, " (failed)");
4861  failover_print (FMA, ")");
4862  if (obufix) {
4863  log_debug ("%s", obuf);
4864  }
4865 #endif
4866  return status;
4867 }
4868 
4869 isc_result_t dhcp_failover_send_poolresp (dhcp_failover_state_t *state,
4870  int leases)
4871 {
4872  dhcp_failover_link_t *link;
4873  isc_result_t status;
4874 #if defined (DEBUG_FAILOVER_MESSAGES)
4875  char obuf [64];
4876  unsigned obufix = 0;
4877 
4878 # define FMA obuf, &obufix, sizeof obuf
4879  failover_print (FMA, "(poolresp");
4880 #else
4881 # define FMA (char *)0, (unsigned *)0, 0
4882 #endif
4883 
4884  if (!state -> link_to_peer ||
4885  state -> link_to_peer -> type != dhcp_type_failover_link)
4886  return DHCP_R_INVALIDARG;
4887  link = (dhcp_failover_link_t *)state -> link_to_peer;
4888 
4889  if (!link -> outer || link -> outer -> type != omapi_type_connection)
4890  return DHCP_R_INVALIDARG;
4891 
4892  status = (dhcp_failover_put_message
4893  (link, link -> outer,
4894  FTM_POOLRESP, link->imsg->xid,
4895  dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED, FMA,
4896  leases),
4897  (failover_option_t *)0));
4898 
4899 #if defined (DEBUG_FAILOVER_MESSAGES)
4900  if (status != ISC_R_SUCCESS)
4901  failover_print (FMA, " (failed)");
4902  failover_print (FMA, ")");
4903  if (obufix) {
4904  log_debug ("%s", obuf);
4905  }
4906 #endif
4907  return status;
4908 }
4909 
4910 isc_result_t dhcp_failover_send_update_request (dhcp_failover_state_t *state)
4911 {
4912  dhcp_failover_link_t *link;
4913  isc_result_t status;
4914 #if defined (DEBUG_FAILOVER_MESSAGES)
4915  char obuf [64];
4916  unsigned obufix = 0;
4917 
4918 # define FMA obuf, &obufix, sizeof obuf
4919  failover_print (FMA, "(updreq");
4920 #else
4921 # define FMA (char *)0, (unsigned *)0, 0
4922 #endif
4923 
4924  if (!state->link_to_peer ||
4925  state->link_to_peer->type != dhcp_type_failover_link)
4926  return (DHCP_R_INVALIDARG);
4927  link = (dhcp_failover_link_t *)state->link_to_peer;
4928 
4929  if (!link->outer || link->outer->type != omapi_type_connection)
4930  return (DHCP_R_INVALIDARG);
4931 
4932  /* We allow an update to be restarted in case we requested an update
4933  * and were interrupted by something. If we had an ALL going we need
4934  * to restart that. Otherwise we simply continue with the request */
4935  if (state->curUPD == FTM_UPDREQALL) {
4936  return (dhcp_failover_send_update_request_all(state));
4937  }
4938 
4939  status = (dhcp_failover_put_message(link, link->outer, FTM_UPDREQ,
4940  link->xid++, NULL));
4941 
4942  state->curUPD = FTM_UPDREQ;
4943 
4944 #if defined (DEBUG_FAILOVER_MESSAGES)
4945  if (status != ISC_R_SUCCESS)
4946  failover_print(FMA, " (failed)");
4947  failover_print(FMA, ")");
4948  if (obufix) {
4949  log_debug("%s", obuf);
4950  }
4951 #endif
4952 
4953  if (status == ISC_R_SUCCESS) {
4954  log_info("Sent update request message to %s", state->name);
4955  } else {
4956  log_error("Failed to send update request all message to %s: %s",
4957  state->name, isc_result_totext(status));
4958  }
4959  return (status);
4960 }
4961 
4962 isc_result_t dhcp_failover_send_update_request_all (dhcp_failover_state_t
4963  *state)
4964 {
4965  dhcp_failover_link_t *link;
4966  isc_result_t status;
4967 #if defined (DEBUG_FAILOVER_MESSAGES)
4968  char obuf [64];
4969  unsigned obufix = 0;
4970 
4971 # define FMA obuf, &obufix, sizeof obuf
4972  failover_print (FMA, "(updreqall");
4973 #else
4974 # define FMA (char *)0, (unsigned *)0, 0
4975 #endif
4976 
4977  if (!state->link_to_peer ||
4978  state->link_to_peer->type != dhcp_type_failover_link)
4979  return (DHCP_R_INVALIDARG);
4980  link = (dhcp_failover_link_t *)state->link_to_peer;
4981 
4982  if (!link->outer || link->outer->type != omapi_type_connection)
4983  return (DHCP_R_INVALIDARG);
4984 
4985  /* We allow an update to be restarted in case we requested an update
4986  * and were interrupted by something.
4987  */
4988 
4989  status = (dhcp_failover_put_message(link, link->outer, FTM_UPDREQALL,
4990  link->xid++, NULL));
4991 
4992  state->curUPD = FTM_UPDREQALL;
4993 
4994 #if defined (DEBUG_FAILOVER_MESSAGES)
4995  if (status != ISC_R_SUCCESS)
4996  failover_print(FMA, " (failed)");
4997  failover_print(FMA, ")");
4998  if (obufix) {
4999  log_debug("%s", obuf);
5000  }
5001 #endif
5002 
5003  if (status == ISC_R_SUCCESS) {
5004  log_info("Sent update request all message to %s", state->name);
5005  } else {
5006  log_error("Failed to send update request all message to %s: %s",
5007  state->name, isc_result_totext(status));
5008  }
5009  return (status);
5010 }
5011 
5012 isc_result_t dhcp_failover_send_update_done (dhcp_failover_state_t *state)
5013 {
5014  dhcp_failover_link_t *link;
5015  isc_result_t status;
5016 #if defined (DEBUG_FAILOVER_MESSAGES)
5017  char obuf [64];
5018  unsigned obufix = 0;
5019 
5020 # define FMA obuf, &obufix, sizeof obuf
5021  failover_print (FMA, "(upddone");
5022 #else
5023 # define FMA (char *)0, (unsigned *)0, 0
5024 #endif
5025 
5026  if (!state -> link_to_peer ||
5027  state -> link_to_peer -> type != dhcp_type_failover_link)
5028  return DHCP_R_INVALIDARG;
5029  link = (dhcp_failover_link_t *)state -> link_to_peer;
5030 
5031  if (!link -> outer || link -> outer -> type != omapi_type_connection)
5032  return DHCP_R_INVALIDARG;
5033 
5034  status = (dhcp_failover_put_message
5035  (link, link -> outer,
5036  FTM_UPDDONE, state->updxid,
5037  (failover_option_t *)0));
5038 
5039 #if defined (DEBUG_FAILOVER_MESSAGES)
5040  if (status != ISC_R_SUCCESS)
5041  failover_print (FMA, " (failed)");
5042  failover_print (FMA, ")");
5043  if (obufix) {
5044  log_debug ("%s", obuf);
5045  }
5046 #endif
5047 
5048  log_info ("Sent update done message to %s", state -> name);
5049 
5050  state->updxid--; /* Paranoia, just so it mismatches. */
5051 
5052  /* There may be uncommitted leases at this point (since
5053  dhcp_failover_process_bind_ack() doesn't commit leases);
5054  commit the lease file. */
5055  commit_leases();
5056 
5057  return status;
5058 }
5059 
5060 /*
5061  * failover_lease_is_better() compares the binding update in 'msg' with
5062  * the current lease in 'lease'. If the determination is that the binding
5063  * update shouldn't be allowed to update/crush more critical binding info
5064  * on the lease, the lease is preferred. A value of true is returned if the
5065  * local lease is preferred, or false if the remote binding update is
5066  * preferred.
5067  *
5068  * For now this function is hopefully simplistic and trivial. It may be that
5069  * a more detailed system of preferences is required, so this is something we
5070  * should monitor as we gain experience with these dueling events.
5071  */
5072 static isc_boolean_t
5073 failover_lease_is_better(dhcp_failover_state_t *state, struct lease *lease,
5074  failover_message_t *msg)
5075 {
5076  binding_state_t local_state;
5077  TIME msg_cltt;
5078 
5079  if (lease->binding_state != lease->desired_binding_state)
5080  local_state = lease->desired_binding_state;
5081  else
5082  local_state = lease->binding_state;
5083 
5084  if ((msg->options_present & FTB_CLTT) != 0)
5085  msg_cltt = msg->cltt;
5086  else
5087  msg_cltt = 0;
5088 
5089  switch(local_state) {
5090  case FTS_ACTIVE:
5091  if (msg->binding_status == FTS_ACTIVE) {
5092  if (msg_cltt < lease->cltt)
5093  return ISC_TRUE;
5094  else if (msg_cltt > lease->cltt)
5095  return ISC_FALSE;
5096  else if (state->i_am == primary)
5097  return ISC_TRUE;
5098  else
5099  return ISC_FALSE;
5100  } else if (msg->binding_status == FTS_EXPIRED) {
5101  return ISC_FALSE;
5102  }
5103  /* FALL THROUGH */
5104 
5105  case FTS_FREE:
5106  case FTS_BACKUP:
5107  case FTS_EXPIRED:
5108  case FTS_RELEASED:
5109  case FTS_ABANDONED:
5110  case FTS_RESET:
5111  if (msg->binding_status == FTS_ACTIVE)
5112  return ISC_FALSE;
5113  else if (state->i_am == primary)
5114  return ISC_TRUE;
5115  else
5116  return ISC_FALSE;
5117  /* FALL THROUGH to impossible condition */
5118 
5119  default:
5120  log_fatal("Impossible condition at %s:%d.", MDL);
5121  }
5122 
5123  log_fatal("Impossible condition at %s:%d.", MDL);
5124  /* Silence compiler warning. */
5125  return ISC_FALSE;
5126 }
5127 
5128 isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *state,
5129  failover_message_t *msg)
5130 {
5131  struct lease *lt = NULL, *lease = NULL;
5132  struct iaddr ia;
5133  int reason = FTR_MISC_REJECT;
5134  const char *message;
5135  int new_binding_state;
5136  int send_to_backup = 0;
5137  int required_options;
5138  isc_boolean_t chaddr_changed = ISC_FALSE;
5139  isc_boolean_t ident_changed = ISC_FALSE;
5140 
5141  /* Validate the binding update. */
5142  required_options = FTB_ASSIGNED_IP_ADDRESS | FTB_BINDING_STATUS;
5143  if ((msg->options_present & required_options) != required_options) {
5144  message = "binding update lacks required options";
5145  reason = FTR_MISSING_BINDINFO;
5146  goto bad;
5147  }
5148 
5149  ia.len = sizeof msg -> assigned_addr;
5150  memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
5151 
5152  if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
5153  message = "unknown IP address";
5154  reason = FTR_ILLEGAL_IP_ADDR;
5155  goto bad;
5156  }
5157 
5158  /*
5159  * If this lease is covered by a different failover peering
5160  * relationship, assert an error.
5161  */
5162  if ((lease->pool == NULL) || (lease->pool->failover_peer == NULL) ||
5163  (lease->pool->failover_peer != state)) {
5164  message = "IP address is covered by a different failover "
5165  "relationship state";
5166  reason = FTR_ILLEGAL_IP_ADDR;
5167  goto bad;
5168  }
5169 
5170  /*
5171  * Dueling updates: This happens when both servers send a BNDUPD
5172  * at the same time. We want the best update to win, which means
5173  * we reject if we think ours is better, or cancel if we think the
5174  * peer's is better. We only assert a problem if the lease is on
5175  * the ACK queue, not on the UPDATE queue. This means that after
5176  * accepting this server's BNDUPD, we will send our own BNDUPD
5177  * /after/ sending the BNDACK (this order was recently enforced in
5178  * queue processing).
5179  */
5180  if ((lease->flags & ON_ACK_QUEUE) != 0) {
5181  if (failover_lease_is_better(state, lease, msg)) {
5182  message = "incoming update is less critical than "
5183  "outgoing update";
5184  reason = FTR_LESS_CRIT_BIND_INFO;
5185  goto bad;
5186  } else {
5187  /* This makes it so we ignore any spurious ACKs. */
5188  dhcp_failover_ack_queue_remove(state, lease);
5189  }
5190  }
5191 
5192  /* Install the new info. Start by taking a copy to markup. */
5193  if (!lease_copy (&lt, lease, MDL)) {
5194  message = "no memory";
5195  goto bad;
5196  }
5197 
5198  if (msg -> options_present & FTB_CHADDR) {
5199  if (msg->binding_status == FTS_ABANDONED) {
5200  message = "BNDUPD to ABANDONED with a CHADDR";
5201  goto bad;
5202  }
5203  if (msg -> chaddr.count > sizeof lt -> hardware_addr.hbuf) {
5204  message = "chaddr too long";
5205  goto bad;
5206  }
5207 
5208  if ((lt->hardware_addr.hlen != msg->chaddr.count) ||
5209  (memcmp(lt->hardware_addr.hbuf, msg->chaddr.data,
5210  msg->chaddr.count) != 0))
5211  chaddr_changed = ISC_TRUE;
5212 
5213  lt -> hardware_addr.hlen = msg -> chaddr.count;
5214  memcpy (lt -> hardware_addr.hbuf, msg -> chaddr.data,
5215  msg -> chaddr.count);
5216  } else if (msg->binding_status == FTS_ACTIVE ||
5217  msg->binding_status == FTS_EXPIRED ||
5218  msg->binding_status == FTS_RELEASED) {
5219  message = "BNDUPD without CHADDR";
5220  reason = FTR_MISSING_BINDINFO;
5221  goto bad;
5222  } else if (msg->binding_status == FTS_ABANDONED) {
5223  chaddr_changed = ISC_TRUE;
5224  lt->hardware_addr.hlen = 0;
5225  if (lt->scope)
5227  }
5228 
5229  /* There is no explicit message content to indicate that the client
5230  * supplied no client-identifier. So if we don't hear of a value,
5231  * we discard the last one.
5232  */
5233  if (msg->options_present & FTB_CLIENT_IDENTIFIER) {
5234  if (msg->binding_status == FTS_ABANDONED) {
5235  message = "BNDUPD to ABANDONED with client-id";
5236  goto bad;
5237  }
5238 
5239  if ((lt->uid_len != msg->client_identifier.count) ||
5240  (lt->uid == NULL) || /* Sanity; should never happen. */
5241  (memcmp(lt->uid, msg->client_identifier.data,
5242  lt->uid_len) != 0))
5243  ident_changed = ISC_TRUE;
5244 
5245  lt->uid_len = msg->client_identifier.count;
5246 
5247  /* Allocate the lt->uid buffer if we haven't already, or
5248  * re-allocate the lt-uid buffer if we have one that is not
5249  * large enough. Otherwise, just use the extant buffer.
5250  */
5251  if (!lt->uid || lt->uid == lt->uid_buf ||
5252  lt->uid_len > lt->uid_max) {
5253  if (lt->uid && lt->uid != lt->uid_buf)
5254  dfree(lt->uid, MDL);
5255 
5256  if (lt->uid_len > sizeof(lt->uid_buf)) {
5257  lt->uid_max = lt->uid_len;
5258  lt->uid = dmalloc(lt->uid_len, MDL);
5259  if (!lt->uid) {
5260  message = "no memory";
5261  goto bad;
5262  }
5263  } else {
5264  lt->uid_max = sizeof(lt->uid_buf);
5265  lt->uid = lt->uid_buf;
5266  }
5267  }
5268  memcpy (lt -> uid,
5269  msg -> client_identifier.data, lt -> uid_len);
5270  } else if (lt->uid && msg->binding_status != FTS_RESET &&
5271  msg->binding_status != FTS_FREE &&
5272  msg->binding_status != FTS_BACKUP) {
5273  ident_changed = ISC_TRUE;
5274  if (lt->uid != lt->uid_buf)
5275  dfree (lt->uid, MDL);
5276  lt->uid = NULL;
5277  lt->uid_max = lt->uid_len = 0;
5278  }
5279 
5280  /*
5281  * A server's configuration can assign a 'binding scope';
5282  *
5283  * set var = "value";
5284  *
5285  * The problem with these binding scopes is that they are refreshed
5286  * when the server processes a client's DHCP packet. A local binding
5287  * scope is trash, then, when the lease has been assigned by the
5288  * partner server. There is no real way to detect this, a peer may
5289  * be updating us (as through potential conflict) with a binding we
5290  * sent them, but we can trivially detect the /problematic/ case;
5291  *
5292  * lease is free.
5293  * primary allocates lease to client A, assigns ddns name A.
5294  * primary fails.
5295  * secondary enters partner down.
5296  * lease expires, and is set free.
5297  * lease is allocated to client B and given ddns name B.
5298  * primary recovers.
5299  *
5300  * The binding update in this case will be active->active, but the
5301  * client identification on the lease will have changed. The ddns
5302  * update on client A will have leaked if we just remove the binding
5303  * scope blindly.
5304  */
5305  if (msg->binding_status == FTS_ACTIVE &&
5306  (chaddr_changed || ident_changed)) {
5307 #if defined (NSUPDATE)
5308  (void) ddns_removals(lease, NULL, NULL, ISC_FALSE);
5309 #endif /* NSUPDATE */
5310 
5311  if (lease->scope != NULL)
5312  binding_scope_dereference(&lease->scope, MDL);
5313  }
5314 
5315  /* XXX Times may need to be adjusted based on clock skew! */
5316  if (msg -> options_present & FTB_STOS) {
5317  lt -> starts = msg -> stos;
5318  }
5319  if (msg -> options_present & FTB_LEASE_EXPIRY) {
5320  lt -> ends = msg -> expiry;
5321  }
5322  if (msg->options_present & FTB_POTENTIAL_EXPIRY) {
5323  lt->atsfp = lt->tsfp = msg->potential_expiry;
5324  }
5325  if (msg->options_present & FTB_IP_FLAGS) {
5326  if (msg->ip_flags & FTF_IP_FLAG_RESERVE) {
5327  if ((((state->i_am == primary) &&
5328  (lease->binding_state == FTS_FREE)) ||
5329  ((state->i_am == secondary) &&
5330  (lease->binding_state == FTS_BACKUP))) &&
5331  !(lease->flags & RESERVED_LEASE)) {
5332  message = "Address is not reserved.";
5333  reason = FTR_IP_NOT_RESERVED;
5334  goto bad;
5335  }
5336 
5337  lt->flags |= RESERVED_LEASE;
5338  } else
5339  lt->flags &= ~RESERVED_LEASE;
5340 
5341  if (msg->ip_flags & FTF_IP_FLAG_BOOTP) {
5342  if ((((state->i_am == primary) &&
5343  (lease->binding_state == FTS_FREE)) ||
5344  ((state->i_am == secondary) &&
5345  (lease->binding_state == FTS_BACKUP))) &&
5346  !(lease->flags & BOOTP_LEASE)) {
5347  message = "Address is not allocated to BOOTP.";
5348  goto bad;
5349  }
5350  lt->flags |= BOOTP_LEASE;
5351  } else
5352  lt->flags &= ~BOOTP_LEASE;
5353 
5354  if (msg->ip_flags & ~(FTF_IP_FLAG_RESERVE | FTF_IP_FLAG_BOOTP))
5355  log_info("Unknown IP-flags set in BNDUPD (0x%x).",
5356  msg->ip_flags);
5357  } else /* Flags may only not appear if the values are zero. */
5358  lt->flags &= ~(RESERVED_LEASE | BOOTP_LEASE);
5359 
5360 #if defined (DEBUG_LEASE_STATE_TRANSITIONS)
5361  log_info ("processing state transition for %s: %s to %s",
5362  piaddr (lease -> ip_addr),
5363  binding_state_print (lease -> binding_state),
5364  binding_state_print (msg -> binding_status));
5365 #endif
5366 
5367  /* If we're in normal state, make sure the state transition
5368  we got is valid. */
5369  if (state -> me.state == normal) {
5370  new_binding_state =
5372  (lease, state, msg -> binding_status,
5373  msg -> potential_expiry));
5374  /* XXX if the transition the peer asked for isn't
5375  XXX allowed, maybe we should make the transition
5376  XXX into potential-conflict at this point. */
5377  } else {
5378  new_binding_state =
5380  (lease, state, msg -> binding_status,
5381  msg -> potential_expiry));
5382  }
5383  if (new_binding_state != msg -> binding_status) {
5384  char outbuf [100];
5385 
5386  if (snprintf (outbuf, sizeof outbuf,
5387  "%s: invalid state transition: %s to %s",
5388  piaddr (lease -> ip_addr),
5389  binding_state_print (lease -> binding_state),
5390  binding_state_print (msg -> binding_status))
5391  >= sizeof outbuf)
5392  log_fatal ("%s: impossible outbuf overflow",
5393  "dhcp_failover_process_bind_update");
5394 
5395  dhcp_failover_send_bind_ack (state, msg,
5396  FTR_FATAL_CONFLICT,
5397  outbuf);
5398  goto out;
5399  }
5400  if (new_binding_state == FTS_EXPIRED ||
5401  new_binding_state == FTS_RELEASED ||
5402  new_binding_state == FTS_RESET) {
5403  lt -> next_binding_state = FTS_FREE;
5404 
5405  /* Mac address affinity. Assign the lease to
5406  * BACKUP state if we are the primary and the
5407  * peer is more likely to reallocate this lease
5408  * to a returning client.
5409  */
5410  if ((state->i_am == primary) &&
5411  !(lt->flags & (RESERVED_LEASE | BOOTP_LEASE)))
5412  send_to_backup = peer_wants_lease(lt);
5413  } else {
5414  lt -> next_binding_state = new_binding_state;
5415  }
5416  msg -> binding_status = lt -> next_binding_state;
5417 
5418  /*
5419  * If we accept a peer's binding update, then we can't rewind a
5420  * lease behind the peer's state.
5421  */
5423 
5424  /* Try to install the new information. */
5425  if (!supersede_lease (lease, lt, 0, 0, 0, 0) ||
5426  !write_lease (lease)) {
5427  message = "database update failed";
5428  bad:
5429  dhcp_failover_send_bind_ack (state, msg, reason, message);
5430  goto out;
5431  } else {
5432  dhcp_failover_queue_ack (state, msg);
5433  }
5434 
5435  /* If it is probably wise, assign lease to backup state if the peer
5436  * is not already hoarding leases.
5437  */
5438  if (send_to_backup && secondary_not_hoarding(state, lease->pool)) {
5439  lease->next_binding_state = FTS_BACKUP;
5440  lease->tstp = cur_time;
5441  lease->starts = cur_time;
5442 
5443  if (!supersede_lease(lease, NULL, 0, 1, 0, 0) ||
5444  !write_lease(lease))
5445  log_error("can't commit lease %s for mac addr "
5446  "affinity", piaddr(lease->ip_addr));
5447 
5449  }
5450 
5451  out:
5452  if (lt)
5453  lease_dereference (&lt, MDL);
5454  if (lease)
5455  lease_dereference (&lease, MDL);
5456 
5457  return ISC_R_SUCCESS;
5458 }
5459 
5460 /* This was hairy enough I didn't want to do it all in an if statement.
5461  *
5462  * Returns: Truth is the secondary is allowed to get more leases based upon
5463  * MAC address affinity. False otherwise.
5464  */
5465 static inline int
5466 secondary_not_hoarding(dhcp_failover_state_t *state, struct pool *p) {
5467  int total;
5468  int hold;
5469  int lts;
5470 
5471  total = p->free_leases + p->backup_leases;
5472 
5473  /* How many leases is one side or the other allowed to "hold"? */
5474  hold = ((total * state->max_lease_ownership) + 50) / 100;
5475 
5476  /* If we were to send leases (or if the secondary were to send us
5477  * leases in the negative direction), how many would that be?
5478  */
5479  lts = (p->free_leases - p->backup_leases) / 2;
5480 
5481  /* The peer is not hoarding leases if we would send them more leases
5482  * (or they would take fewer leases) than the maximum they are allowed
5483  * to hold (the negative hold).
5484  */
5485  return(lts > -hold);
5486 }
5487 
5488 isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *state,
5489  failover_message_t *msg)
5490 {
5491  struct lease *lt = (struct lease *)0;
5492  struct lease *lease = (struct lease *)0;
5493  struct iaddr ia;
5494  const char *message = "no memory";
5495  u_int32_t pot_expire;
5496  int send_to_backup = ISC_FALSE;
5497  struct timeval tv;
5498 
5499  ia.len = sizeof msg -> assigned_addr;
5500  memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
5501 
5502  if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
5503  message = "no such lease";
5504  goto bad;
5505  }
5506 
5507  /* XXX check for conflicts. */
5508  if (msg -> options_present & FTB_REJECT_REASON) {
5509  log_error ("bind update on %s from %s rejected: %.*s",
5510  piaddr (ia), state -> name,
5511  (int)((msg -> options_present & FTB_MESSAGE)
5512  ? msg -> message.count
5514  (msg -> reject_reason))),
5515  (msg -> options_present & FTB_MESSAGE)
5516  ? (const char *)(msg -> message.data)
5518  (msg -> reject_reason)));
5519  goto unqueue;
5520  }
5521 
5522  /* Silently discard acks for leases we did not update (or multiple
5523  * acks).
5524  */
5525  if (!lease->last_xid)
5526  goto unqueue;
5527 
5528  if (lease->last_xid != msg->xid) {
5529  message = "xid mismatch";
5530  goto bad;
5531  }
5532 
5533  /* XXX Times may need to be adjusted based on clock skew! */
5534  if (msg->options_present & FTO_POTENTIAL_EXPIRY)
5535  pot_expire = msg->potential_expiry;
5536  else
5537  pot_expire = lease->tstp;
5538 
5539  /* If the lease was desired to enter a binding state, we set
5540  * such a value upon transmitting a bndupd. We do not clear it
5541  * if we receive a bndupd in the meantime (or change the state
5542  * of the lease again ourselves), but we do set binding_state
5543  * if we get a bndupd.
5544  *
5545  * So desired_binding_state tells us what we sent a bndupd for,
5546  * and binding_state tells us what we have since determined in
5547  * the meantime.
5548  */
5549  if (lease->desired_binding_state == FTS_EXPIRED ||
5550  lease->desired_binding_state == FTS_RESET ||
5552  {
5553  /* It is not a problem to do this directly as we call
5554  * supersede_lease immediately after: the lease is requeued
5555  * even if its sort order (tsfp) has changed.
5556  */
5557  lease->atsfp = lease->tsfp = pot_expire;
5558  if ((state->i_am == secondary) &&
5559  (lease->flags & RESERVED_LEASE))
5560  lease->next_binding_state = FTS_BACKUP;
5561  else
5562  lease->next_binding_state = FTS_FREE;
5563 
5564  /* Clear this condition for the next go-round. */
5565  lease->desired_binding_state = lease->next_binding_state;
5566 
5567  /* The peer will have made this state change, so set rewind. */
5568  lease->rewind_binding_state = lease->next_binding_state;
5569 
5570  supersede_lease(lease, NULL, 0, 0, 0, 0);
5571  write_lease(lease);
5572 
5573  /* Lease has returned to FREE state from the
5574  * transitional states. If the lease 'belongs'
5575  * to a client that would be served by the
5576  * peer, process a binding update now to send
5577  * the lease to backup state. But not if we
5578  * think we already have.
5579  */
5580  if (state->i_am == primary &&
5581  !(lease->flags & (RESERVED_LEASE | BOOTP_LEASE)) &&
5582  peer_wants_lease(lease))
5583  send_to_backup = ISC_TRUE;
5584 
5585  if (!send_to_backup && state->me.state == normal)
5586  commit_leases();
5587  } else {
5588  /* XXX It could be a problem to do this directly if the lease
5589  * XXX is sorted by tsfp.
5590  */
5591  lease->atsfp = lease->tsfp = pot_expire;
5592  if (lease->desired_binding_state != lease->binding_state) {
5593  lease->next_binding_state =
5594  lease->desired_binding_state;
5595  supersede_lease(lease, NULL, 0, 0, 0, 0);
5596  }
5597  write_lease(lease);
5598  /* Commit the lease only after a two-second timeout,
5599  so that if we get a bunch of acks in quick
5600  succession (e.g., when stealing leases from the
5601  secondary), we do not do an immediate commit for
5602  each one. */
5603  tv.tv_sec = cur_time + 2;
5604  tv.tv_usec = 0;
5605  add_timeout(&tv, commit_leases_timeout, (void *)0, 0, 0);
5606  }
5607 
5608  unqueue:
5609  dhcp_failover_ack_queue_remove (state, lease);
5610 
5611  /* If we are supposed to send an update done after we send
5612  this lease, go ahead and send it. */
5613  if (state -> send_update_done == lease) {
5614  lease_dereference (&state -> send_update_done, MDL);
5616  }
5617 
5618  /* Now that the lease is off the ack queue, consider putting it
5619  * back on the update queue for mac address affinity.
5620  */
5621  if (send_to_backup && secondary_not_hoarding(state, lease->pool)) {
5622  lease->next_binding_state = FTS_BACKUP;
5623  lease->tstp = lease->starts = cur_time;
5624 
5625  if (!supersede_lease(lease, NULL, 0, 1, 0, 0) ||
5626  !write_lease(lease))
5627  log_error("can't commit lease %s for "
5628  "client affinity", piaddr(lease->ip_addr));
5629 
5630  if (state->me.state == normal)
5631  commit_leases();
5632  }
5633 
5634  /* If there are updates pending, we've created space to send at
5635  least one. */
5637 
5638  out:
5639  lease_dereference (&lease, MDL);
5640  if (lt)
5641  lease_dereference (&lt, MDL);
5642 
5643  return ISC_R_SUCCESS;
5644 
5645  bad:
5646  log_info ("bind update on %s got ack from %s: %s.",
5647  piaddr (ia), state -> name, message);
5648  goto out;
5649 }
5650 
5651 isc_result_t dhcp_failover_generate_update_queue (dhcp_failover_state_t *state,
5652  int everythingp)
5653 {
5654  struct shared_network *s;
5655  struct pool *p;
5656  struct lease *l;
5657  int i;
5658 #define FREE_LEASES 0
5659 #define ACTIVE_LEASES 1
5660 #define EXPIRED_LEASES 2
5661 #define ABANDONED_LEASES 3
5662 #define BACKUP_LEASES 4
5663 #define RESERVED_LEASES 5
5664  struct lease **lptr[RESERVED_LEASES+1];
5665 
5666  /* Loop through each pool in each shared network and call the
5667  expiry routine on the pool. */
5668  for (s = shared_networks; s; s = s -> next) {
5669  for (p = s -> pools; p; p = p -> next) {
5670  if (p->failover_peer != state)
5671  continue;
5672 
5673  lptr[FREE_LEASES] = &p->free;
5674  lptr[ACTIVE_LEASES] = &p->active;
5675  lptr[EXPIRED_LEASES] = &p->expired;
5676  lptr[ABANDONED_LEASES] = &p->abandoned;
5677  lptr[BACKUP_LEASES] = &p->backup;
5678  lptr[RESERVED_LEASES] = &p->reserved;
5679 
5680  for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
5681  for (l = *(lptr [i]); l; l = l -> next) {
5682  if ((l->flags & ON_QUEUE) == 0 &&
5683  (everythingp ||
5684  (l->tstp > l->atsfp) ||
5685  (i == EXPIRED_LEASES))) {
5688  }
5689  }
5690  }
5691  }
5692  }
5693  return ISC_R_SUCCESS;
5694 }
5695 
5696 isc_result_t
5697 dhcp_failover_process_update_request (dhcp_failover_state_t *state,
5698  failover_message_t *msg)
5699 {
5700  if (state->send_update_done) {
5701  log_info("Received update request while old update still "
5702  "flying! Silently discarding old request.");
5703  lease_dereference(&state->send_update_done, MDL);
5704  }
5705 
5706  /* Generate a fresh update queue. */
5708 
5709  state->updxid = msg->xid;
5710 
5711  /* If there's anything on the update queue (there shouldn't be
5712  anything on the ack queue), trigger an update done message
5713  when we get an ack for that lease. */
5714  if (state -> update_queue_tail) {
5715  lease_reference (&state -> send_update_done,
5716  state -> update_queue_tail, MDL);
5718  log_info ("Update request from %s: sending update",
5719  state -> name);
5720  } else {
5721  /* Otherwise, there are no updates to send, so we can
5722  just send an UPDDONE message immediately. */
5724  log_info ("Update request from %s: nothing pending",
5725  state -> name);
5726  }
5727 
5728  return ISC_R_SUCCESS;
5729 }
5730 
5731 isc_result_t
5732 dhcp_failover_process_update_request_all (dhcp_failover_state_t *state,
5733  failover_message_t *msg)
5734 {
5735  if (state->send_update_done) {
5736  log_info("Received update request while old update still "
5737  "flying! Silently discarding old request.");
5738  lease_dereference(&state->send_update_done, MDL);
5739  }
5740 
5741  /* Generate a fresh update queue that includes every lease. */
5743 
5744  state->updxid = msg->xid;
5745 
5746  if (state -> update_queue_tail) {
5747  lease_reference (&state -> send_update_done,
5748  state -> update_queue_tail, MDL);
5750  log_info ("Update request all from %s: sending update",
5751  state -> name);
5752  } else {
5753  /* This should really never happen, but it could happen
5754  on a server that currently has no leases configured. */
5756  log_info ("Update request all from %s: nothing pending",
5757  state -> name);
5758  }
5759 
5760  return ISC_R_SUCCESS;
5761 }
5762 
5763 isc_result_t
5764 dhcp_failover_process_update_done (dhcp_failover_state_t *state,
5765  failover_message_t *msg)
5766 {
5767  struct timeval tv;
5768 
5769  log_info ("failover peer %s: peer update completed.",
5770  state -> name);
5771 
5772  state -> curUPD = 0;
5773 
5774  switch (state -> me.state) {
5775  case unknown_state:
5776  case partner_down:
5777  case normal:
5780  case shut_down:
5781  case paused:
5782  case recover_done:
5783  case startup:
5784  case recover_wait:
5785  break; /* shouldn't happen. */
5786 
5787  /* We got the UPDDONE, so we can go into normal state! */
5788  case potential_conflict:
5789  if (state->partner.state == conflict_done) {
5790  if (state->i_am == secondary) {
5792  } else {
5793  log_error("Secondary is in conflict_done "
5794  "state after conflict resolution, "
5795  "this is illegal.");
5797  }
5798  } else {
5799  if (state->i_am == primary)
5801  else
5802  log_error("Spurious update-done message.");
5803  }
5804 
5805  break;
5806 
5807  case conflict_done:
5808  log_error("Spurious update-done message.");
5809  break;
5810 
5811  case recover:
5812  /* Wait for MCLT to expire before moving to recover_done,
5813  except that if both peers come up in recover, there is
5814  no point in waiting for MCLT to expire - this probably
5815  indicates the initial startup of a newly-configured
5816  failover pair. */
5817  if (state -> me.stos + state -> mclt > cur_time &&
5818  state -> partner.state != recover &&
5819  state -> partner.state != recover_done) {
5821 #if defined (DEBUG_FAILOVER_TIMING)
5822  log_info ("add_timeout +%d %s",
5823  (int)(cur_time -
5824  state -> me.stos + state -> mclt),
5825  "dhcp_failover_recover_done");
5826 #endif
5827  tv . tv_sec = (int)(state -> me.stos + state -> mclt);
5828  tv . tv_usec = 0;
5829  add_timeout (&tv,
5831  state,
5832  (tvref_t)omapi_object_reference,
5833  (tvunref_t)
5835  } else
5837  }
5838 
5839  return ISC_R_SUCCESS;
5840 }
5841 
5842 void dhcp_failover_recover_done (void *sp)
5843 {
5844  dhcp_failover_state_t *state = sp;
5845 
5846 #if defined (DEBUG_FAILOVER_TIMING)
5847  log_info ("dhcp_failover_recover_done");
5848 #endif
5849 
5851 }
5852 
5853 #if defined (DEBUG_FAILOVER_MESSAGES)
5854 /* Print hunks of failover messages, doing line breaks as appropriate.
5855  Note that this assumes syslog is being used, rather than, e.g., the
5856  Windows NT logging facility, where just dumping the whole message in
5857  one hunk would be more appropriate. */
5858 
5859 void failover_print (char *obuf,
5860  unsigned *obufix, unsigned obufmax, const char *s)
5861 {
5862  int len = strlen (s);
5863 
5864  while (len + *obufix + 1 >= obufmax) {
5865  log_debug ("%s", obuf);
5866  if (!*obufix) {
5867  log_debug ("%s", s);
5868  *obufix = 0;
5869  return;
5870  }
5871  *obufix = 0;
5872  }
5873  strcpy (&obuf [*obufix], s);
5874  *obufix += len;
5875 }
5876 #endif /* defined (DEBUG_FAILOVER_MESSAGES) */
5877 
5878 /* Taken from draft-ietf-dhc-loadb-01.txt: */
5879 /* A "mixing table" of 256 distinct values, in pseudo-random order. */
5880 unsigned char loadb_mx_tbl[256] = {
5881  251, 175, 119, 215, 81, 14, 79, 191, 103, 49,
5882  181, 143, 186, 157, 0, 232, 31, 32, 55, 60,
5883  152, 58, 17, 237, 174, 70, 160, 144, 220, 90,
5884  57, 223, 59, 3, 18, 140, 111, 166, 203, 196,
5885  134, 243, 124, 95, 222, 179, 197, 65, 180, 48,
5886  36, 15, 107, 46, 233, 130, 165, 30, 123, 161,
5887  209, 23, 97, 16, 40, 91, 219, 61, 100, 10,
5888  210, 109, 250, 127, 22, 138, 29, 108, 244, 67,
5889  207, 9, 178, 204, 74, 98, 126, 249, 167, 116,
5890  34, 77, 193, 200, 121, 5, 20, 113, 71, 35,
5891  128, 13, 182, 94, 25, 226, 227, 199, 75, 27,
5892  41, 245, 230, 224, 43, 225, 177, 26, 155, 150,
5893  212, 142, 218, 115, 241, 73, 88, 105, 39, 114,
5894  62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
5895  154, 122, 12, 84, 82, 163, 44, 139, 228, 236,
5896  205, 242, 217, 11, 187, 146, 159, 64, 86, 239,
5897  195, 42, 106, 198, 118, 112, 184, 172, 87, 2,
5898  173, 117, 176, 229, 247, 253, 137, 185, 99, 164,
5899  102, 147, 45, 66, 231, 52, 141, 211, 194, 206,
5900  246, 238, 56, 110, 78, 248, 63, 240, 189, 93,
5901  92, 51, 53, 183, 19, 171, 72, 50, 33, 104,
5902  101, 69, 8, 252, 83, 120, 76, 135, 85, 54,
5903  202, 125, 188, 213, 96, 235, 136, 208, 162, 129,
5904  190, 132, 156, 38, 47, 1, 7, 254, 24, 4,
5905  216, 131, 89, 21, 28, 133, 37, 153, 149, 80,
5906  170, 68, 6, 169, 234, 151 };
5907 
5908 static unsigned char loadb_p_hash (const unsigned char *, unsigned);
5909 static unsigned char loadb_p_hash (const unsigned char *key, unsigned len)
5910 {
5911  unsigned char hash = len;
5912  int i;
5913  for(i = len; i > 0; )
5914  hash = loadb_mx_tbl [hash ^ (key [--i])];
5915  return hash;
5916 }
5917 
5918 int load_balance_mine (struct packet *packet, dhcp_failover_state_t *state)
5919 {
5920  struct option_cache *oc;
5921  struct data_string ds;
5922  unsigned char hbaix;
5923  int hm;
5924  u_int16_t ec;
5925 
5926  ec = ntohs(packet->raw->secs);
5927 
5928 #if defined(SECS_BYTEORDER)
5929  /*
5930  * If desired check to see if the secs field may have been byte
5931  * swapped. We assume it has if the high order byte isn't cleared
5932  * while the low order byte is cleared. In this case we swap the
5933  * bytes and continue processing.
5934  */
5935  if ((ec > 255) && ((ec & 0xff) == 0)) {
5936  ec = (ec >> 8) | (ec << 8);
5937  }
5938 #endif
5939 
5940  if (state->load_balance_max_secs < ec) {
5941  return (1);
5942  }
5943 
5944  /* If we don't have a hash bucket array, we can't tell if this
5945  one's ours, so we assume it's not. */
5946  if (!state->hba)
5947  return (0);
5948 
5949  oc = lookup_option(&dhcp_universe, packet->options,
5951  if (!oc)
5952  oc = lookup_option(&dhcp_universe, packet -> options,
5954  memset(&ds, 0, sizeof ds);
5955  if (oc &&
5956  evaluate_option_cache(&ds, packet, NULL, NULL,
5957  packet->options, NULL,
5958  &global_scope, oc, MDL)) {
5959  hbaix = loadb_p_hash(ds.data, ds.len);
5960 
5961  data_string_forget(&ds, MDL);
5962  } else {
5963  hbaix = loadb_p_hash(packet->raw->chaddr,
5964  packet->raw->hlen);
5965  }
5966 
5967  hm = state->hba[(hbaix >> 3) & 0x1F] & (1 << (hbaix & 0x07));
5968 
5969  if (state->i_am == primary)
5970  return (hm);
5971  else
5972  return (!hm);
5973 }
5974 
5975 /* The inverse of load_balance_mine ("load balance theirs"). We can't
5976  * use the regular load_balance_mine() and invert it because of the case
5977  * where there might not be an HBA, and we want to indicate false here
5978  * in this case only.
5979  */
5980 int
5981 peer_wants_lease(struct lease *lp)
5982 {
5983  dhcp_failover_state_t *state;
5984  unsigned char hbaix;
5985  int hm;
5986 
5987  if (!lp->pool)
5988  return 0;
5989 
5990  state = lp->pool->failover_peer;
5991 
5992  if (!state || !state->hba)
5993  return 0;
5994 
5995  if (lp->uid_len)
5996  hbaix = loadb_p_hash(lp->uid, lp->uid_len);
5997  else if (lp->hardware_addr.hlen > 1)
5998  /* Skip the first byte, which is the hardware type, and is
5999  * not included during actual load balancing checks above
6000  * since it is separate from the packet header chaddr field.
6001  * The remainder of the hardware address should be identical
6002  * to the chaddr contents.
6003  */
6004  hbaix = loadb_p_hash(lp->hardware_addr.hbuf + 1,
6005  lp->hardware_addr.hlen - 1);
6006  else /* impossible to categorize into LBA */
6007  return 0;
6008 
6009  hm = state->hba[(hbaix >> 3) & 0x1F] & (1 << (hbaix & 0x07));
6010 
6011  if (state->i_am == primary)
6012  return !hm;
6013  else
6014  return hm;
6015 }
6016 
6017 /* This deals with what to do with bind updates when
6018  we're in the normal state
6019 
6020  Note that tsfp had better be set from the latest bind update
6021  _before_ this function is called! */
6022 
6024 normal_binding_state_transition_check (struct lease *lease,
6025  dhcp_failover_state_t *state,
6026  binding_state_t binding_state,
6027  u_int32_t tsfp)
6028 {
6029  binding_state_t new_state;
6030 
6031  /* If there is no transition, it's no problem. */
6032  if (binding_state == lease -> binding_state)
6033  return binding_state;
6034 
6035  switch (lease -> binding_state) {
6036  case FTS_FREE:
6037  case FTS_ABANDONED:
6038  switch (binding_state) {
6039  case FTS_ACTIVE:
6040  case FTS_ABANDONED:
6041  case FTS_BACKUP:
6042  case FTS_EXPIRED:
6043  case FTS_RELEASED:
6044  case FTS_RESET:
6045  /* If the lease was free, and our peer is primary,
6046  then it can make it active, or abandoned, or
6047  backup. Abandoned is treated like free in
6048  this case. */
6049  if (state -> i_am == secondary)
6050  return binding_state;
6051 
6052  /* Otherwise, it can't legitimately do any sort of
6053  state transition. Because the lease was free,
6054  and the error has already been made, we allow the
6055  peer to change its state anyway, but log a warning
6056  message in hopes that the error will be fixed. */
6057  case FTS_FREE: /* for compiler */
6058  new_state = binding_state;
6059  goto out;
6060 
6061  default:
6062  log_fatal ("Impossible case at %s:%d.", MDL);
6063  return FTS_RESET;
6064  }
6065  case FTS_ACTIVE:
6066  /* The secondary can't change the state of an active
6067  lease. */
6068  if (state -> i_am == primary) {
6069  /* Except that the client may send the DHCPRELEASE
6070  to the secondary, and we have to accept that. */
6071  if (binding_state == FTS_RELEASED)
6072  return binding_state;
6073  new_state = lease -> binding_state;
6074  goto out;
6075  }
6076 
6077  /* So this is only for transitions made by the primary: */
6078  switch (binding_state) {
6079  case FTS_FREE:
6080  case FTS_BACKUP:
6081  /* Can't set a lease to free or backup until the
6082  peer agrees that it's expired. */
6083  if (tsfp > cur_time) {
6084  new_state = lease -> binding_state;
6085  goto out;
6086  }
6087  return binding_state;
6088 
6089  case FTS_EXPIRED:
6090  /* XXX 65 should be the clock skew between the peers
6091  XXX plus a fudge factor. This code will result
6092  XXX in problems if MCLT is really short or the
6093  XXX max-lease-time is really short (less than the
6094  XXX fudge factor. */
6095  if (lease -> ends - 65 > cur_time) {
6096  new_state = lease -> binding_state;
6097  goto out;
6098  }
6099 
6100  case FTS_RELEASED:
6101  case FTS_ABANDONED:
6102  case FTS_RESET:
6103  case FTS_ACTIVE:
6104  return binding_state;
6105 
6106  default:
6107  log_fatal ("Impossible case at %s:%d.", MDL);
6108  return FTS_RESET;
6109  }
6110  break;
6111  case FTS_EXPIRED:
6112  switch (binding_state) {
6113  case FTS_BACKUP:
6114  case FTS_FREE:
6115  /* Can't set a lease to free or backup until the
6116  peer agrees that it's expired. */
6117  if (tsfp > cur_time) {
6118  new_state = lease -> binding_state;
6119  goto out;
6120  }
6121  return binding_state;
6122 
6123  case FTS_ACTIVE:
6124  case FTS_RELEASED:
6125  case FTS_ABANDONED:
6126  case FTS_RESET:
6127  case FTS_EXPIRED:
6128  return binding_state;
6129 
6130  default:
6131  log_fatal ("Impossible case at %s:%d.", MDL);
6132  return FTS_RESET;
6133  }
6134  case FTS_RELEASED:
6135  switch (binding_state) {
6136  case FTS_FREE:
6137  case FTS_BACKUP:
6138 
6139  /* These are invalid state transitions - should we
6140  prevent them? */
6141  case FTS_EXPIRED:
6142  case FTS_ABANDONED:
6143  case FTS_RESET:
6144  case FTS_ACTIVE:
6145  case FTS_RELEASED:
6146  return binding_state;
6147 
6148  default:
6149  log_fatal ("Impossible case at %s:%d.", MDL);
6150  return FTS_RESET;
6151  }
6152  case FTS_RESET:
6153  switch (binding_state) {
6154  case FTS_FREE:
6155  case FTS_BACKUP:
6156  /* Can't set a lease to free or backup until the
6157  peer agrees that it's expired. */
6158  if (tsfp > cur_time) {
6159  new_state = lease -> binding_state;
6160  goto out;
6161  }
6162  return binding_state;
6163 
6164  case FTS_ACTIVE:
6165  case FTS_EXPIRED:
6166  case FTS_RELEASED:
6167  case FTS_ABANDONED:
6168  case FTS_RESET:
6169  return binding_state;
6170 
6171  default:
6172  log_fatal ("Impossible case at %s:%d.", MDL);
6173  return FTS_RESET;
6174  }
6175  case FTS_BACKUP:
6176  switch (binding_state) {
6177  case FTS_ACTIVE:
6178  case FTS_ABANDONED:
6179  case FTS_EXPIRED:
6180  case FTS_RELEASED:
6181  case FTS_RESET:
6182  /* If the lease was in backup, and our peer
6183  is secondary, then it can make it active
6184  or abandoned. */
6185  if (state -> i_am == primary)
6186  return binding_state;
6187 
6188  /* Either the primary or the secondary can
6189  reasonably move a lease from the backup
6190  state to the free state. */
6191  case FTS_FREE:
6192  return binding_state;
6193 
6194  case FTS_BACKUP:
6195  new_state = lease -> binding_state;
6196  goto out;
6197 
6198  default:
6199  log_fatal ("Impossible case at %s:%d.", MDL);
6200  return FTS_RESET;
6201  }
6202 
6203  default:
6204  log_fatal ("Impossible case at %s:%d.", MDL);
6205  return FTS_RESET;
6206  }
6207  out:
6208  return new_state;
6209 }
6210 
6211 /* Determine whether the state transition is okay when we're potentially
6212  in conflict with the peer. */
6214 conflict_binding_state_transition_check (struct lease *lease,
6215  dhcp_failover_state_t *state,
6216  binding_state_t binding_state,
6217  u_int32_t tsfp)
6218 {
6219  binding_state_t new_state;
6220 
6221  /* If there is no transition, it's no problem. */
6222  if (binding_state == lease -> binding_state)
6223  new_state = binding_state;
6224  else {
6225  switch (lease -> binding_state) {
6226  /* If we think the lease is not in use, then the
6227  state into which the partner put it is just fine,
6228  whatever it is. */
6229  case FTS_FREE:
6230  case FTS_ABANDONED:
6231  case FTS_EXPIRED:
6232  case FTS_RELEASED:
6233  case FTS_RESET:
6234  case FTS_BACKUP:
6235  new_state = binding_state;
6236  break;
6237 
6238  /* If we think the lease *is* in use, then we're not
6239  going to take the partner's change if the partner
6240  thinks it's free. */
6241  case FTS_ACTIVE:
6242  switch (binding_state) {
6243  case FTS_FREE:
6244  case FTS_BACKUP:
6245  new_state = lease -> binding_state;
6246  break;
6247 
6248  case FTS_EXPIRED:
6249  /* If we don't agree about expiry, it's
6250  * invalid. 65 should allow for max
6251  * clock skew (60) plus some fudge.
6252  * XXX: should we refetch cur_time?
6253  */
6254  if ((lease->ends - 65) > cur_time)
6255  new_state = lease->binding_state;
6256  else
6257  new_state = binding_state;
6258  break;
6259 
6260  /* RELEASED, RESET, and ABANDONED indicate
6261  * that our partner has information about
6262  * this lease that we did not witness. Our
6263  * partner wins.
6264  */
6265  case FTS_RELEASED:
6266  case FTS_RESET:
6267  case FTS_ABANDONED:
6268  new_state = binding_state;
6269  break;
6270 
6271  default:
6272  log_fatal ("Impossible case at %s:%d.", MDL);
6273  return FTS_RESET;
6274  }
6275  break;
6276 
6277  default:
6278  log_fatal ("Impossible case at %s:%d.", MDL);
6279  return FTS_RESET;
6280  }
6281  }
6282  return new_state;
6283 }
6284 
6285 /* We can reallocate a lease under the following circumstances:
6286 
6287  (1) It belongs to us - it's FTS_FREE, and we're primary, or it's
6288  FTS_BACKUP, and we're secondary.
6289  (2) We're in partner_down, and the lease is not active, and we
6290  can be sure that the other server didn't make it active.
6291  We can only be sure that the server didn't make it active
6292  when we are in the partner_down state and one of the following
6293  two conditions holds:
6294  (a) in the case that the time sent from the peer is earlier than
6295  the time we entered the partner_down state, at least MCLT has
6296  gone by since we entered partner_down, or
6297  (b) in the case that the time sent from the peer is later than
6298  the time when we entered partner_down, the current time is
6299  later than the time sent from the peer by at least MCLT. */
6300 
6301 int lease_mine_to_reallocate (struct lease *lease)
6302 {
6303  dhcp_failover_state_t *peer;
6304 
6305  if (lease && lease->pool &&
6306  (peer = lease->pool->failover_peer)) {
6307  /*
6308  * In addition to the normal rules governing wether a server
6309  * is allowed to operate changes on a lease, the server is
6310  * allowed to operate on a lease from the standpoint of the
6311  * most conservative guess of the peer's state for this lease.
6312  */
6313  switch (lease->binding_state) {
6314  case FTS_ACTIVE:
6315  /* ACTIVE leases may not be reallocated. */
6316  return 0;
6317 
6318  case FTS_FREE:
6319  case FTS_ABANDONED:
6320  /* FREE leases may only be allocated by the primary,
6321  * unless the secondary is acting in partner_down
6322  * state and stos+mclt or tsfp+mclt has expired,
6323  * whichever is greater.
6324  *
6325  * ABANDONED are treated the same as FREE for all
6326  * purposes here. Note that servers will only try
6327  * for ABANDONED leases as a last resort anyway.
6328  */
6329  if (peer -> i_am == primary)
6330  return 1;
6331 
6332  return(peer->service_state == service_partner_down &&
6333  ((lease->tsfp < peer->me.stos) ?
6334  (peer->me.stos + peer->mclt < cur_time) :
6335  (lease->tsfp + peer->mclt < cur_time)));
6336 
6337  case FTS_RELEASED:
6338  case FTS_EXPIRED:
6339  /*
6340  * These leases are generally untouchable until the
6341  * peer acknowledges their state change. However, as
6342  * this is impossible if the peer is offline, the
6343  * failover protocol permits an 'optimization' to
6344  * rewind the lease to a previous state that the server
6345  * is allowed to operate on, if that was the state that
6346  * was last acknowledged by the peer.
6347  *
6348  * So if a lease was free, was allocated by this
6349  * server, and expired without ever being transmitted
6350  * to the peer, it can be returned to free and given
6351  * to any new client legally.
6352  */
6353  if ((peer->i_am == primary) &&
6354  (lease->rewind_binding_state == FTS_FREE))
6355  return 1;
6356  if ((peer->i_am == secondary) &&
6357  (lease->rewind_binding_state == FTS_BACKUP))
6358  return 1;
6359 
6360  /* FALL THROUGH (released, expired, reset) */
6361  case FTS_RESET:
6362  /*
6363  * Released, expired, and reset leases go onto the
6364  * 'expired' queue all together. Upon entry into
6365  * partner-down state, this queue of leases has their
6366  * tsfp values modified to equal stos+mclt, the point
6367  * at which the server is allowed to remove them from
6368  * these transitional states.
6369  *
6370  * Note that although tsfp has been possibly extended
6371  * past the actual tsfp we received from the peer, we
6372  * don't have to take any special action. Since tsfp
6373  * will be equal to the current time when the lease
6374  * transitions to free, tsfp will not be used to grant
6375  * lease-times longer than the MCLT to clients, which
6376  * is the only danger for this sort of modification.
6377  */
6378  return((peer->service_state == service_partner_down) &&
6379  (lease->tsfp < cur_time));
6380 
6381  case FTS_BACKUP:
6382  /* Only the secondary may allocate BACKUP leases,
6383  * unless in partner_down state in which case at
6384  * least TSFP+MCLT or STOS+MCLT must have expired,
6385  * whichever is greater.
6386  */
6387  if (peer->i_am == secondary)
6388  return 1;
6389 
6390  return((peer->service_state == service_partner_down) &&
6391  ((lease->tsfp < peer->me.stos) ?
6392  (peer->me.stos + peer->mclt < cur_time) :
6393  (lease->tsfp + peer->mclt < cur_time)));
6394 
6395  default:
6396  /* All lease states appear above. */
6397  log_fatal("Impossible case at %s:%d.", MDL);
6398  break;
6399  }
6400  return 0;
6401  }
6402  if (lease)
6403  return(lease->binding_state == FTS_FREE ||
6404  lease->binding_state == FTS_BACKUP);
6405  else
6406  return 0;
6407 }
6408 
6409 static isc_result_t failover_message_reference (failover_message_t **mp,
6410  failover_message_t *m,
6411  const char *file, int line)
6412 {
6413  *mp = m;
6414  m -> refcnt++;
6415  return ISC_R_SUCCESS;
6416 }
6417 
6418 static isc_result_t failover_message_dereference (failover_message_t **mp,
6419  const char *file, int line)
6420 {
6421  failover_message_t *m;
6422  m = (*mp);
6423  m -> refcnt--;
6424  if (m -> refcnt == 0) {
6425  if (m -> next)
6426  failover_message_dereference (&m -> next,
6427  file, line);
6428  if (m -> chaddr.data)
6429  dfree (m -> chaddr.data, file, line);
6430  if (m -> client_identifier.data)
6431  dfree (m -> client_identifier.data, file, line);
6432  if (m -> hba.data)
6433  dfree (m -> hba.data, file, line);
6434  if (m -> message.data)
6435  dfree (m -> message.data, file, line);
6436  if (m -> relationship_name.data)
6437  dfree (m -> relationship_name.data, file, line);
6438  if (m -> reply_options.data)
6439  dfree (m -> reply_options.data, file, line);
6440  if (m -> request_options.data)
6441  dfree (m -> request_options.data, file, line);
6442  if (m -> vendor_class.data)
6443  dfree (m -> vendor_class.data, file, line);
6444  if (m -> vendor_options.data)
6445  dfree (m -> vendor_options.data, file, line);
6446  if (m -> ddns.data)
6447  dfree (m -> ddns.data, file, line);
6448  dfree (*mp, file, line);
6449  }
6450  *mp = 0;
6451  return ISC_R_SUCCESS;
6452 }
6453 
6454 OMAPI_OBJECT_ALLOC (dhcp_failover_state, dhcp_failover_state_t,
6456 OMAPI_OBJECT_ALLOC (dhcp_failover_listener, dhcp_failover_listener_t,
6458 OMAPI_OBJECT_ALLOC (dhcp_failover_link, dhcp_failover_link_t,
6460 #endif /* defined (FAILOVER_PROTOCOL) */
6461 
6462 const char *binding_state_print (enum failover_state state)
6463 {
6464  switch (state) {
6465  case FTS_FREE:
6466  return "free";
6467  break;
6468 
6469  case FTS_ACTIVE:
6470  return "active";
6471  break;
6472 
6473  case FTS_EXPIRED:
6474  return "expired";
6475  break;
6476 
6477  case FTS_RELEASED:
6478  return "released";
6479  break;
6480 
6481  case FTS_ABANDONED:
6482  return "abandoned";
6483  break;
6484 
6485  case FTS_RESET:
6486  return "reset";
6487  break;
6488 
6489  case FTS_BACKUP:
6490  return "backup";
6491  break;
6492 
6493  default:
6494  return "unknown";
6495  break;
6496  }
6497 }
isc_result_t dhcp_failover_state_signal(omapi_object_t *, const char *, va_list)
#define FTS_ABANDONED
Definition: dhcpd.h:502
int supersede_lease(struct lease *, struct lease *, int, int, int, int)
Definition: mdb.c:1095
unsigned len
Definition: omapip.h:83
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:3615
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 **)
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:521
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:454
omapi_object_type_t * dhcp_type_failover_link
#define FTS_FREE
Definition: dhcpd.h:498
#define DHCP_R_PROTOCOLERROR
Definition: result.h:47
struct shared_network * shared_networks
Definition: mdb.c:34
unsigned char * uid
Definition: dhcpd.h:539
#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:582
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
struct lease * reserved
Definition: dhcpd.h:951
#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:501
int int int log_debug(const char *,...) __attribute__((__format__(__printf__
struct lease * next_pending
Definition: dhcpd.h:596
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 lease * abandoned
Definition: dhcpd.h:950
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:500
int binding_scope_dereference(struct binding_scope **ptr, const char *file, int line)
Definition: tree.c:3775
#define ON_UPDATE_QUEUE
Definition: dhcpd.h:550
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:541
void(* tvunref_t)(void *, const char *, int)
Definition: dhcpd.h:1350
unsigned len
Definition: inet.h:32
dhcp_failover_state_t * failover_peer
Definition: dhcpd.h:961
#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:1349
const char * binding_state_print(enum failover_state state)
Definition: failover.c:6462
struct option_state * options
Definition: dhcpd.h:414
isc_result_t dhcp_failover_send_bind_ack(dhcp_failover_state_t *, failover_message_t *, int, const char *)
struct lease * backup
Definition: dhcpd.h:949
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:377
isc_result_t dhcp_failover_state_destroy(omapi_object_t *, const char *, int)
void pool_timer(void *)
Definition: mdb.c:1867
struct hardware hardware_addr
Definition: dhcpd.h:543
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 *)
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 *)
struct lease * active
Definition: dhcpd.h:946
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:527
#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:939
binding_state_t binding_state
Definition: dhcpd.h:577
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:1815
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:504
Definition: dhcpd.h:376
struct pool * pool
Definition: dhcpd.h:532
isc_result_t omapi_object_dereference(omapi_object_t **, const char *, int)
Definition: alloc.c:579
void commit_leases_timeout(void *)
Definition: db.c:1003
isc_result_t omapi_signal(omapi_object_t *, const char *,...)
Definition: support.c:268
int dhcp_failover_queue_ack(dhcp_failover_state_t *, failover_message_t *msg)
dhcp_failover_listener_t
Definition: dhcpd.h:3586
TIME atsfp
Definition: dhcpd.h:593
u_int8_t * data
Definition: dhcpd.h:252
#define cur_time
Definition: dhcpd.h:1988
int free_leases
Definition: dhcpd.h:954
isc_result_t dhcp_failover_link_signal(omapi_object_t *, const char *, va_list)
#define BACKUP_LEASES
TIME starts
Definition: dhcpd.h:527
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:545
struct lease * free
Definition: dhcpd.h:948
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:953
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
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:503
#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:595
unsigned addrlen
Definition: omapip.h:137
TIME cltt
Definition: dhcpd.h:594
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:540
struct iaddr ip_addr
Definition: dhcpd.h:526
#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:552
isc_result_t ddns_removals(struct lease *, struct iasubopt *, struct dhcp_ddns_cb *, isc_boolean_t)
#define RESERVED_LEASE
Definition: dhcpd.h:548
struct timeval cur_tv
Definition: dispatch.c:35
binding_state_t rewind_binding_state
Definition: dhcpd.h:580
TIME tstp
Definition: dhcpd.h:591
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:579
int dhcp_failover_write_all_states(void)
int commit_leases()
Definition: dhclient.c:1810
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:592
#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:455
struct lease * next
Definition: dhcpd.h:523
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:154
#define FREE_LEASES
struct lease * expired
Definition: dhcpd.h:947
struct ipv6_pool ** pools
TIME next_event_time
Definition: dhcpd.h:952
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:542
isc_result_t dhcp_failover_listener_signal(omapi_object_t *, const char *, va_list)
#define ON_ACK_QUEUE
Definition: dhcpd.h:551
struct shared_network * next
Definition: dhcpd.h:969
#define DHCP_R_INCOMPLETE
Definition: result.h:57
#define BOOTP_LEASE
Definition: dhcpd.h:547
const char * file
Definition: dhcpd.h:3615
#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
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 *)
isc_result_t dhcp_failover_send_update_request_all(dhcp_failover_state_t *)
TIME ends
Definition: dhcpd.h:527
struct binding_scope * scope
Definition: dhcpd.h:529
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:65
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 *)
int find_lease_by_ip_addr(struct lease **, struct iaddr, const char *, int)
Definition: mdb.c:2003
isc_result_t dhcp_failover_listener_get_value(omapi_object_t *, omapi_object_t *, omapi_data_string_t *, omapi_value_t **)
binding_state_t next_binding_state
Definition: dhcpd.h:578
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:505
int dhcp_failover_state_pool_check(dhcp_failover_state_t *)
isc_result_t dhcp_failover_process_bind_update(dhcp_failover_state_t *, failover_message_t *)
struct pool * pools
Definition: dhcpd.h:977
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:941
#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:1644
int backup_leases
Definition: dhcpd.h:955
#define FTS_ACTIVE
Definition: dhcpd.h:499
void dhcp_failover_auto_partner_down(void *vs)