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