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