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