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