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