server/failover.c

Go to the documentation of this file.
00001 /* failover.c
00002 
00003    Failover protocol support code... */
00004 
00005 /*
00006  * Copyright (c) 2004-2014 by Internet Systems Consortium, Inc. ("ISC")
00007  * Copyright (c) 1999-2003 by Internet Software Consortium
00008  *
00009  * Permission to use, copy, modify, and distribute this software for any
00010  * purpose with or without fee is hereby granted, provided that the above
00011  * copyright notice and this permission notice appear in all copies.
00012  *
00013  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
00014  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00015  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
00016  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00017  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00018  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
00019  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00020  *
00021  *   Internet Systems Consortium, Inc.
00022  *   950 Charter Street
00023  *   Redwood City, CA 94063
00024  *   <info@isc.org>
00025  *   https://www.isc.org/
00026  *
00027  */
00028 
00029 #include "cdefs.h"
00030 #include "dhcpd.h"
00031 #include <omapip/omapip_p.h>
00032 
00033 #include "trace.h"
00034 
00035 #if defined (FAILOVER_PROTOCOL)
00036 dhcp_failover_state_t *failover_states;
00037 static isc_result_t do_a_failover_option (omapi_object_t *,
00038                                           dhcp_failover_link_t *);
00039 dhcp_failover_listener_t *failover_listeners;
00040 
00041 static isc_result_t failover_message_reference (failover_message_t **,
00042                                                 failover_message_t *,
00043                                                 const char *file, int line);
00044 static isc_result_t failover_message_dereference (failover_message_t **,
00045                                                   const char *file, int line);
00046 
00047 static void dhcp_failover_pool_balance(dhcp_failover_state_t *state);
00048 static void dhcp_failover_pool_reqbalance(dhcp_failover_state_t *state);
00049 static int dhcp_failover_pool_dobalance(dhcp_failover_state_t *state,
00050                                         isc_boolean_t *sendreq);
00051 static inline int secondary_not_hoarding(dhcp_failover_state_t *state,
00052                                          struct pool *p);
00053 
00054 
00055 void dhcp_failover_startup ()
00056 {
00057         dhcp_failover_state_t *state;
00058         isc_result_t status;
00059         struct timeval tv;
00060 
00061         for (state = failover_states; state; state = state -> next) {
00062                 dhcp_failover_state_transition (state, "startup");
00063 
00064                 if (state -> pool_count == 0) {
00065                         log_error ("failover peer declaration with no %s",
00066                                    "referring pools.");
00067                         log_error ("In order to use failover, you MUST %s",
00068                                    "refer to your main failover declaration");
00069                         log_error ("in each pool declaration.   You MUST %s",
00070                                    "NOT use range declarations outside");
00071                         log_fatal ("of pool declarations.");
00072                 }
00073                 /* In case the peer is already running, immediately try
00074                    to establish a connection with it. */
00075                 status = dhcp_failover_link_initiate ((omapi_object_t *)state);
00076                 if (status != ISC_R_SUCCESS && status != DHCP_R_INCOMPLETE) {
00077 #if defined (DEBUG_FAILOVER_TIMING)
00078                         log_info ("add_timeout +90 dhcp_failover_reconnect");
00079 #endif
00080                         tv . tv_sec = cur_time + 90;
00081                         tv . tv_usec = 0;
00082                         add_timeout (&tv,
00083                                      dhcp_failover_reconnect, state,
00084                                      (tvref_t)
00085                                      dhcp_failover_state_reference,
00086                                      (tvunref_t)
00087                                      dhcp_failover_state_dereference);
00088                         log_error ("failover peer %s: %s", state -> name,
00089                                    isc_result_totext (status));
00090                 }
00091 
00092                 status = (dhcp_failover_listen
00093                           ((omapi_object_t *)state));
00094                 if (status != ISC_R_SUCCESS) {
00095 #if defined (DEBUG_FAILOVER_TIMING)
00096                         log_info ("add_timeout +90 %s",
00097                                   "dhcp_failover_listener_restart");
00098 #endif
00099                         tv . tv_sec = cur_time + 90;
00100                         tv . tv_usec = 0;
00101                         add_timeout (&tv,
00102                                      dhcp_failover_listener_restart,
00103                                      state,
00104                                      (tvref_t)omapi_object_reference,
00105                                      (tvunref_t)omapi_object_dereference);
00106                 }
00107         }
00108 }
00109 
00110 int dhcp_failover_write_all_states ()
00111 {
00112         dhcp_failover_state_t *state;
00113 
00114         for (state = failover_states; state; state = state -> next) {
00115                 if (!write_failover_state (state))
00116                         return 0;
00117         }
00118         return 1;
00119 }
00120 
00121 isc_result_t enter_failover_peer (peer)
00122         dhcp_failover_state_t *peer;
00123 {
00124         dhcp_failover_state_t *dup = (dhcp_failover_state_t *)0;
00125         isc_result_t status;
00126 
00127         status = find_failover_peer (&dup, peer -> name, MDL);
00128         if (status == ISC_R_NOTFOUND) {
00129                 if (failover_states) {
00130                         dhcp_failover_state_reference (&peer -> next,
00131                                                       failover_states, MDL);
00132                         dhcp_failover_state_dereference (&failover_states,
00133                                                          MDL);
00134                 }
00135                 dhcp_failover_state_reference (&failover_states, peer, MDL);
00136                 return ISC_R_SUCCESS;
00137         }
00138         dhcp_failover_state_dereference (&dup, MDL);
00139         if (status == ISC_R_SUCCESS)
00140                 return ISC_R_EXISTS;
00141         return status;
00142 }
00143 
00144 isc_result_t find_failover_peer (peer, name, file, line)
00145         dhcp_failover_state_t **peer;
00146         const char *name;
00147         const char *file;
00148         int line;
00149 {
00150         dhcp_failover_state_t *p;
00151 
00152         for (p = failover_states; p; p = p -> next)
00153                 if (!strcmp (name, p -> name))
00154                         break;
00155         if (p)
00156                 return dhcp_failover_state_reference (peer, p, file, line);
00157         return ISC_R_NOTFOUND;
00158 }
00159 
00160 /* The failover protocol has three objects associated with it.  For
00161    each failover partner declaration in the dhcpd.conf file, primary
00162    or secondary, there is a failover_state object.  For any primary or
00163    secondary state object that has a connection to its peer, there is
00164    also a failover_link object, which has its own input state separate
00165    from the failover protocol state for managing the actual bytes
00166    coming in off the wire.  Finally, there will be one listener object
00167    for every distinct port number associated with a secondary
00168    failover_state object.  Normally all secondary failover_state
00169    objects are expected to listen on the same port number, so there
00170    need be only one listener object, but if different port numbers are
00171    specified for each failover object, there could be as many as one
00172    listener object for each secondary failover_state object. */
00173 
00174 /* This, then, is the implementation of the failover link object. */
00175 
00176 isc_result_t dhcp_failover_link_initiate (omapi_object_t *h)
00177 {
00178         isc_result_t status;
00179         dhcp_failover_link_t *obj;
00180         dhcp_failover_state_t *state;
00181         omapi_object_t *o;
00182         int i;
00183         struct data_string ds;
00184         omapi_addr_list_t *addrs = (omapi_addr_list_t *)0;
00185         omapi_addr_t local_addr;
00186 
00187         /* Find the failover state in the object chain. */
00188         for (o = h; o -> outer; o = o -> outer)
00189                 ;
00190         for (; o; o = o -> inner) {
00191                 if (o -> type == dhcp_type_failover_state)
00192                         break;
00193         }
00194         if (!o)
00195                 return DHCP_R_INVALIDARG;
00196         state = (dhcp_failover_state_t *)o;
00197 
00198         obj = (dhcp_failover_link_t *)0;
00199         status = dhcp_failover_link_allocate (&obj, MDL);
00200         if (status != ISC_R_SUCCESS)
00201                 return status;
00202         option_cache_reference (&obj -> peer_address,
00203                                 state -> partner.address, MDL);
00204         obj -> peer_port = state -> partner.port;
00205         dhcp_failover_state_reference (&obj -> state_object, state, MDL);
00206 
00207         memset (&ds, 0, sizeof ds);
00208         if (!evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0,
00209                                     (struct client_state *)0,
00210                                     (struct option_state *)0,
00211                                     (struct option_state *)0,
00212                                     &global_scope, obj -> peer_address, MDL)) {
00213                 dhcp_failover_link_dereference (&obj, MDL);
00214                 return ISC_R_UNEXPECTED;
00215         }
00216 
00217         /* Make an omapi address list out of a buffer containing zero or more
00218            IPv4 addresses. */
00219         status = omapi_addr_list_new (&addrs, ds.len / 4, MDL);
00220         if (status != ISC_R_SUCCESS) {
00221                 dhcp_failover_link_dereference (&obj, MDL);
00222                 return status;
00223         }
00224 
00225         for (i = 0; i  < addrs -> count; i++) {
00226                 addrs -> addresses [i].addrtype = AF_INET;
00227                 addrs -> addresses [i].addrlen = sizeof (struct in_addr);
00228                 memcpy (addrs -> addresses [i].address,
00229                         &ds.data [i * 4], sizeof (struct in_addr));
00230                 addrs -> addresses [i].port = obj -> peer_port;
00231         }
00232         data_string_forget (&ds, MDL);
00233 
00234         /* Now figure out the local address that we're supposed to use. */
00235         if (!state -> me.address ||
00236             !evaluate_option_cache (&ds, (struct packet *)0,
00237                                     (struct lease *)0,
00238                                     (struct client_state *)0,
00239                                     (struct option_state *)0,
00240                                     (struct option_state *)0,
00241                                     &global_scope, state -> me.address,
00242                                     MDL)) {
00243                 memset (&local_addr, 0, sizeof local_addr);
00244                 local_addr.addrtype = AF_INET;
00245                 local_addr.addrlen = sizeof (struct in_addr);
00246                 if (!state -> server_identifier.len) {
00247                         log_fatal ("failover peer %s: no local address.",
00248                                    state -> name);
00249                 }
00250         } else {
00251                 if (ds.len != sizeof (struct in_addr)) {
00252                         log_error("failover peer %s: 'address' parameter "
00253                                   "fails to resolve to an IPv4 address",
00254                                   state->name);
00255                         data_string_forget (&ds, MDL);
00256                         dhcp_failover_link_dereference (&obj, MDL);
00257                         omapi_addr_list_dereference (&addrs, MDL);
00258                         return DHCP_R_INVALIDARG;
00259                 }
00260                 local_addr.addrtype = AF_INET;
00261                 local_addr.addrlen = ds.len;
00262                 memcpy (local_addr.address, ds.data, ds.len);
00263                 if (!state -> server_identifier.len)
00264                         data_string_copy (&state -> server_identifier,
00265                                           &ds, MDL);
00266                 data_string_forget (&ds, MDL);
00267                 local_addr.port = 0;  /* Let the O.S. choose. */
00268         }
00269 
00270         status = omapi_connect_list ((omapi_object_t *)obj,
00271                                      addrs, &local_addr);
00272         omapi_addr_list_dereference (&addrs, MDL);
00273 
00274         dhcp_failover_link_dereference (&obj, MDL);
00275         return status;
00276 }
00277 
00278 isc_result_t dhcp_failover_link_signal (omapi_object_t *h,
00279                                         const char *name, va_list ap)
00280 {
00281         isc_result_t status;
00282         dhcp_failover_link_t *link;
00283         omapi_object_t *c;
00284         dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
00285         char *sname;
00286         int slen;
00287         struct timeval tv;
00288 
00289         if (h -> type != dhcp_type_failover_link) {
00290                 /* XXX shouldn't happen.   Put an assert here? */
00291                 return ISC_R_UNEXPECTED;
00292         }
00293         link = (dhcp_failover_link_t *)h;
00294 
00295         if (!strcmp (name, "connect")) {
00296             if (link -> state_object -> i_am == primary) {
00297                 status = dhcp_failover_send_connect (h);
00298                 if (status != ISC_R_SUCCESS) {
00299                     log_info ("dhcp_failover_send_connect: %s",
00300                               isc_result_totext (status));
00301                     omapi_disconnect (h -> outer, 1);
00302                 }
00303             } else
00304                 status = ISC_R_SUCCESS;
00305             /* Allow the peer fifteen seconds to send us a
00306                startup message. */
00307 #if defined (DEBUG_FAILOVER_TIMING)
00308             log_info ("add_timeout +15 %s",
00309                       "dhcp_failover_link_startup_timeout");
00310 #endif
00311             tv . tv_sec = cur_time + 15;
00312             tv . tv_usec = 0;
00313             add_timeout (&tv,
00314                          dhcp_failover_link_startup_timeout,
00315                          link,
00316                          (tvref_t)dhcp_failover_link_reference,
00317                          (tvunref_t)dhcp_failover_link_dereference);
00318             return status;
00319         }
00320 
00321         if (!strcmp (name, "disconnect")) {
00322             if (link -> state_object) {
00323                 dhcp_failover_state_reference (&state,
00324                                                link -> state_object, MDL);
00325                 link -> state = dhcp_flink_disconnected;
00326 
00327                 /* Make the transition. */
00328                 if (state->link_to_peer == link)
00329                     dhcp_failover_state_transition(link->state_object, name);
00330 
00331                 /* Schedule an attempt to reconnect. */
00332 #if defined (DEBUG_FAILOVER_TIMING)
00333                 log_info("add_timeout +5 dhcp_failover_reconnect");
00334 #endif
00335                 tv.tv_sec = cur_time + 5;
00336                 tv.tv_usec = cur_tv.tv_usec;
00337                 add_timeout(&tv, dhcp_failover_reconnect, state,
00338                             (tvref_t)dhcp_failover_state_reference,
00339                             (tvunref_t)dhcp_failover_state_dereference);
00340 
00341                 dhcp_failover_state_dereference (&state, MDL);
00342             }
00343             return ISC_R_SUCCESS;
00344         }
00345 
00346         if (!strcmp (name, "status")) {
00347           if (link -> state_object) {
00348             isc_result_t        status;
00349 
00350             status = va_arg(ap, isc_result_t);
00351 
00352             if ((status == ISC_R_HOSTUNREACH) || (status == ISC_R_TIMEDOUT)) {
00353               dhcp_failover_state_reference (&state,
00354                                              link -> state_object, MDL);
00355               link -> state = dhcp_flink_disconnected;
00356 
00357               /* Make the transition. */
00358               dhcp_failover_state_transition (link -> state_object,
00359                                               "disconnect");
00360 
00361               /* Start trying to reconnect. */
00362 #if defined (DEBUG_FAILOVER_TIMING)
00363               log_info ("add_timeout +5 %s",
00364                         "dhcp_failover_reconnect");
00365 #endif
00366               tv . tv_sec = cur_time + 5;
00367               tv . tv_usec = 0;
00368               add_timeout (&tv, dhcp_failover_reconnect,
00369                            state,
00370                            (tvref_t)dhcp_failover_state_reference,
00371                            (tvunref_t)dhcp_failover_state_dereference);
00372             }
00373             dhcp_failover_state_dereference (&state, MDL);
00374           }
00375           return ISC_R_SUCCESS;
00376         }
00377 
00378         /* Not a signal we recognize? */
00379         if (strcmp (name, "ready")) {
00380                 if (h -> inner && h -> inner -> type -> signal_handler)
00381                         return (*(h -> inner -> type -> signal_handler))
00382                                 (h -> inner, name, ap);
00383                 return ISC_R_NOTFOUND;
00384         }
00385 
00386         if (!h -> outer || h -> outer -> type != omapi_type_connection)
00387                 return DHCP_R_INVALIDARG;
00388         c = h -> outer;
00389 
00390         /* We get here because we requested that we be woken up after
00391            some number of bytes were read, and that number of bytes
00392            has in fact been read. */
00393         switch (link -> state) {
00394               case dhcp_flink_start:
00395                 link -> state = dhcp_flink_message_length_wait;
00396                 if ((omapi_connection_require (c, 2)) != ISC_R_SUCCESS)
00397                         break;
00398               case dhcp_flink_message_length_wait:
00399               next_message:
00400                 link -> state = dhcp_flink_message_wait;
00401                 link -> imsg = dmalloc (sizeof (failover_message_t), MDL);
00402                 if (!link -> imsg) {
00403                         status = ISC_R_NOMEMORY;
00404                       dhcp_flink_fail:
00405                         if (link -> imsg) {
00406                                 failover_message_dereference (&link->imsg,
00407                                                               MDL);
00408                         }
00409                         link -> state = dhcp_flink_disconnected;
00410                         log_info ("message length wait: %s",
00411                                   isc_result_totext (status));
00412                         omapi_disconnect (c, 1);
00413                         /* XXX just blow away the protocol state now?
00414                            XXX or will disconnect blow it away? */
00415                         return ISC_R_UNEXPECTED;
00416                 }
00417                 memset (link -> imsg, 0, sizeof (failover_message_t));
00418                 link -> imsg -> refcnt = 1;
00419                 /* Get the length: */
00420                 omapi_connection_get_uint16 (c, &link -> imsg_len);
00421                 link -> imsg_count = 0; /* Bytes read. */
00422 
00423                 /* Ensure the message is of valid length. */
00424                 if (link->imsg_len < DHCP_FAILOVER_MIN_MESSAGE_SIZE ||
00425                     link->imsg_len > DHCP_FAILOVER_MAX_MESSAGE_SIZE) {
00426                         status = ISC_R_UNEXPECTED;
00427                         goto dhcp_flink_fail;
00428                 }
00429 
00430                 if ((omapi_connection_require (c, link -> imsg_len - 2U)) !=
00431                     ISC_R_SUCCESS)
00432                         break;
00433               case dhcp_flink_message_wait:
00434                 /* Read in the message.  At this point we have the
00435                    entire message in the input buffer.  For each
00436                    incoming value ID, set a bit in the bitmask
00437                    indicating that we've gotten it.  Maybe flag an
00438                    error message if the bit is already set.  Once
00439                    we're done reading, we can check the bitmask to
00440                    make sure that the required fields for each message
00441                    have been included. */
00442 
00443                 link -> imsg_count += 2;        /* Count the length as read. */
00444 
00445                 /* Get message type. */
00446                 omapi_connection_copyout (&link -> imsg -> type, c, 1);
00447                 link -> imsg_count++;
00448 
00449                 /* Get message payload offset. */
00450                 omapi_connection_copyout (&link -> imsg_payoff, c, 1);
00451                 link -> imsg_count++;
00452 
00453                 /* Get message time. */
00454                 omapi_connection_get_uint32 (c, &link -> imsg -> time);
00455                 link -> imsg_count += 4;
00456 
00457                 /* Get transaction ID. */
00458                 omapi_connection_get_uint32 (c, &link -> imsg -> xid);
00459                 link -> imsg_count += 4;
00460 
00461 #if defined (DEBUG_FAILOVER_MESSAGES)
00462 # if !defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
00463                 if (link->imsg->type == FTM_CONTACT)
00464                         goto skip_contact;
00465 # endif
00466                 log_info ("link: message %s  payoff %d  time %ld  xid %ld",
00467                           dhcp_failover_message_name (link -> imsg -> type),
00468                           link -> imsg_payoff,
00469                           (unsigned long)link -> imsg -> time,
00470                           (unsigned long)link -> imsg -> xid);
00471 # if !defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
00472               skip_contact:
00473 # endif
00474 #endif
00475                 /* Skip over any portions of the message header that we
00476                    don't understand. */
00477                 if (link -> imsg_payoff - link -> imsg_count) {
00478                         omapi_connection_copyout ((unsigned char *)0, c,
00479                                                   (link -> imsg_payoff -
00480                                                    link -> imsg_count));
00481                         link -> imsg_count = link -> imsg_payoff;
00482                 }
00483                                 
00484                 /* Now start sucking options off the wire. */
00485                 while (link -> imsg_count < link -> imsg_len) {
00486                         status = do_a_failover_option (c, link);
00487                         if (status != ISC_R_SUCCESS)
00488                                 goto dhcp_flink_fail;
00489                 }
00490 
00491                 /* If it's a connect message, try to associate it with
00492                    a state object. */
00493                 /* XXX this should be authenticated! */
00494                 if (link -> imsg -> type == FTM_CONNECT) {
00495                     const char *errmsg;
00496                     int reason;
00497 
00498                     if (!(link->imsg->options_present &
00499                                 FTB_RELATIONSHIP_NAME)) {
00500                         errmsg = "missing relationship-name";
00501                         reason = FTR_INVALID_PARTNER;
00502                         goto badconnect;
00503                     }
00504 
00505                     /* See if we can find a failover_state object that
00506                        matches this connection.  This message should only
00507                        be received by a secondary from a primary. */
00508                     for (s = failover_states; s; s = s -> next) {
00509                         if (dhcp_failover_state_match_by_name(s,
00510                             &link->imsg->relationship_name))
00511                                 state = s;
00512                     }
00513 
00514                     /* If we can't find a failover protocol state
00515                        for this remote host, drop the connection */
00516                     if (!state) {
00517                             errmsg = "unknown failover relationship name";
00518                             reason = FTR_INVALID_PARTNER;
00519 
00520                           badconnect:
00521                                 /* XXX Send a refusal message first?
00522                                    XXX Look in protocol spec for guidance. */
00523 
00524                             if (state != NULL) {
00525                                 sname = state->name;
00526                                 slen = strlen(sname);
00527                             } else if (link->imsg->options_present &
00528                                        FTB_RELATIONSHIP_NAME) {
00529                                 sname = (char *)link->imsg->
00530                                                 relationship_name.data;
00531                                 slen = link->imsg->relationship_name.count;
00532                             } else {
00533                                 sname = "unknown";
00534                                 slen = strlen(sname);
00535                             }
00536 
00537                             log_error("Failover CONNECT from %.*s: %s",
00538                                       slen, sname, errmsg);
00539                             dhcp_failover_send_connectack
00540                                     ((omapi_object_t *)link, state,
00541                                      reason, errmsg);
00542                             log_info ("failover: disconnect: %s", errmsg);
00543                             omapi_disconnect (c, 0);
00544                             link -> state = dhcp_flink_disconnected;
00545                             return ISC_R_SUCCESS;
00546                     }
00547 
00548                     if ((cur_time > link -> imsg -> time &&
00549                          cur_time - link -> imsg -> time > 60) ||
00550                         (cur_time < link -> imsg -> time &&
00551                          link -> imsg -> time - cur_time > 60)) {
00552                             errmsg = "time offset too large";
00553                             reason = FTR_TIMEMISMATCH;
00554                             goto badconnect;
00555                     }
00556 
00557                     if (!(link -> imsg -> options_present & FTB_HBA) ||
00558                         link -> imsg -> hba.count != 32) {
00559                             errmsg = "invalid HBA";
00560                             reason = FTR_HBA_CONFLICT; /* XXX */
00561                             goto badconnect;
00562                     }
00563                     if (state -> hba)
00564                             dfree (state -> hba, MDL);
00565                     state -> hba = dmalloc (32, MDL);
00566                     if (!state -> hba) {
00567                             errmsg = "no memory";
00568                             reason = FTR_MISC_REJECT;
00569                             goto badconnect;
00570                     }
00571                     memcpy (state -> hba, link -> imsg -> hba.data, 32);
00572 
00573                     if (!link -> state_object)
00574                             dhcp_failover_state_reference
00575                                     (&link -> state_object, state, MDL);
00576                     if (!link -> peer_address)
00577                             option_cache_reference
00578                                     (&link -> peer_address,
00579                                      state -> partner.address, MDL);
00580                 }
00581 
00582                 /* If we don't have a state object at this point, it's
00583                    some kind of bogus situation, so just drop the
00584                    connection. */
00585                 if (!link -> state_object) {
00586                         log_info ("failover: connect: no matching state.");
00587                         omapi_disconnect (c, 1);
00588                         link -> state = dhcp_flink_disconnected;
00589                         return DHCP_R_INVALIDARG;
00590                 }
00591 
00592                 /* Once we have the entire message, and we've validated
00593                    it as best we can here, pass it to the parent. */
00594                 omapi_signal ((omapi_object_t *)link -> state_object,
00595                               "message", link);
00596                 link -> state = dhcp_flink_message_length_wait;
00597                 if (link -> imsg)
00598                         failover_message_dereference (&link -> imsg, MDL);
00599                 /* XXX This is dangerous because we could get into a tight
00600                    XXX loop reading input without servicing any other stuff.
00601                    XXX There needs to be a way to relinquish control but
00602                    XXX get it back immediately if there's no other work to
00603                    XXX do. */
00604                 if ((omapi_connection_require (c, 2)) == ISC_R_SUCCESS)
00605                         goto next_message;
00606                 break;
00607 
00608               default:
00609                 log_fatal("Impossible case at %s:%d.", MDL);
00610                 break;
00611         }
00612         return ISC_R_SUCCESS;
00613 }
00614 
00615 static isc_result_t do_a_failover_option (c, link)
00616         omapi_object_t *c;
00617         dhcp_failover_link_t *link;
00618 {
00619         u_int16_t option_code;
00620         u_int16_t option_len;
00621         unsigned char *op;
00622         unsigned op_size;
00623         unsigned op_count;
00624         int i;
00625         
00626         if (link -> imsg_count + 2 > link -> imsg_len) {
00627                 log_error ("FAILOVER: message overflow at option code.");
00628                 return DHCP_R_PROTOCOLERROR;
00629         }
00630 
00631         /* Get option code. */
00632         omapi_connection_get_uint16 (c, &option_code);
00633         link -> imsg_count += 2;
00634         
00635         if (link -> imsg_count + 2 > link -> imsg_len) {
00636                 log_error ("FAILOVER: message overflow at length.");
00637                 return DHCP_R_PROTOCOLERROR;
00638         }
00639 
00640         /* Get option length. */
00641         omapi_connection_get_uint16 (c, &option_len);
00642         link -> imsg_count += 2;
00643 
00644         if (link -> imsg_count + option_len > link -> imsg_len) {
00645                 log_error ("FAILOVER: message overflow at data.");
00646                 return DHCP_R_PROTOCOLERROR;
00647         }
00648 
00649         /* If it's an unknown code, skip over it. */
00650         if ((option_code > FTO_MAX) ||
00651             (ft_options[option_code].type == FT_UNDEF)) {
00652 #if defined (DEBUG_FAILOVER_MESSAGES)
00653                 log_debug ("  option code %d (%s) len %d (not recognized)",
00654                            option_code,
00655                            dhcp_failover_option_name (option_code),
00656                            option_len);
00657 #endif
00658                 omapi_connection_copyout ((unsigned char *)0, c, option_len);
00659                 link -> imsg_count += option_len;
00660                 return ISC_R_SUCCESS;
00661         }
00662 
00663         /* If it's the digest, do it now. */
00664         if (ft_options [option_code].type == FT_DIGEST) {
00665                 link -> imsg_count += option_len;
00666                 if (link -> imsg_count != link -> imsg_len) {
00667                         log_error ("FAILOVER: digest not at end of message");
00668                         return DHCP_R_PROTOCOLERROR;
00669                 }
00670 #if defined (DEBUG_FAILOVER_MESSAGES)
00671                 log_debug ("  option %s len %d",
00672                            ft_options [option_code].name, option_len);
00673 #endif
00674                 /* For now, just dump it. */
00675                 omapi_connection_copyout ((unsigned char *)0, c, option_len);
00676                 return ISC_R_SUCCESS;
00677         }
00678         
00679         /* Only accept an option once. */
00680         if (link -> imsg -> options_present & ft_options [option_code].bit) {
00681                 log_error ("FAILOVER: duplicate option %s",
00682                            ft_options [option_code].name);
00683                 return DHCP_R_PROTOCOLERROR;
00684         }
00685 
00686         /* Make sure the option is appropriate for this type of message.
00687            Really, any option is generally allowed for any message, and the
00688            cases where this is not true are too complicated to represent in
00689            this way - what this code is doing is to just avoid saving the
00690            value of an option we don't have any way to use, which allows
00691            us to make the failover_message structure smaller. */
00692         if (ft_options [option_code].bit &&
00693             !(fto_allowed [link -> imsg -> type] &
00694               ft_options [option_code].bit)) {
00695                 omapi_connection_copyout ((unsigned char *)0, c, option_len);
00696                 link -> imsg_count += option_len;
00697                 return ISC_R_SUCCESS;
00698         }               
00699 
00700         /* Figure out how many elements, how big they are, and where
00701            to store them. */
00702         if (ft_options [option_code].num_present) {
00703                 /* If this option takes a fixed number of elements,
00704                    we expect the space for them to be preallocated,
00705                    and we can just read the data in. */
00706 
00707                 op = ((unsigned char *)link -> imsg) +
00708                         ft_options [option_code].offset;
00709                 op_size = ft_sizes [ft_options [option_code].type];
00710                 op_count = ft_options [option_code].num_present;
00711 
00712                 if (option_len != op_size * op_count) {
00713                         log_error ("FAILOVER: option size (%d:%d), option %s",
00714                                    option_len,
00715                                    (ft_sizes [ft_options [option_code].type] *
00716                                     ft_options [option_code].num_present),
00717                                    ft_options [option_code].name);
00718                         return DHCP_R_PROTOCOLERROR;
00719                 }
00720         } else {
00721                 failover_option_t *fo;
00722 
00723                 /* FT_DDNS* are special - one or two bytes of status
00724                    followed by the client FQDN. */
00725                 if (ft_options [option_code].type == FT_DDNS1 ||
00726                     ft_options [option_code].type == FT_DDNS1) {
00727                         ddns_fqdn_t *ddns =
00728                                 ((ddns_fqdn_t *)
00729                                  (((char *)link -> imsg) +
00730                                   ft_options [option_code].offset));
00731 
00732                         op_count = (ft_options [option_code].type == FT_DDNS1
00733                                     ? 1 : 2);
00734 
00735                         omapi_connection_copyout (&ddns -> codes [0],
00736                                                   c, op_count);
00737                         link -> imsg_count += op_count;
00738                         if (op_count == 1)
00739                                 ddns -> codes [1] = 0;
00740                         op_size = 1;
00741                         op_count = option_len - op_count;
00742 
00743                         ddns -> length = op_count;
00744                         ddns -> data = dmalloc (op_count, MDL);
00745                         if (!ddns -> data) {
00746                                 log_error ("FAILOVER: no memory getting%s(%d)",
00747                                            " DNS data ", op_count);
00748 
00749                                 /* Actually, NO_MEMORY, but if we lose here
00750                                    we have to drop the connection. */
00751                                 return DHCP_R_PROTOCOLERROR;
00752                         }
00753                         omapi_connection_copyout (ddns -> data, c, op_count);
00754                         goto out;
00755                 }
00756 
00757                 /* A zero for num_present means that any number of
00758                    elements can appear, so we have to figure out how
00759                    many we got from the length of the option, and then
00760                    fill out a failover_option structure describing the
00761                    data. */
00762                 op_size = ft_sizes [ft_options [option_code].type];
00763 
00764                 /* Make sure that option data length is a multiple of the
00765                    size of the data type being sent. */
00766                 if (op_size > 1 && option_len % op_size) {
00767                         log_error ("FAILOVER: option_len %d not %s%d",
00768                                    option_len, "multiple of ", op_size);
00769                         return DHCP_R_PROTOCOLERROR;
00770                 }
00771 
00772                 op_count = option_len / op_size;
00773                 
00774                 fo = ((failover_option_t *)
00775                       (((char *)link -> imsg) +
00776                        ft_options [option_code].offset));
00777 
00778                 fo -> count = op_count;
00779                 fo -> data = dmalloc (option_len, MDL);
00780                 if (!fo -> data) {
00781                         log_error ("FAILOVER: no memory getting %s (%d)",
00782                                    "option data", op_count);
00783 
00784                         return DHCP_R_PROTOCOLERROR;
00785                 }                       
00786                 op = fo -> data;
00787         }
00788 
00789         /* For single-byte message values and multi-byte values that
00790            don't need swapping, just read them in all at once. */
00791         if (op_size == 1 || ft_options [option_code].type == FT_IPADDR) {
00792                 omapi_connection_copyout ((unsigned char *)op, c, option_len);
00793                 link -> imsg_count += option_len;
00794 
00795                 /*
00796                  * As of 3.1.0, many option codes were changed to conform to
00797                  * draft revision 12 (which alphabetized, then renumbered all
00798                  * the option codes without preserving the version option code
00799                  * nor bumping its value).  As it turns out, the message codes
00800                  * for CONNECT and CONNECTACK turn out the same, so it tries
00801                  * its darndest to connect, and falls short (when TLS_REQUEST
00802                  * comes up size 2 rather than size 1 as draft revision 12 also
00803                  * mandates).
00804                  *
00805                  * The VENDOR_CLASS code in 3.0.x was 11, which is now the HBA
00806                  * code.  Both work out to be arbitrarily long text-or-byte
00807                  * strings, so they pass parsing.
00808                  *
00809                  * Note that it is possible (or intentional), if highly
00810                  * improbable, for the HBA bit array to exactly match
00811                  * isc-V3.0.x.  Warning here is not an issue; if it really is
00812                  * 3.0.x, there will be a protocol error later on.  If it isn't
00813                  * actually 3.0.x, then I guess the lucky user will have to
00814                  * live with a weird warning.
00815                  */
00816                 if ((option_code == 11) && (option_len > 9) &&
00817                     (strncmp((const char *)op, "isc-V3.0.", 9) == 0)) {
00818                         log_error("WARNING: failover as of versions 3.1.0 and "
00819                                   "on are not reverse compatible with "
00820                                   "versions 3.0.x.");
00821                 }
00822 
00823                 goto out;
00824         }
00825 
00826         /* For values that require swapping, read them in one at a time
00827            using routines that swap bytes. */
00828         for (i = 0; i < op_count; i++) {
00829                 switch (ft_options [option_code].type) {
00830                       case FT_UINT32:
00831                         omapi_connection_get_uint32 (c, (u_int32_t *)op);
00832                         op += 4;
00833                         link -> imsg_count += 4;
00834                         break;
00835                         
00836                       case FT_UINT16:
00837                         omapi_connection_get_uint16 (c, (u_int16_t *)op);
00838                         op += 2;
00839                         link -> imsg_count += 2;
00840                         break;
00841                         
00842                       default:
00843                         /* Everything else should have been handled
00844                            already. */
00845                         log_error ("FAILOVER: option %s: bad type %d",
00846                                    ft_options [option_code].name,
00847                                    ft_options [option_code].type);
00848                         return DHCP_R_PROTOCOLERROR;
00849                 }
00850         }
00851       out:
00852         /* Remember that we got this option. */
00853         link -> imsg -> options_present |= ft_options [option_code].bit;
00854         return ISC_R_SUCCESS;
00855 }
00856 
00857 isc_result_t dhcp_failover_link_set_value (omapi_object_t *h,
00858                                            omapi_object_t *id,
00859                                            omapi_data_string_t *name,
00860                                            omapi_typed_data_t *value)
00861 {
00862         if (h -> type != omapi_type_protocol)
00863                 return DHCP_R_INVALIDARG;
00864 
00865         /* Never valid to set these. */
00866         if (!omapi_ds_strcmp (name, "link-port") ||
00867             !omapi_ds_strcmp (name, "link-name") ||
00868             !omapi_ds_strcmp (name, "link-state"))
00869                 return ISC_R_NOPERM;
00870 
00871         if (h -> inner && h -> inner -> type -> set_value)
00872                 return (*(h -> inner -> type -> set_value))
00873                         (h -> inner, id, name, value);
00874         return ISC_R_NOTFOUND;
00875 }
00876 
00877 isc_result_t dhcp_failover_link_get_value (omapi_object_t *h,
00878                                            omapi_object_t *id,
00879                                            omapi_data_string_t *name,
00880                                            omapi_value_t **value)
00881 {
00882         dhcp_failover_link_t *link;
00883 
00884         if (h -> type != omapi_type_protocol)
00885                 return DHCP_R_INVALIDARG;
00886         link = (dhcp_failover_link_t *)h;
00887         
00888         if (!omapi_ds_strcmp (name, "link-port")) {
00889                 return omapi_make_int_value (value, name,
00890                                              (int)link -> peer_port, MDL);
00891         } else if (!omapi_ds_strcmp (name, "link-state")) {
00892                 if (link -> state >= dhcp_flink_state_max)
00893                         return omapi_make_string_value (value, name,
00894                                                         "invalid link state",
00895                                                         MDL);
00896                 return omapi_make_string_value
00897                         (value, name,
00898                          dhcp_flink_state_names [link -> state], MDL);
00899         }
00900 
00901         if (h -> inner && h -> inner -> type -> get_value)
00902                 return (*(h -> inner -> type -> get_value))
00903                         (h -> inner, id, name, value);
00904         return ISC_R_NOTFOUND;
00905 }
00906 
00907 isc_result_t dhcp_failover_link_destroy (omapi_object_t *h,
00908                                          const char *file, int line)
00909 {
00910         dhcp_failover_link_t *link;
00911         if (h -> type != dhcp_type_failover_link)
00912                 return DHCP_R_INVALIDARG;
00913         link = (dhcp_failover_link_t *)h;
00914 
00915         if (link -> peer_address)
00916                 option_cache_dereference (&link -> peer_address, file, line);
00917         if (link -> imsg)
00918                 failover_message_dereference (&link -> imsg, file, line);
00919         if (link -> state_object)
00920                 dhcp_failover_state_dereference (&link -> state_object,
00921                                                  file, line);
00922         return ISC_R_SUCCESS;
00923 }
00924 
00925 /* Write all the published values associated with the object through the
00926    specified connection. */
00927 
00928 isc_result_t dhcp_failover_link_stuff_values (omapi_object_t *c,
00929                                               omapi_object_t *id,
00930                                               omapi_object_t *l)
00931 {
00932         dhcp_failover_link_t *link;
00933         isc_result_t status;
00934 
00935         if (l -> type != dhcp_type_failover_link)
00936                 return DHCP_R_INVALIDARG;
00937         link = (dhcp_failover_link_t *)l;
00938         
00939         status = omapi_connection_put_name (c, "link-port");
00940         if (status != ISC_R_SUCCESS)
00941                 return status;
00942         status = omapi_connection_put_uint32 (c, sizeof (int));
00943         if (status != ISC_R_SUCCESS)
00944                 return status;
00945         status = omapi_connection_put_uint32 (c, link -> peer_port);
00946         if (status != ISC_R_SUCCESS)
00947                 return status;
00948         
00949         status = omapi_connection_put_name (c, "link-state");
00950         if (status != ISC_R_SUCCESS)
00951                 return status;
00952         if (link -> state >= dhcp_flink_state_max)
00953                 status = omapi_connection_put_string (c, "invalid link state");
00954         else
00955                 status = (omapi_connection_put_string
00956                           (c, dhcp_flink_state_names [link -> state]));
00957         if (status != ISC_R_SUCCESS)
00958                 return status;
00959 
00960         if (link -> inner && link -> inner -> type -> stuff_values)
00961                 return (*(link -> inner -> type -> stuff_values)) (c, id,
00962                                                                 link -> inner);
00963         return ISC_R_SUCCESS;
00964 }
00965 
00966 /* Set up a listener for the omapi protocol.    The handle stored points to
00967    a listener object, not a protocol object. */
00968 
00969 isc_result_t dhcp_failover_listen (omapi_object_t *h)
00970 {
00971         isc_result_t status;
00972         dhcp_failover_listener_t *obj, *l;
00973         omapi_value_t *value = (omapi_value_t *)0;
00974         omapi_addr_t local_addr;
00975         unsigned long port;
00976 
00977         status = omapi_get_value_str (h, (omapi_object_t *)0,
00978                                       "local-port", &value);
00979         if (status != ISC_R_SUCCESS)
00980                 return status;
00981         if (!value -> value) {
00982                 omapi_value_dereference (&value, MDL);
00983                 return DHCP_R_INVALIDARG;
00984         }
00985         
00986         status = omapi_get_int_value (&port, value -> value);
00987         omapi_value_dereference (&value, MDL);
00988         if (status != ISC_R_SUCCESS)
00989                 return status;
00990         local_addr.port = port;
00991 
00992         status = omapi_get_value_str (h, (omapi_object_t *)0,
00993                                       "local-address", &value);
00994         if (status != ISC_R_SUCCESS)
00995                 return status;
00996         if (!value -> value) {
00997               nogood:
00998                 omapi_value_dereference (&value, MDL);
00999                 return DHCP_R_INVALIDARG;
01000         }
01001         
01002         if (value -> value -> type != omapi_datatype_data ||
01003             value -> value -> u.buffer.len != sizeof (struct in_addr))
01004                 goto nogood;
01005 
01006         memcpy (local_addr.address, value -> value -> u.buffer.value,
01007                 value -> value -> u.buffer.len);
01008         local_addr.addrlen = value -> value -> u.buffer.len;
01009         local_addr.addrtype = AF_INET;
01010 
01011         omapi_value_dereference (&value, MDL);
01012 
01013         /* Are we already listening on this port and address? */
01014         for (l = failover_listeners; l; l = l -> next) {
01015                 if (l -> address.port == local_addr.port &&
01016                     l -> address.addrtype == local_addr.addrtype &&
01017                     l -> address.addrlen == local_addr.addrlen &&
01018                     !memcmp (l -> address.address, local_addr.address,
01019                              local_addr.addrlen))
01020                         break;
01021         }
01022         /* Already listening. */
01023         if (l)
01024                 return ISC_R_SUCCESS;
01025 
01026         obj = (dhcp_failover_listener_t *)0;
01027         status = dhcp_failover_listener_allocate (&obj, MDL);
01028         if (status != ISC_R_SUCCESS)
01029                 return status;
01030         obj -> address = local_addr;
01031         
01032         status = omapi_listen_addr ((omapi_object_t *)obj, &obj -> address, 1);
01033         if (status != ISC_R_SUCCESS)
01034                 return status;
01035 
01036         status = omapi_object_reference (&h -> outer,
01037                                          (omapi_object_t *)obj, MDL);
01038         if (status != ISC_R_SUCCESS) {
01039                 dhcp_failover_listener_dereference (&obj, MDL);
01040                 return status;
01041         }
01042         status = omapi_object_reference (&obj -> inner, h, MDL);
01043         if (status != ISC_R_SUCCESS) {
01044                 dhcp_failover_listener_dereference (&obj, MDL);
01045                 return status;
01046         }
01047 
01048         /* Put this listener on the list. */
01049         if (failover_listeners) {
01050                 dhcp_failover_listener_reference (&obj -> next,
01051                                                   failover_listeners, MDL);
01052                 dhcp_failover_listener_dereference (&failover_listeners, MDL);
01053         }
01054         dhcp_failover_listener_reference (&failover_listeners, obj, MDL);
01055 
01056         return dhcp_failover_listener_dereference (&obj, MDL);
01057 }
01058 
01059 /* Signal handler for protocol listener - if we get a connect signal,
01060    create a new protocol connection, otherwise pass the signal down. */
01061 
01062 isc_result_t dhcp_failover_listener_signal (omapi_object_t *o,
01063                                             const char *name, va_list ap)
01064 {
01065         isc_result_t status;
01066         omapi_connection_object_t *c;
01067         dhcp_failover_link_t *obj;
01068         dhcp_failover_listener_t *p;
01069         dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
01070 
01071         if (!o || o -> type != dhcp_type_failover_listener)
01072                 return DHCP_R_INVALIDARG;
01073         p = (dhcp_failover_listener_t *)o;
01074 
01075         /* Not a signal we recognize? */
01076         if (strcmp (name, "connect")) {
01077                 if (p -> inner && p -> inner -> type -> signal_handler)
01078                         return (*(p -> inner -> type -> signal_handler))
01079                                 (p -> inner, name, ap);
01080                 return ISC_R_NOTFOUND;
01081         }
01082 
01083         c = va_arg (ap, omapi_connection_object_t *);
01084         if (!c || c -> type != omapi_type_connection)
01085                 return DHCP_R_INVALIDARG;
01086 
01087         /* See if we can find a failover_state object that
01088            matches this connection. */
01089         for (s = failover_states; s; s = s -> next) {
01090                 if (dhcp_failover_state_match
01091                     (s, (u_int8_t *)&c -> remote_addr.sin_addr,
01092                     sizeof c -> remote_addr.sin_addr)) {
01093                         state = s;
01094                         break;
01095                 }
01096         }               
01097         if (!state) {
01098                 log_info ("failover: listener: no matching state");
01099                 omapi_disconnect ((omapi_object_t *)c, 1);
01100                 return(ISC_R_NOTFOUND);
01101         }
01102 
01103         obj = (dhcp_failover_link_t *)0;
01104         status = dhcp_failover_link_allocate (&obj, MDL);
01105         if (status != ISC_R_SUCCESS)
01106                 return status;
01107         obj -> peer_port = ntohs (c -> remote_addr.sin_port);
01108 
01109         status = omapi_object_reference (&obj -> outer,
01110                                          (omapi_object_t *)c, MDL);
01111         if (status != ISC_R_SUCCESS) {
01112               lose:
01113                 dhcp_failover_link_dereference (&obj, MDL);
01114                 log_info ("failover: listener: picayune failure.");
01115                 omapi_disconnect ((omapi_object_t *)c, 1);
01116                 return status;
01117         }
01118 
01119         status = omapi_object_reference (&c -> inner,
01120                                          (omapi_object_t *)obj, MDL);
01121         if (status != ISC_R_SUCCESS)
01122                 goto lose;
01123 
01124         status = dhcp_failover_state_reference (&obj -> state_object,
01125                                                 state, MDL);
01126         if (status != ISC_R_SUCCESS)
01127                 goto lose;
01128 
01129         omapi_signal_in ((omapi_object_t *)obj, "connect");
01130 
01131         return dhcp_failover_link_dereference (&obj, MDL);
01132 }
01133 
01134 isc_result_t dhcp_failover_listener_set_value (omapi_object_t *h,
01135                                                 omapi_object_t *id,
01136                                                 omapi_data_string_t *name,
01137                                                 omapi_typed_data_t *value)
01138 {
01139         if (h -> type != dhcp_type_failover_listener)
01140                 return DHCP_R_INVALIDARG;
01141         
01142         if (h -> inner && h -> inner -> type -> set_value)
01143                 return (*(h -> inner -> type -> set_value))
01144                         (h -> inner, id, name, value);
01145         return ISC_R_NOTFOUND;
01146 }
01147 
01148 isc_result_t dhcp_failover_listener_get_value (omapi_object_t *h,
01149                                                 omapi_object_t *id,
01150                                                 omapi_data_string_t *name,
01151                                                 omapi_value_t **value)
01152 {
01153         if (h -> type != dhcp_type_failover_listener)
01154                 return DHCP_R_INVALIDARG;
01155         
01156         if (h -> inner && h -> inner -> type -> get_value)
01157                 return (*(h -> inner -> type -> get_value))
01158                         (h -> inner, id, name, value);
01159         return ISC_R_NOTFOUND;
01160 }
01161 
01162 isc_result_t dhcp_failover_listener_destroy (omapi_object_t *h,
01163                                               const char *file, int line)
01164 {
01165         dhcp_failover_listener_t *l;
01166 
01167         if (h -> type != dhcp_type_failover_listener)
01168                 return DHCP_R_INVALIDARG;
01169         l = (dhcp_failover_listener_t *)h;
01170         if (l -> next)
01171                 dhcp_failover_listener_dereference (&l -> next, file, line);
01172 
01173         return ISC_R_SUCCESS;
01174 }
01175 
01176 /* Write all the published values associated with the object through the
01177    specified connection. */
01178 
01179 isc_result_t dhcp_failover_listener_stuff (omapi_object_t *c,
01180                                            omapi_object_t *id,
01181                                            omapi_object_t *p)
01182 {
01183         if (p -> type != dhcp_type_failover_listener)
01184                 return DHCP_R_INVALIDARG;
01185 
01186         if (p -> inner && p -> inner -> type -> stuff_values)
01187                 return (*(p -> inner -> type -> stuff_values)) (c, id,
01188                                                                 p -> inner);
01189         return ISC_R_SUCCESS;
01190 }
01191 
01192 /* Set up master state machine for the failover protocol. */
01193 
01194 isc_result_t dhcp_failover_register (omapi_object_t *h)
01195 {
01196         isc_result_t status;
01197         dhcp_failover_state_t *obj;
01198         unsigned long port;
01199         omapi_value_t *value = (omapi_value_t *)0;
01200 
01201         status = omapi_get_value_str (h, (omapi_object_t *)0,
01202                                       "local-port", &value);
01203         if (status != ISC_R_SUCCESS)
01204                 return status;
01205         if (!value -> value) {
01206                 omapi_value_dereference (&value, MDL);
01207                 return DHCP_R_INVALIDARG;
01208         }
01209         
01210         status = omapi_get_int_value (&port, value -> value);
01211         omapi_value_dereference (&value, MDL);
01212         if (status != ISC_R_SUCCESS)
01213                 return status;
01214 
01215         obj = (dhcp_failover_state_t *)0;
01216         dhcp_failover_state_allocate (&obj, MDL);
01217         obj -> me.port = port;
01218         
01219         status = omapi_listen ((omapi_object_t *)obj, port, 1);
01220         if (status != ISC_R_SUCCESS) {
01221                 dhcp_failover_state_dereference (&obj, MDL);
01222                 return status;
01223         }
01224 
01225         status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj,
01226                                          MDL);
01227         if (status != ISC_R_SUCCESS) {
01228                 dhcp_failover_state_dereference (&obj, MDL);
01229                 return status;
01230         }
01231         status = omapi_object_reference (&obj -> inner, h, MDL);
01232         dhcp_failover_state_dereference (&obj, MDL);
01233         return status;
01234 }
01235 
01236 /* Signal handler for protocol state machine. */
01237 
01238 isc_result_t dhcp_failover_state_signal (omapi_object_t *o,
01239                                          const char *name, va_list ap)
01240 {
01241         isc_result_t status;
01242         dhcp_failover_state_t *state;
01243         dhcp_failover_link_t *link;
01244         struct timeval tv;
01245 
01246         if (!o || o -> type != dhcp_type_failover_state)
01247                 return DHCP_R_INVALIDARG;
01248         state = (dhcp_failover_state_t *)o;
01249 
01250         /* Not a signal we recognize? */
01251         if (strcmp (name, "disconnect") &&
01252             strcmp (name, "message")) {
01253                 if (state -> inner && state -> inner -> type -> signal_handler)
01254                         return (*(state -> inner -> type -> signal_handler))
01255                                 (state -> inner, name, ap);
01256                 return ISC_R_NOTFOUND;
01257         }
01258 
01259         /* Handle connect signals by seeing what state we're in
01260            and potentially doing a state transition. */
01261         if (!strcmp (name, "disconnect")) {
01262                 link = va_arg (ap, dhcp_failover_link_t *);
01263 
01264                 dhcp_failover_link_dereference (&state -> link_to_peer, MDL);
01265                 dhcp_failover_state_transition (state, "disconnect");
01266                 if (state -> i_am == primary) {
01267 #if defined (DEBUG_FAILOVER_TIMING)
01268                         log_info ("add_timeout +90 %s",
01269                                   "dhcp_failover_reconnect");
01270 #endif
01271                         tv . tv_sec = cur_time + 90;
01272                         tv . tv_usec = 0;
01273                         add_timeout (&tv, dhcp_failover_reconnect,
01274                                      state,
01275                                      (tvref_t)dhcp_failover_state_reference,
01276                                      (tvunref_t)
01277                                      dhcp_failover_state_dereference);
01278                 }
01279         } else if (!strcmp (name, "message")) {
01280                 link = va_arg (ap, dhcp_failover_link_t *);
01281 
01282                 if (link -> imsg -> type == FTM_CONNECT) {
01283                         /* If we already have a link to the peer, it must be
01284                            dead, so drop it.
01285                            XXX Is this the right thing to do?
01286                            XXX Probably not - what if both peers start at
01287                            XXX the same time? */
01288                         if (state -> link_to_peer) {
01289                                 dhcp_failover_send_connectack
01290                                         ((omapi_object_t *)link, state,
01291                                          FTR_DUP_CONNECTION,
01292                                          "already connected");
01293                                 omapi_disconnect (link -> outer, 1);
01294                                 return ISC_R_SUCCESS;
01295                         }
01296                         if (!(link -> imsg -> options_present & FTB_MCLT)) {
01297                                 dhcp_failover_send_connectack
01298                                         ((omapi_object_t *)link, state,
01299                                          FTR_INVALID_MCLT,
01300                                          "no MCLT provided");
01301                                 omapi_disconnect (link -> outer, 1);
01302                                 return ISC_R_SUCCESS;
01303                         }                               
01304 
01305                         dhcp_failover_link_reference (&state -> link_to_peer,
01306                                                       link, MDL);
01307                         status = (dhcp_failover_send_connectack
01308                                   ((omapi_object_t *)link, state, 0, 0));
01309                         if (status != ISC_R_SUCCESS) {
01310                                 dhcp_failover_link_dereference
01311                                         (&state -> link_to_peer, MDL);
01312                                 log_info ("dhcp_failover_send_connectack: %s",
01313                                           isc_result_totext (status));
01314                                 omapi_disconnect (link -> outer, 1);
01315                                 return ISC_R_SUCCESS;
01316                         }
01317                         if (link -> imsg -> options_present & FTB_MAX_UNACKED)
01318                                 state -> partner.max_flying_updates =
01319                                         link -> imsg -> max_unacked;
01320                         if (link -> imsg -> options_present & FTB_RECEIVE_TIMER)
01321                                 state -> partner.max_response_delay =
01322                                         link -> imsg -> receive_timer;
01323                         state -> mclt = link -> imsg -> mclt;
01324                         dhcp_failover_send_state (state);
01325                         cancel_timeout (dhcp_failover_link_startup_timeout,
01326                                         link);
01327                 } else if (link -> imsg -> type == FTM_CONNECTACK) {
01328                     const char *errmsg;
01329                     char errbuf[1024];
01330                     int reason;
01331 
01332                     cancel_timeout (dhcp_failover_link_startup_timeout,
01333                                     link);
01334 
01335                     if (!(link->imsg->options_present &
01336                           FTB_RELATIONSHIP_NAME)) {
01337                         errmsg = "missing relationship-name";
01338                         reason = FTR_INVALID_PARTNER;
01339                         goto badconnectack;
01340                     }
01341 
01342                     if (link->imsg->options_present & FTB_REJECT_REASON) {
01343                         /* XXX: add message option to text output. */
01344                         log_error ("Failover CONNECT to %s rejected: %s",
01345                                    state ? state->name : "unknown",
01346                                    (dhcp_failover_reject_reason_print
01347                                     (link -> imsg -> reject_reason)));
01348                         /* XXX print message from peer if peer sent message. */
01349                         omapi_disconnect (link -> outer, 1);
01350                         return ISC_R_SUCCESS;
01351                     }
01352 
01353                     if (!dhcp_failover_state_match_by_name(state,
01354                         &link->imsg->relationship_name)) {
01355                         /* XXX: Overflow results in log truncation, safe. */
01356                         snprintf(errbuf, sizeof(errbuf), "remote failover "
01357                                  "relationship name %.*s does not match",
01358                                  (int)link->imsg->relationship_name.count,
01359                                  link->imsg->relationship_name.data);
01360                         errmsg = errbuf;
01361                         reason = FTR_INVALID_PARTNER;
01362                       badconnectack:
01363                         log_error("Failover CONNECTACK from %s: %s",
01364                                   state->name, errmsg);
01365                         dhcp_failover_send_disconnect ((omapi_object_t *)link,
01366                                                        reason, errmsg);
01367                         omapi_disconnect (link -> outer, 0);
01368                         return ISC_R_SUCCESS;
01369                     }
01370 
01371                     if (state -> link_to_peer) {
01372                         errmsg = "already connected";
01373                         reason = FTR_DUP_CONNECTION;
01374                         goto badconnectack;
01375                     }
01376 
01377                     if ((cur_time > link -> imsg -> time &&
01378                          cur_time - link -> imsg -> time > 60) ||
01379                         (cur_time < link -> imsg -> time &&
01380                          link -> imsg -> time - cur_time > 60)) {
01381                             errmsg = "time offset too large";
01382                             reason = FTR_TIMEMISMATCH;
01383                             goto badconnectack;
01384                     }
01385 
01386                     dhcp_failover_link_reference (&state -> link_to_peer,
01387                                                   link, MDL);
01388 #if 0
01389                     /* XXX This is probably the right thing to do, but
01390                        XXX for release three, to make the smallest possible
01391                        XXX change, we are doing this when the peer state
01392                        XXX changes instead. */
01393                     if (state -> me.state == startup)
01394                             dhcp_failover_set_state (state,
01395                                                      state -> saved_state);
01396                     else
01397 #endif
01398                             dhcp_failover_send_state (state);
01399 
01400                     if (link -> imsg -> options_present & FTB_MAX_UNACKED)
01401                             state -> partner.max_flying_updates =
01402                                     link -> imsg -> max_unacked;
01403                     if (link -> imsg -> options_present & FTB_RECEIVE_TIMER)
01404                             state -> partner.max_response_delay =
01405                                     link -> imsg -> receive_timer;
01406 #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
01407                     log_info ("add_timeout +%d %s",
01408                               (int)state -> partner.max_response_delay / 3,
01409                               "dhcp_failover_send_contact");
01410 #endif
01411                     tv . tv_sec = cur_time +
01412                             (int)state -> partner.max_response_delay / 3;
01413                     tv . tv_usec = 0;
01414                     add_timeout (&tv,
01415                                  dhcp_failover_send_contact, state,
01416                                  (tvref_t)dhcp_failover_state_reference,
01417                                  (tvunref_t)dhcp_failover_state_dereference);
01418 #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
01419                     log_info ("add_timeout +%d %s",
01420                               (int)state -> me.max_response_delay,
01421                               "dhcp_failover_timeout");
01422 #endif
01423                     tv . tv_sec = cur_time +
01424                             (int)state -> me.max_response_delay;
01425                     tv . tv_usec = 0;
01426                     add_timeout (&tv,
01427                                  dhcp_failover_timeout, state,
01428                                  (tvref_t)dhcp_failover_state_reference,
01429                                  (tvunref_t)dhcp_failover_state_dereference);
01430                 } else if (link -> imsg -> type == FTM_DISCONNECT) {
01431                     if (link -> imsg -> reject_reason) {
01432                         log_error ("Failover DISCONNECT from %s: %s",
01433                                    state ? state->name : "unknown",
01434                                    (dhcp_failover_reject_reason_print
01435                                     (link -> imsg -> reject_reason)));
01436                     }
01437                     omapi_disconnect (link -> outer, 1);
01438                 } else if (link -> imsg -> type == FTM_BNDUPD) {
01439                         dhcp_failover_process_bind_update (state,
01440                                                            link -> imsg);
01441                 } else if (link -> imsg -> type == FTM_BNDACK) {
01442                         dhcp_failover_process_bind_ack (state, link -> imsg);
01443                 } else if (link -> imsg -> type == FTM_UPDREQ) {
01444                         dhcp_failover_process_update_request (state,
01445                                                               link -> imsg);
01446                 } else if (link -> imsg -> type == FTM_UPDREQALL) {
01447                         dhcp_failover_process_update_request_all
01448                                 (state, link -> imsg);
01449                 } else if (link -> imsg -> type == FTM_UPDDONE) {
01450                         dhcp_failover_process_update_done (state,
01451                                                            link -> imsg);
01452                 } else if (link -> imsg -> type == FTM_POOLREQ) {
01453                         dhcp_failover_pool_reqbalance(state);
01454                 } else if (link -> imsg -> type == FTM_POOLRESP) {
01455                         log_info ("pool response: %ld leases",
01456                                   (unsigned long)
01457                                   link -> imsg -> addresses_transferred);
01458                 } else if (link -> imsg -> type == FTM_STATE) {
01459                         dhcp_failover_peer_state_changed (state,
01460                                                           link -> imsg);
01461                 }
01462 
01463                 /* Add a timeout so that if the partner doesn't send
01464                    another message for the maximum transmit idle time
01465                    plus a grace of one second, we close the
01466                    connection. */
01467                 if (state -> link_to_peer &&
01468                     state -> link_to_peer == link &&
01469                     state -> link_to_peer -> state != dhcp_flink_disconnected)
01470                 {
01471 #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
01472                     log_info ("add_timeout +%d %s",
01473                               (int)state -> me.max_response_delay,
01474                               "dhcp_failover_timeout");
01475 #endif
01476                     tv . tv_sec = cur_time +
01477                             (int)state -> me.max_response_delay;
01478                     tv . tv_usec = 0;
01479                     add_timeout (&tv,
01480                                  dhcp_failover_timeout, state,
01481                                  (tvref_t)dhcp_failover_state_reference,
01482                                  (tvunref_t)dhcp_failover_state_dereference);
01483 
01484                 }
01485         }
01486 
01487         /* Handle all the events we care about... */
01488         return ISC_R_SUCCESS;
01489 }
01490 
01491 isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *state,
01492                                              const char *name)
01493 {
01494         isc_result_t status;
01495 
01496         /* XXX Check these state transitions against the spec! */
01497         if (!strcmp (name, "disconnect")) {
01498                 if (state -> link_to_peer) {
01499                     log_info ("peer %s: disconnected", state -> name);
01500                     if (state -> link_to_peer -> state_object)
01501                         dhcp_failover_state_dereference
01502                                 (&state -> link_to_peer -> state_object, MDL);
01503                     dhcp_failover_link_dereference (&state -> link_to_peer,
01504                                                     MDL);
01505                 }
01506                 cancel_timeout (dhcp_failover_send_contact, state);
01507                 cancel_timeout (dhcp_failover_timeout, state);
01508                 cancel_timeout (dhcp_failover_startup_timeout, state);
01509 
01510                 switch (state -> me.state == startup ?
01511                         state -> saved_state : state -> me.state) {
01512                       /* In these situations, we remain in the current
01513                        * state, or if in startup enter those states.
01514                        */
01515                       case communications_interrupted:
01516                       case conflict_done:
01517                       case partner_down:
01518                       case paused:
01519                       case recover:
01520                       case recover_done:
01521                       case recover_wait:
01522                       case resolution_interrupted:
01523                       case shut_down:
01524                         /* Already in the right state? */
01525                         if (state -> me.state == startup)
01526                                 return (dhcp_failover_set_state
01527                                         (state, state -> saved_state));
01528                         return ISC_R_SUCCESS;
01529                 
01530                       case potential_conflict:
01531                         return dhcp_failover_set_state
01532                                 (state, resolution_interrupted);
01533                                 
01534                       case normal:
01535                         return dhcp_failover_set_state
01536                                 (state, communications_interrupted);
01537 
01538                       case unknown_state:
01539                         return dhcp_failover_set_state
01540                                 (state, resolution_interrupted);
01541 
01542                       default:
01543                         log_fatal("Impossible case at %s:%d.", MDL);
01544                         break;  /* can't happen. */
01545                 }
01546         } else if (!strcmp (name, "connect")) {
01547                 switch (state -> me.state) {
01548                       case communications_interrupted:
01549                         status = dhcp_failover_set_state (state, normal);
01550                         dhcp_failover_send_updates (state);
01551                         return status;
01552 
01553                       case resolution_interrupted:
01554                         return dhcp_failover_set_state (state,
01555                                                         potential_conflict);
01556 
01557                       case conflict_done:
01558                       case partner_down:
01559                       case potential_conflict:
01560                       case normal:
01561                       case recover:
01562                       case shut_down:
01563                       case paused:
01564                       case unknown_state:
01565                       case recover_done:
01566                       case startup:
01567                       case recover_wait:
01568                         return dhcp_failover_send_state (state);
01569 
01570                       default:
01571                         log_fatal("Impossible case at %s:%d.", MDL);
01572                         break;
01573                 }
01574         } else if (!strcmp (name, "startup")) {
01575                 dhcp_failover_set_state (state, startup);
01576                 return ISC_R_SUCCESS;
01577         } else if (!strcmp (name, "connect-timeout")) {
01578                 switch (state -> me.state) {
01579                       case communications_interrupted:
01580                       case partner_down:
01581                       case resolution_interrupted:
01582                       case paused:
01583                       case startup:
01584                       case shut_down:
01585                       case conflict_done:
01586                         return ISC_R_SUCCESS;
01587 
01588                       case normal:
01589                       case recover:
01590                       case recover_wait:
01591                       case recover_done:
01592                       case unknown_state:
01593                         return dhcp_failover_set_state
01594                                 (state, communications_interrupted);
01595 
01596                       case potential_conflict:
01597                         return dhcp_failover_set_state
01598                                 (state, resolution_interrupted);
01599 
01600                       default:
01601                         log_fatal("Impossible case at %s:%d.", MDL);
01602                         break;
01603                 }
01604         }
01605         return DHCP_R_INVALIDARG;
01606 }
01607 
01608 isc_result_t dhcp_failover_set_service_state (dhcp_failover_state_t *state)
01609 {
01610         switch (state -> me.state) {
01611               case unknown_state:
01612                 state -> service_state = not_responding;
01613                 state -> nrr = " (my state unknown)";
01614                 break;
01615 
01616               case partner_down:
01617                 state -> service_state = service_partner_down;
01618                 state -> nrr = "";
01619                 break;
01620 
01621               case normal:
01622                 state -> service_state = cooperating;
01623                 state -> nrr = "";
01624                 break;
01625 
01626               case communications_interrupted:
01627                 state -> service_state = not_cooperating;
01628                 state -> nrr = "";
01629                 break;
01630 
01631               case resolution_interrupted:
01632               case potential_conflict:
01633               case conflict_done:
01634                 state -> service_state = not_responding;
01635                 state -> nrr = " (resolving conflicts)";
01636                 break;
01637 
01638               case recover:
01639                 state -> service_state = not_responding;
01640                 state -> nrr = " (recovering)";
01641                 break;
01642 
01643               case shut_down:
01644                 state -> service_state = not_responding;
01645                 state -> nrr = " (shut down)";
01646                 break;
01647 
01648               case paused:
01649                 state -> service_state = not_responding;
01650                 state -> nrr = " (paused)";
01651                 break;
01652 
01653               case recover_wait:
01654                 state -> service_state = not_responding;
01655                 state -> nrr = " (recover wait)";
01656                 break;
01657 
01658               case recover_done:
01659                 state -> service_state = not_responding;
01660                 state -> nrr = " (recover done)";
01661                 break;
01662 
01663               case startup:
01664                 state -> service_state = service_startup;
01665                 state -> nrr = " (startup)";
01666                 break;
01667 
01668               default:
01669                 log_fatal("Impossible case at %s:%d.\n", MDL);
01670                 break;
01671         }
01672 
01673         /* Some peer states can require us not to respond, even if our
01674            state doesn't. */
01675         /* XXX hm.   I suspect this isn't true anymore. */
01676         if (state -> service_state != not_responding) {
01677                 switch (state -> partner.state) {
01678                       case partner_down:
01679                         state -> service_state = not_responding;
01680                         state -> nrr = " (peer demands: recovering)";
01681                         break;
01682 
01683                       case potential_conflict:
01684                       case conflict_done:
01685                       case resolution_interrupted:
01686                         state -> service_state = not_responding;
01687                         state -> nrr = " (peer demands: resolving conflicts)";
01688                         break;
01689 
01690                         /* Other peer states don't affect our behaviour. */
01691                       default:
01692                         break;
01693                 }
01694         }
01695 
01696         return ISC_R_SUCCESS;
01697 }
01698 
01699 isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *state,
01700                                       enum failover_state new_state)
01701 {
01702     enum failover_state saved_state;
01703     TIME saved_stos;
01704     struct pool *p;
01705     struct shared_network *s;
01706     struct lease *l;
01707     struct timeval tv;
01708 
01709     TRACE(DHCPD_FAILOVER_SET_STATE_START(state->me.state, new_state));
01710 
01711     /* If we're in certain states where we're sending updates, and the peer
01712      * state changes, we need to re-schedule any pending updates just to
01713      * be on the safe side.  This results in retransmission.
01714      */
01715     switch (state -> me.state) {
01716       case normal:
01717       case potential_conflict:
01718       case partner_down:
01719         if (state -> ack_queue_tail) {
01720             struct lease *lp;
01721                 
01722             /* Zap the flags. */
01723             for (lp = state -> ack_queue_head; lp; lp = lp -> next_pending)
01724                     lp -> flags = ((lp -> flags & ~ON_ACK_QUEUE) |
01725                                    ON_UPDATE_QUEUE);
01726 
01727             /* Now hook the ack queue to the beginning of the update
01728                queue. */
01729             if (state -> update_queue_head) {
01730                 lease_reference (&state -> ack_queue_tail -> next_pending,
01731                                  state -> update_queue_head, MDL);
01732                 lease_dereference (&state -> update_queue_head, MDL);
01733             }
01734             lease_reference (&state -> update_queue_head,
01735                              state -> ack_queue_head, MDL);
01736             if (!state -> update_queue_tail) {
01737 #if defined (POINTER_DEBUG)
01738                 if (state -> ack_queue_tail -> next_pending) {
01739                     log_error ("next pending on ack queue tail.");
01740                     abort ();
01741                 }
01742 #endif
01743                 lease_reference (&state -> update_queue_tail,
01744                                  state -> ack_queue_tail, MDL);
01745             }
01746             lease_dereference (&state -> ack_queue_tail, MDL);
01747             lease_dereference (&state -> ack_queue_head, MDL);
01748             state -> cur_unacked_updates = 0;
01749         }
01750         /* We will re-queue a timeout later, if applicable. */
01751         cancel_timeout (dhcp_failover_keepalive, state);
01752         break;
01753         
01754       default:
01755         break;
01756     }
01757 
01758     /* Tentatively make the transition. */
01759     saved_state = state -> me.state;
01760     saved_stos = state -> me.stos;
01761 
01762     /* Keep the old stos if we're going into recover_wait or if we're
01763        coming into or out of startup. */
01764     if (new_state != recover_wait && new_state != startup &&
01765         saved_state != startup)
01766             state -> me.stos = cur_time;
01767 
01768     /* If we're in shutdown, peer is in partner_down, and we're moving
01769        to recover, we can skip waiting for MCLT to expire.    This happens
01770        when a server is moved administratively into shutdown prior to
01771        actually shutting down.   Of course, if there are any updates
01772        pending we can't actually do this. */
01773     if (new_state == recover && saved_state == shut_down &&
01774         state -> partner.state == partner_down &&
01775         !state -> update_queue_head && !state -> ack_queue_head)
01776             state -> me.stos = cur_time - state -> mclt;
01777 
01778     state -> me.state = new_state;
01779     if (new_state == startup && saved_state != startup)
01780         state -> saved_state = saved_state;
01781 
01782     /* If we can't record the new state, we can't make a state transition. */
01783     if (!write_failover_state (state) || !commit_leases ()) {
01784             log_error ("Unable to record current failover state for %s",
01785                        state -> name);
01786             state -> me.state = saved_state;
01787             state -> me.stos = saved_stos;
01788             return ISC_R_IOERROR;
01789     }
01790 
01791     log_info ("failover peer %s: I move from %s to %s",
01792               state -> name, dhcp_failover_state_name_print (saved_state),
01793               dhcp_failover_state_name_print (state -> me.state));
01794 
01795     /* If both servers are now normal log it */
01796     if ((state->me.state == normal) && (state->partner.state == normal))
01797             log_info("failover peer %s: Both servers normal", state->name);
01798     
01799     /* If we were in startup and we just left it, cancel the timeout. */
01800     if (new_state != startup && saved_state == startup)
01801         cancel_timeout (dhcp_failover_startup_timeout, state);
01802 
01803     /*
01804      * If the state changes for any reason, cancel 'delayed auto state
01805      * changes' (currently there is just the one).
01806      */
01807     cancel_timeout(dhcp_failover_auto_partner_down, state);
01808 
01809     /* Set our service state. */
01810     dhcp_failover_set_service_state (state);
01811 
01812     /* Tell the peer about it. */
01813     if (state -> link_to_peer)
01814             dhcp_failover_send_state (state);
01815 
01816     switch (new_state) {
01817           case communications_interrupted:
01818             /*
01819              * There is an optional feature to automatically enter partner
01820              * down after a timer expires, upon entering comms-interrupted.
01821              * This feature is generally not safe except in specific
01822              * circumstances.
01823              *
01824              * A zero value (also the default) disables it.
01825              */
01826             if (state->auto_partner_down == 0)
01827                 break;
01828 
01829 #if defined (DEBUG_FAILOVER_TIMING)
01830             log_info("add_timeout +%lu dhcp_failover_auto_partner_down",
01831                       (unsigned long)state->auto_partner_down);
01832 #endif
01833             tv.tv_sec = cur_time + state->auto_partner_down;
01834             tv.tv_usec = 0;
01835             add_timeout(&tv, dhcp_failover_auto_partner_down, state,
01836                         (tvref_t)omapi_object_reference,
01837                         (tvunref_t)omapi_object_dereference);
01838             break;
01839 
01840           case normal:
01841             /* Upon entering normal state, the server is expected to retransmit
01842              * all pending binding updates.  This is a good opportunity to
01843              * rebalance the pool (potentially making new pending updates),
01844              * which also schedules the next pool rebalance.
01845              */
01846             dhcp_failover_pool_balance(state);
01847             dhcp_failover_generate_update_queue(state, 0);
01848 
01849             if (state->update_queue_tail != NULL) {
01850                 dhcp_failover_send_updates(state);
01851                 log_info("Sending updates to %s.", state->name);
01852             }
01853 
01854             break;
01855 
01856           case potential_conflict:
01857             if (state -> i_am == primary)
01858                     dhcp_failover_send_update_request (state);
01859             break;
01860 
01861           case startup:
01862 #if defined (DEBUG_FAILOVER_TIMING)
01863             log_info ("add_timeout +15 %s",
01864                       "dhcp_failover_startup_timeout");
01865 #endif
01866             tv . tv_sec = cur_time + 15;
01867             tv . tv_usec = 0;
01868             add_timeout (&tv,
01869                          dhcp_failover_startup_timeout,
01870                          state,
01871                          (tvref_t)omapi_object_reference,
01872                          (tvunref_t)
01873                          omapi_object_dereference);
01874             break;
01875             
01876             /* If we come back in recover_wait and there's still waiting
01877                to do, set a timeout. */
01878           case recover_wait:
01879             if (state -> me.stos + state -> mclt > cur_time) {
01880 #if defined (DEBUG_FAILOVER_TIMING)
01881                     log_info ("add_timeout +%d %s",
01882                               (int)(cur_time -
01883                                     state -> me.stos + state -> mclt),
01884                               "dhcp_failover_startup_timeout");
01885 #endif
01886                     tv . tv_sec = (int)(state -> me.stos + state -> mclt);
01887                     tv . tv_usec = 0;
01888                     add_timeout (&tv,
01889                                  dhcp_failover_recover_done,
01890                                  state,
01891                                  (tvref_t)omapi_object_reference,
01892                                  (tvunref_t)
01893                                  omapi_object_dereference);
01894             } else
01895                     dhcp_failover_recover_done (state);
01896             break;
01897             
01898           case recover:
01899             /* XXX: We're supposed to calculate if updreq or updreqall is
01900              * needed.  In theory, we should only have to updreqall if we
01901              * are positive we lost our stable storage.
01902              */
01903             if (state -> link_to_peer)
01904                     dhcp_failover_send_update_request_all (state);
01905             break;
01906 
01907           case partner_down:
01908             /* For every expired lease, set a timeout for it to become free. */
01909             for (s = shared_networks; s; s = s -> next) {
01910                 for (p = s -> pools; p; p = p -> next) {
01911                     if (p -> failover_peer == state) {
01912                         for (l = p->expired ; l ; l = l->next) {
01913                             l->tsfp = state->me.stos + state->mclt;
01914                             l->sort_time = (l->tsfp > l->ends) ?
01915                                            l->tsfp : l->ends;
01916                         }
01917                         if (p->expired &&
01918                             (p->expired->sort_time < p->next_event_time)) {
01919 
01920                             p->next_event_time = p->expired->sort_time;
01921 #if defined (DEBUG_FAILOVER_TIMING)
01922                             log_info ("add_timeout +%d %s",
01923                                       (int)(cur_time - p->next_event_time),
01924                                       "pool_timer");
01925 #endif
01926                             tv.tv_sec = p->next_event_time;
01927                             tv.tv_usec = 0;
01928                             add_timeout(&tv, pool_timer, p,
01929                                         (tvref_t)pool_reference,
01930                                         (tvunref_t)pool_dereference);
01931                         }
01932                     }
01933                 }
01934             }
01935             break;
01936 
01937 
01938           default:
01939             break;
01940     }
01941 
01942     TRACE(DHCPD_FAILOVER_SET_STATE_DONE());
01943 
01944     return ISC_R_SUCCESS;
01945 }
01946 
01947 isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state,
01948                                                failover_message_t *msg)
01949 {
01950         enum failover_state previous_state = state -> partner.state;
01951         enum failover_state new_state;
01952         int startupp;
01953 
01954         new_state = msg -> server_state;
01955         startupp = (msg -> server_flags & FTF_SERVER_STARTUP) ? 1 : 0;
01956 
01957         if (state -> partner.state == new_state && state -> me.state) {
01958                 switch (state -> me.state) {
01959                       case startup:
01960                         dhcp_failover_set_state (state, state -> saved_state);
01961                         return ISC_R_SUCCESS;
01962 
01963                       case unknown_state:
01964                       case normal:
01965                       case potential_conflict:
01966                       case recover_done:
01967                       case shut_down:
01968                       case paused:
01969                       case recover_wait:
01970                         return ISC_R_SUCCESS;
01971 
01972                         /* If we get a peer state change when we're
01973                            disconnected, we always process it. */
01974                       case partner_down:
01975                       case communications_interrupted:
01976                       case resolution_interrupted:
01977                       case recover:
01978                       case conflict_done:
01979                         break;
01980 
01981                       default:
01982                         log_fatal("Impossible case at %s:%d.", MDL);
01983                         break;
01984                 }
01985         }
01986 
01987         state -> partner.state = new_state;
01988 
01989         log_info ("failover peer %s: peer moves from %s to %s",
01990                   state -> name,
01991                   dhcp_failover_state_name_print (previous_state),
01992                   dhcp_failover_state_name_print (state -> partner.state));
01993 
01994         /* If both servers are now normal log it */
01995         if ((state->me.state == normal) && (state->partner.state == normal))
01996                 log_info("failover peer %s: Both servers normal", state->name);
01997     
01998         if (!write_failover_state (state) || !commit_leases ()) {
01999                 /* This is bad, but it's not fatal.  Of course, if we
02000                    can't write to the lease database, we're not going to
02001                    get much done anyway. */
02002                 log_error ("Unable to record current failover state for %s",
02003                            state -> name);
02004         }
02005 
02006         /* Quickly validate the new state as being one of the 13 known
02007          * states.
02008          */
02009         switch (new_state) {
02010               case unknown_state:
02011               case startup:
02012               case normal:
02013               case communications_interrupted:
02014               case partner_down:
02015               case potential_conflict:
02016               case recover:
02017               case paused:
02018               case shut_down:
02019               case recover_done:
02020               case resolution_interrupted:
02021               case conflict_done:
02022               case recover_wait:
02023                 break;
02024 
02025               default:
02026                 log_error("failover peer %s: Invalid state: %d", state->name,
02027                           new_state);
02028                 dhcp_failover_set_state(state, shut_down);
02029                 return ISC_R_SUCCESS;
02030         }
02031 
02032         /* Do any state transitions that are required as a result of the
02033            peer's state transition. */
02034 
02035         switch (state -> me.state == startup ?
02036                 state -> saved_state : state -> me.state) {
02037               case normal:
02038                 switch (new_state) {
02039                       case normal:
02040                         dhcp_failover_state_pool_check (state);
02041                         break;
02042 
02043                       case partner_down:
02044                         if (state -> me.state == startup)
02045                                 dhcp_failover_set_state (state, recover);
02046                         else
02047                                 dhcp_failover_set_state (state,
02048                                                          potential_conflict);
02049                         break;
02050 
02051                       case potential_conflict:
02052                       case resolution_interrupted:
02053                       case conflict_done:
02054                         /* None of these transitions should ever occur. */
02055                         log_error("Peer %s: Invalid state transition %s "
02056                                 "to %s.", state->name,
02057                                 dhcp_failover_state_name_print(previous_state),
02058                                 dhcp_failover_state_name_print(new_state));
02059                         dhcp_failover_set_state (state, shut_down);
02060                         break;
02061 
02062                       case recover:
02063                       case shut_down:
02064                         dhcp_failover_set_state (state, partner_down);
02065                         break;
02066 
02067                       case paused:
02068                         dhcp_failover_set_state (state,
02069                                                  communications_interrupted);
02070                         break;
02071 
02072                       default:
02073                         /* recover_wait, recover_done, unknown_state, startup,
02074                          * communications_interrupted
02075                          */
02076                         break;
02077                 }
02078                 break;
02079 
02080               case recover:
02081                 switch (new_state) {
02082                       case recover:
02083                         log_info ("failover peer %s: requesting %s",
02084                                   state -> name, "full update from peer");
02085                         /* Don't send updreqall if we're really in the
02086                            startup state, because that will result in two
02087                            being sent. */
02088                         if (state -> me.state == recover)
02089                                 dhcp_failover_send_update_request_all (state);
02090                         break;
02091 
02092                       case potential_conflict:
02093                       case resolution_interrupted:
02094                       case conflict_done:
02095                       case normal:
02096                         dhcp_failover_set_state (state, potential_conflict);
02097                         break;
02098 
02099                       case partner_down:
02100                       case communications_interrupted:
02101                         /* We're supposed to send an update request at this
02102                            point. */
02103                         /* XXX we don't currently have code here to do any
02104                            XXX clever detection of when we should send an
02105                            XXX UPDREQALL message rather than an UPDREQ
02106                            XXX message.   What to do, what to do? */
02107                         /* Currently when we enter recover state, no matter
02108                          * the reason, we send an UPDREQALL.  So, it makes
02109                          * the most sense to stick to that until something
02110                          * better is done.
02111                          * Furthermore, we only want to send the update
02112                          * request if we are not in startup state.
02113                          */
02114                         if (state -> me.state == recover)
02115                                 dhcp_failover_send_update_request_all (state);
02116                         break;
02117 
02118                       case shut_down:
02119                         /* XXX We're not explicitly told what to do in this
02120                            XXX case, but this transition is consistent with
02121                            XXX what is elsewhere in the draft. */
02122                         dhcp_failover_set_state (state, partner_down);
02123                         break;
02124 
02125                         /* We can't really do anything in this case. */
02126                       default:
02127                         /* paused, recover_done, recover_wait, unknown_state,
02128                          * startup.
02129                          */
02130                         break;
02131                 }
02132                 break;
02133 
02134               case potential_conflict:
02135                 switch (new_state) {
02136                       case normal:
02137                         /* This is an illegal transition. */
02138                         log_error("Peer %s moves to normal during conflict "
02139                                   "resolution - panic, shutting down.",
02140                                   state->name);
02141                         dhcp_failover_set_state(state, shut_down);
02142                         break;
02143 
02144                       case conflict_done:
02145                         if (previous_state == potential_conflict)
02146                                 dhcp_failover_send_update_request (state);
02147                         else
02148                                 log_error("Peer %s: Unexpected move to "
02149                                           "conflict-done.", state->name);
02150                         break;
02151 
02152                       case recover_done:
02153                       case recover_wait:
02154                       case potential_conflict:
02155                       case partner_down:
02156                       case communications_interrupted:
02157                       case resolution_interrupted:
02158                       case paused:
02159                         break;
02160 
02161                       case recover:
02162                         dhcp_failover_set_state (state, recover);
02163                         break;
02164 
02165                       case shut_down:
02166                         dhcp_failover_set_state (state, partner_down);
02167                         break;
02168 
02169                       default:
02170                         /* unknown_state, startup */
02171                         break;
02172                 }
02173                 break;
02174 
02175               case conflict_done:
02176                 switch (new_state) {
02177                       case normal:
02178                       case shut_down:
02179                         dhcp_failover_set_state(state, new_state);
02180                         break;
02181 
02182                       default:
02183                         log_fatal("Peer %s: Invalid attempt to move from %s "
02184                                 "to %s while local state is conflict-done.",
02185                                 state->name,
02186                                 dhcp_failover_state_name_print(previous_state),
02187                                 dhcp_failover_state_name_print(new_state));
02188                 }
02189                 break;
02190 
02191               case partner_down:
02192                 /* Take no action if other server is starting up. */
02193                 if (startupp)
02194                         break;
02195 
02196                 switch (new_state) {
02197                         /* This is where we should be. */
02198                       case recover:
02199                       case recover_wait:
02200                         break;
02201 
02202                       case recover_done:
02203                         dhcp_failover_set_state (state, normal);
02204                         break;
02205 
02206                       case normal:
02207                       case potential_conflict:
02208                       case partner_down:
02209                       case communications_interrupted:
02210                       case resolution_interrupted:
02211                       case conflict_done:
02212                         dhcp_failover_set_state (state, potential_conflict);
02213                         break;
02214 
02215                       default:
02216                         /* shut_down, paused, unknown_state, startup */
02217                         break;
02218                 }
02219                 break;
02220 
02221               case communications_interrupted:
02222                 switch (new_state) {
02223                       case paused:
02224                         /* Stick with the status quo. */
02225                         break;
02226 
02227                         /* If we're in communications-interrupted and an
02228                            amnesic peer connects, go to the partner_down
02229                            state immediately. */
02230                       case recover:
02231                         dhcp_failover_set_state (state, partner_down);
02232                         break;
02233 
02234                       case normal:
02235                       case communications_interrupted:
02236                       case recover_done:
02237                       case recover_wait:
02238                         /* XXX so we don't need to do this specially in
02239                            XXX the CONNECT and CONNECTACK handlers. */
02240                         dhcp_failover_send_updates (state);
02241                         dhcp_failover_set_state (state, normal);
02242                         break;
02243 
02244                       case potential_conflict:
02245                       case partner_down:
02246                       case resolution_interrupted:
02247                       case conflict_done:
02248                         dhcp_failover_set_state (state, potential_conflict);
02249                         break;
02250 
02251                       case shut_down:
02252                         dhcp_failover_set_state (state, partner_down);
02253                         break;
02254 
02255                       default:
02256                         /* unknown_state, startup */
02257                         break;
02258                 }
02259                 break;
02260 
02261               case resolution_interrupted:
02262                 switch (new_state) {
02263                       case normal:
02264                       case recover:
02265                       case potential_conflict:
02266                       case partner_down:
02267                       case communications_interrupted:
02268                       case resolution_interrupted:
02269                       case conflict_done:
02270                       case recover_done:
02271                       case recover_wait:
02272                         dhcp_failover_set_state (state, potential_conflict);
02273                         break;
02274 
02275                       case shut_down:
02276                         dhcp_failover_set_state (state, partner_down);
02277                         break;
02278 
02279                       default:
02280                         /* paused, unknown_state, startup */
02281                         break;
02282                 }
02283                 break;
02284 
02285               /* Make no transitions while in recover_wait...just wait. */
02286               case recover_wait:
02287                 break;
02288 
02289               case recover_done:
02290                 switch (new_state) {
02291                       case recover_done:
02292                         log_error("Both servers have entered recover-done!");
02293                       case normal:
02294                         dhcp_failover_set_state (state, normal);
02295                         break;
02296 
02297                       case shut_down:
02298                         dhcp_failover_set_state (state, partner_down);
02299                         break;
02300 
02301                       default:
02302                         /* potential_conflict, partner_down,
02303                          * communications_interrupted, resolution_interrupted,
02304                          * paused, recover, recover_wait, unknown_state,
02305                          * startup.
02306                          */
02307                         break;
02308                 }
02309                 break;
02310 
02311                 /* We are essentially dead in the water when we're in
02312                    either shut_down or paused states, and do not do any
02313                    automatic state transitions. */
02314               case shut_down:
02315               case paused:
02316                 break;
02317 
02318               /* XXX: Shouldn't this be a fatal condition? */
02319               case unknown_state:
02320                 break;
02321 
02322               default:
02323                 log_fatal("Impossible condition at %s:%d.", MDL);
02324                 break;
02325 
02326         }
02327 
02328         /* If we didn't make a transition out of startup as a result of
02329            the peer's state change, do it now as a result of the fact that
02330            we got a state change from the peer. */
02331         if (state -> me.state == startup && state -> saved_state != startup)
02332                 dhcp_failover_set_state (state, state -> saved_state);
02333         
02334         /* For now, just set the service state based on the peer's state
02335            if necessary. */
02336         dhcp_failover_set_service_state (state);
02337 
02338         return ISC_R_SUCCESS;
02339 }
02340 
02341 /*
02342  * Balance operation manual entry; startup, entrance to normal state.  No
02343  * sense sending a POOLREQ at this stage; the peer is likely about to schedule
02344  * their own rebalance event upon entering normal themselves.
02345  */
02346 static void
02347 dhcp_failover_pool_balance(dhcp_failover_state_t *state)
02348 {
02349         /* Cancel pending event. */
02350         cancel_timeout(dhcp_failover_pool_rebalance, state);
02351         state->sched_balance = 0;
02352 
02353         dhcp_failover_pool_dobalance(state, NULL);
02354 }
02355 
02356 /*
02357  * Balance operation entry from timer event.  Once per timer interval is
02358  * the only time we want to emit POOLREQs (asserting an interrupt in our
02359  * peer).
02360  */
02361 void
02362 dhcp_failover_pool_rebalance(void *failover_state)
02363 {
02364         dhcp_failover_state_t *state;
02365         isc_boolean_t sendreq = ISC_FALSE;
02366 
02367         state = (dhcp_failover_state_t *)failover_state;
02368 
02369         /* Clear scheduled event indicator. */
02370         state->sched_balance = 0;
02371 
02372         if (dhcp_failover_pool_dobalance(state, &sendreq))
02373                 dhcp_failover_send_updates(state);
02374 
02375         if (sendreq)
02376                 dhcp_failover_send_poolreq(state);
02377 }
02378 
02379 /*
02380  * Balance operation entry from POOLREQ protocol message.  Do not permit a
02381  * POOLREQ to send back a POOLREQ.  Ping pong.
02382  */
02383 static void
02384 dhcp_failover_pool_reqbalance(dhcp_failover_state_t *state)
02385 {
02386         int queued;
02387 
02388         /* Cancel pending event. */
02389         cancel_timeout(dhcp_failover_pool_rebalance, state);
02390         state->sched_balance = 0;
02391 
02392         queued = dhcp_failover_pool_dobalance(state, NULL);
02393 
02394         dhcp_failover_send_poolresp(state, queued);
02395 
02396         if (queued)
02397                 dhcp_failover_send_updates(state);
02398         else
02399                 log_info("peer %s: Got POOLREQ, answering negatively!  "
02400                          "Peer may be out of leases or database inconsistent.",
02401                          state->name);
02402 }
02403 
02404 /*
02405  * Do the meat of the work common to all forms of pool rebalance.  If the
02406  * caller deems it appropriate to transmit POOLREQ messages, it can use the
02407  * sendreq pointer to pass in the address of a FALSE value which this function
02408  * will conditionally turn TRUE if a POOLREQ is determined to be necessary.
02409  * A NULL value may be passed, in which case no action is taken.
02410  */
02411 static int
02412 dhcp_failover_pool_dobalance(dhcp_failover_state_t *state,
02413                             isc_boolean_t *sendreq)
02414 {
02415         int lts, total, thresh, hold, panic, pass;
02416         int leases_queued = 0;
02417         struct lease *lp = (struct lease *)0;
02418         struct lease *next = (struct lease *)0;
02419         struct shared_network *s;
02420         struct pool *p;
02421         binding_state_t peer_lease_state;
02422         /* binding_state_t my_lease_state; */
02423         /* XXX Why is this my_lease_state never used? */
02424         struct lease **lq;
02425         int (*log_func)(const char *, ...);
02426         const char *result, *reqlog;
02427 
02428         if (state -> me.state != normal)
02429                 return 0;
02430 
02431         TRACE(DHCPD_FAILOVER_POOL_DOBALANCE_START());
02432 
02433         state->last_balance = cur_time;
02434 
02435         for (s = shared_networks ; s ; s = s->next) {
02436             for (p = s->pools ; p ; p = p->next) {
02437                 if (p->failover_peer != state)
02438                     continue;
02439 
02440                 /* Right now we're giving the peer half of the free leases.
02441                    If we have more leases than the peer (i.e., more than
02442                    half), then the number of leases we have, less the number
02443                    of leases the peer has, will be how many more leases we
02444                    have than the peer has.   So if we send half that number
02445                    to the peer, we should be even. */
02446                 if (p->failover_peer->i_am == primary) {
02447                         lts = (p->free_leases - p->backup_leases) / 2;
02448                         peer_lease_state = FTS_BACKUP;
02449                         /* my_lease_state = FTS_FREE; */
02450                         lq = &p->free;
02451                 } else {
02452                         lts = (p->backup_leases - p->free_leases) / 2;
02453                         peer_lease_state = FTS_FREE;
02454                         /* my_lease_state = FTS_BACKUP; */
02455                         lq = &p->backup;
02456                 }
02457 
02458                 total = p->backup_leases + p->free_leases;
02459 
02460                 thresh = ((total * state->max_lease_misbalance) + 50) / 100;
02461                 hold = ((total * state->max_lease_ownership) + 50) / 100;
02462 
02463                 /*
02464                  * If we need leases (so lts is negative) more than negative
02465                  * double the thresh%, panic and send poolreq to hopefully wake
02466                  * up the peer (but more likely the db is inconsistent).  But,
02467                  * if this comes out zero, switch to -1 so that the POOLREQ is
02468                  * sent on lts == -2 rather than right away at -1.
02469                  *
02470                  * Note that we do not subtract -1 from panic all the time
02471                  * because thresh% and hold% may come out to the same number,
02472                  * and that is correct operation...where thresh% and hold% are
02473                  * both -1, we want to send poolreq when lts reaches -3.  So,
02474                  * "-3 < -2", lts < panic.
02475                  */
02476                 panic = thresh * -2;
02477 
02478                 if (panic == 0)
02479                         panic = -1;
02480 
02481                 if ((sendreq != NULL) && (lts < panic)) {
02482                         reqlog = "  (requesting peer rebalance!)";
02483                         *sendreq = ISC_TRUE;
02484                 } else
02485                         reqlog = "";
02486 
02487                 log_info("balancing pool %lx %s  total %d  free %d  "
02488                          "backup %d  lts %d  max-own (+/-)%d%s",
02489                          (unsigned long)p,
02490                          (p->shared_network ?
02491                           p->shared_network->name : ""), p->lease_count,
02492                          p->free_leases, p->backup_leases, lts, hold,
02493                          reqlog);
02494 
02495                 /* In the first pass, try to allocate leases to the
02496                  * peer which it would normally be responsible for (if
02497                  * the lease has a hardware address or client-identifier,
02498                  * and the load-balance-algorithm chooses the peer to
02499                  * answer that address), up to a hold% excess in the peer's
02500                  * favor.  In the second pass, just send the oldest (first
02501                  * on the list) leases up to a hold% excess in our favor.
02502                  *
02503                  * This could make for additional pool rebalance
02504                  * events, but preserving MAC possession should be
02505                  * worth it.
02506                  */
02507                 pass = 0;
02508                 lease_reference(&lp, *lq, MDL);
02509 
02510                 while (lp) {
02511                         if (next)
02512                             lease_dereference(&next, MDL);
02513                         if (lp->next)
02514                             lease_reference(&next, lp->next, MDL);
02515 
02516                         /*
02517                          * Stop if the pool is 'balanced enough.'
02518                          *
02519                          * The pool is balanced enough if:
02520                          *
02521                          * 1) We're on the first run through and the peer has
02522                          *    its fair share of leases already (lts reaches
02523                          *    -hold).
02524                          * 2) We're on the second run through, we are shifting
02525                          *    never-used leases, and there is a perfectly even
02526                          *    balance (lts reaches zero).
02527                          * 3) Second run through, we are shifting previously
02528                          *    used leases, and the local system has its fair
02529                          *    share but no more (lts reaches hold).
02530                          *
02531                          * Note that this is implemented below in 3,2,1 order.
02532                          */
02533                         if (pass) {
02534                                 if (lp->ends) {
02535                                         if (lts <= hold)
02536                                                 break;
02537                                 } else {
02538                                         if (lts <= 0)
02539                                                 break;
02540                                 }
02541                         } else if (lts <= -hold)
02542                                 break;
02543 
02544                         if (pass || peer_wants_lease(lp)) {
02545                             --lts;
02546                             ++leases_queued;
02547                             lp->next_binding_state = peer_lease_state;
02548                             lp->tstp = cur_time;
02549                             lp->starts = cur_time;
02550 
02551                             if (!supersede_lease(lp, NULL, 0, 1, 0) ||
02552                                 !write_lease(lp))
02553                                     log_error("can't commit lease %s on "
02554                                               "giveaway", piaddr(lp->ip_addr));
02555                         }
02556 
02557                         lease_dereference(&lp, MDL);
02558                         if (next)
02559                                 lease_reference(&lp, next, MDL);
02560                         else if (!pass) {
02561                                 pass = 1;
02562                                 lease_reference(&lp, *lq, MDL);
02563                         }
02564                 }
02565 
02566                 if (next)
02567                         lease_dereference(&next, MDL);
02568                 if (lp)
02569                         lease_dereference(&lp, MDL);
02570 
02571                 if (lts > thresh) {
02572                         result = "IMBALANCED";
02573                         log_func = log_error;
02574                 } else {
02575                         result = "balanced";
02576                         log_func = log_info;
02577                 }
02578 
02579                 log_func("%s pool %lx %s  total %d  free %d  backup %d  "
02580                          "lts %d  max-misbal %d", result, (unsigned long)p,
02581                          (p->shared_network ?
02582                           p->shared_network->name : ""), p->lease_count,
02583                          p->free_leases, p->backup_leases, lts, thresh);
02584  
02585                 /* Recalculate next rebalance event timer. */
02586                 dhcp_failover_pool_check(p);
02587             }
02588         }
02589 
02590         if (leases_queued)
02591                 commit_leases();
02592 
02593         TRACE(DHCPD_FAILOVER_POOL_DOBALANCE_DONE());
02594 
02595         return leases_queued;
02596 }
02597 
02598 /* dhcp_failover_pool_check: Called whenever FREE or BACKUP leases change
02599  * states, on both servers.  Check the scheduled time to rebalance the pool
02600  * and lower it if applicable.
02601  */
02602 void
02603 dhcp_failover_pool_check(struct pool *pool)
02604 {
02605         dhcp_failover_state_t *peer;
02606         TIME est1, est2;
02607         struct timeval tv;
02608 
02609         peer = pool->failover_peer;
02610 
02611         if(!peer || peer->me.state != normal)
02612                 return;
02613 
02614         /* Estimate the time left until lease exhaustion.
02615          * The first lease on the backup or free lists is also the oldest
02616          * lease.  It is reasonable to guess that it will take at least
02617          * as much time for a pool to run out of leases, as the present
02618          * age of the oldest lease (seconds since it expired).
02619          *
02620          * Note that this isn't so sane of an assumption if the oldest
02621          * lease is a virgin (ends = 0), we wind up sending this against
02622          * the max_balance bounds check.
02623          */
02624         if(pool->free && pool->free->ends < cur_time)
02625                 est1 = cur_time - pool->free->ends;
02626         else
02627                 est1 = 0;
02628 
02629         if(pool->backup && pool->backup->ends < cur_time)
02630                 est2 = cur_time - pool->backup->ends;
02631         else
02632                 est2 = 0;
02633 
02634         /* We don't want to schedule rebalance for when we think we'll run
02635          * out of leases, we want to schedule the rebalance for when we think
02636          * the disparity will be 'large enough' to warrant action.
02637          */
02638         est1 = ((est1 * peer->max_lease_misbalance) + 50) / 100;
02639         est2 = ((est2 * peer->max_lease_misbalance) + 50) / 100;
02640 
02641         /* Guess when the local system will begin issuing POOLREQ panic
02642          * attacks because "max_lease_misbalance*2" has been exceeded.
02643          */
02644         if(peer->i_am == primary)
02645                 est1 *= 2;
02646         else
02647                 est2 *= 2;
02648 
02649         /* Select the smallest time. */
02650         if(est1 > est2)
02651                 est1 = est2;
02652 
02653         /* Bounded by the maximum configured value. */
02654         if(est1 > peer->max_balance)
02655                 est1 = peer->max_balance;
02656 
02657         /* Project this time into the future. */
02658         est1 += cur_time;
02659 
02660         /* Do not move the time down under the minimum. */
02661         est2 = peer->last_balance + peer->min_balance;
02662         if(peer->last_balance && (est1 < est2))
02663                 est1 = est2;
02664 
02665         /* Introduce a random delay. */
02666         est1 += random() % 5;
02667 
02668         /* Do not move the time forward, or reset to the same time. */
02669         if(peer->sched_balance) {
02670                 if (est1 >= peer->sched_balance)
02671                         return;
02672 
02673                 /* We are about to schedule the time down, cancel the
02674                  * current timeout.
02675                  */
02676                 cancel_timeout(dhcp_failover_pool_rebalance, peer);
02677         }
02678 
02679         /* The time is different, and lower, use it. */
02680         peer->sched_balance = est1;
02681 
02682 #if defined(DEBUG_FAILOVER_TIMING)
02683         log_info("add_timeout +%d dhcp_failover_pool_rebalance",
02684                  (int)(est1 - cur_time));
02685 #endif
02686         tv.tv_sec = est1;
02687         tv.tv_usec = 0;
02688         add_timeout(&tv, dhcp_failover_pool_rebalance, peer,
02689                         (tvref_t)dhcp_failover_state_reference,
02690                         (tvunref_t)dhcp_failover_state_dereference);
02691 }
02692 
02693 int dhcp_failover_state_pool_check (dhcp_failover_state_t *state)
02694 {
02695         struct shared_network *s;
02696         struct pool *p;
02697 
02698         for (s = shared_networks; s; s = s -> next) {
02699                 for (p = s -> pools; p; p = p -> next) {
02700                         if (p -> failover_peer != state)
02701                                 continue;
02702                         dhcp_failover_pool_check (p);
02703                 }
02704         }
02705         return 0;
02706 }
02707 
02708 isc_result_t dhcp_failover_send_updates (dhcp_failover_state_t *state)
02709 {
02710         struct lease *lp = (struct lease *)0;
02711         isc_result_t status;
02712 
02713         /* Can't update peer if we're not talking to it! */
02714         if (!state -> link_to_peer)
02715                 return ISC_R_SUCCESS;
02716 
02717         /* If there are acks pending, transmit them prior to potentially
02718          * sending new updates for the same lease.
02719          */
02720         if (state->toack_queue_head != NULL)
02721                 dhcp_failover_send_acks(state);
02722 
02723         while ((state -> partner.max_flying_updates >
02724                 state -> cur_unacked_updates) && state -> update_queue_head) {
02725                 /* Grab the head of the update queue. */
02726                 lease_reference (&lp, state -> update_queue_head, MDL);
02727 
02728                 /* Send the update to the peer. */
02729                 status = dhcp_failover_send_bind_update (state, lp);
02730                 if (status != ISC_R_SUCCESS) {
02731                         lease_dereference (&lp, MDL);
02732                         return status;
02733                 }
02734                 lp -> flags &= ~ON_UPDATE_QUEUE;
02735 
02736                 /* Take it off the head of the update queue and put the next
02737                    item in the update queue at the head. */
02738                 lease_dereference (&state -> update_queue_head, MDL);
02739                 if (lp -> next_pending) {
02740                         lease_reference (&state -> update_queue_head,
02741                                          lp -> next_pending, MDL);
02742                         lease_dereference (&lp -> next_pending, MDL);
02743                 } else {
02744                         lease_dereference (&state -> update_queue_tail, MDL);
02745                 }
02746 
02747                 if (state -> ack_queue_head) {
02748                         lease_reference
02749                                 (&state -> ack_queue_tail -> next_pending,
02750                                  lp, MDL);
02751                         lease_dereference (&state -> ack_queue_tail, MDL);
02752                 } else {
02753                         lease_reference (&state -> ack_queue_head, lp, MDL);
02754                 }
02755 #if defined (POINTER_DEBUG)
02756                 if (lp -> next_pending) {
02757                         log_error ("ack_queue_tail: lp -> next_pending");
02758                         abort ();
02759                 }
02760 #endif
02761                 lease_reference (&state -> ack_queue_tail, lp, MDL);
02762                 lp -> flags |= ON_ACK_QUEUE;
02763                 lease_dereference (&lp, MDL);
02764 
02765                 /* Count the object as an unacked update. */
02766                 state -> cur_unacked_updates++;
02767         }
02768         return ISC_R_SUCCESS;
02769 }
02770 
02771 /* Queue an update for a lease.   Always returns 1 at this point - it's
02772    not an error for this to be called on a lease for which there's no
02773    failover peer. */
02774 
02775 int dhcp_failover_queue_update (struct lease *lease, int immediate)
02776 {
02777         dhcp_failover_state_t *state;
02778 
02779         if (!lease -> pool ||
02780             !lease -> pool -> failover_peer)
02781                 return 1;
02782 
02783         /* If it's already on the update queue, leave it there. */
02784         if (lease -> flags & ON_UPDATE_QUEUE)
02785                 return 1;
02786 
02787         /* Get the failover state structure for this lease. */
02788         state = lease -> pool -> failover_peer;
02789 
02790         /* If it's on the ack queue, take it off. */
02791         if (lease -> flags & ON_ACK_QUEUE)
02792                 dhcp_failover_ack_queue_remove (state, lease);
02793 
02794         if (state -> update_queue_head) {
02795                 lease_reference (&state -> update_queue_tail -> next_pending,
02796                                  lease, MDL);
02797                 lease_dereference (&state -> update_queue_tail, MDL);
02798         } else {
02799                 lease_reference (&state -> update_queue_head, lease, MDL);
02800         }
02801 #if defined (POINTER_DEBUG)
02802         if (lease -> next_pending) {
02803                 log_error ("next pending on update queue lease.");
02804 #if defined (DEBUG_RC_HISTORY)
02805                 dump_rc_history (lease);
02806 #endif
02807                 abort ();
02808         }
02809 #endif
02810         lease_reference (&state -> update_queue_tail, lease, MDL);
02811         lease -> flags |= ON_UPDATE_QUEUE;
02812         if (immediate)
02813                 dhcp_failover_send_updates (state);
02814         return 1;
02815 }
02816 
02817 int dhcp_failover_send_acks (dhcp_failover_state_t *state)
02818 {
02819         failover_message_t *msg = (failover_message_t *)0;
02820 
02821         /* Must commit all leases prior to acking them. */
02822         if (!commit_leases ())
02823                 return 0;
02824 
02825         while (state -> toack_queue_head) {
02826                 failover_message_reference
02827                         (&msg, state -> toack_queue_head, MDL);
02828                 failover_message_dereference
02829                         (&state -> toack_queue_head, MDL);
02830                 if (msg -> next) {
02831                         failover_message_reference
02832                                 (&state -> toack_queue_head, msg -> next, MDL);
02833                 }
02834 
02835                 dhcp_failover_send_bind_ack (state, msg, 0, (const char *)0);
02836 
02837                 failover_message_dereference (&msg, MDL);
02838         }
02839 
02840         if (state -> toack_queue_tail)
02841                 failover_message_dereference (&state -> toack_queue_tail, MDL);
02842         state -> pending_acks = 0;
02843 
02844         return 1;
02845 }
02846 
02847 void dhcp_failover_toack_queue_timeout (void *vs)
02848 {
02849         dhcp_failover_state_t *state = vs;
02850 
02851 #if defined (DEBUG_FAILOVER_TIMING)
02852         log_info ("dhcp_failover_toack_queue_timeout");
02853 #endif
02854 
02855         dhcp_failover_send_acks (state);
02856 }
02857 
02858 /* Queue an ack for a message.  There is currently no way to queue a
02859    negative ack -- these need to be sent directly. */
02860 
02861 int dhcp_failover_queue_ack (dhcp_failover_state_t *state,
02862                              failover_message_t *msg)
02863 {
02864         struct timeval tv;
02865 
02866         if (state -> toack_queue_head) {
02867                 failover_message_reference
02868                         (&state -> toack_queue_tail -> next, msg, MDL);
02869                 failover_message_dereference (&state -> toack_queue_tail, MDL);
02870         } else {
02871                 failover_message_reference (&state -> toack_queue_head,
02872                                             msg, MDL);
02873         }
02874         failover_message_reference (&state -> toack_queue_tail, msg, MDL);
02875 
02876         state -> pending_acks++;
02877 
02878         /* Flush the toack queue whenever we exceed half the number of
02879            allowed unacked updates. */
02880         if (state -> pending_acks >= state -> partner.max_flying_updates / 2) {
02881                 dhcp_failover_send_acks (state);
02882         }
02883 
02884         /* Schedule a timeout to flush the ack queue. */
02885         if (state -> pending_acks > 0) {
02886 #if defined (DEBUG_FAILOVER_TIMING)
02887                 log_info ("add_timeout +2 %s",
02888                           "dhcp_failover_toack_queue_timeout");
02889 #endif
02890                 tv . tv_sec = cur_time + 2;
02891                 tv . tv_usec = 0;
02892                 add_timeout (&tv,
02893                              dhcp_failover_toack_queue_timeout, state,
02894                              (tvref_t)dhcp_failover_state_reference,
02895                              (tvunref_t)dhcp_failover_state_dereference);
02896         }
02897 
02898         return 1;
02899 }
02900 
02901 void dhcp_failover_ack_queue_remove (dhcp_failover_state_t *state,
02902                                      struct lease *lease)
02903 {
02904         struct lease *lp;
02905 
02906         if (!(lease -> flags & ON_ACK_QUEUE))
02907                 return;
02908 
02909         if (state -> ack_queue_head == lease) {
02910                 lease_dereference (&state -> ack_queue_head, MDL);
02911                 if (lease -> next_pending) {
02912                         lease_reference (&state -> ack_queue_head,
02913                                          lease -> next_pending, MDL);
02914                         lease_dereference (&lease -> next_pending, MDL);
02915                 } else {
02916                         lease_dereference (&state -> ack_queue_tail, MDL);
02917                 }
02918         } else {
02919                 for (lp = state -> ack_queue_head;
02920                      lp && lp -> next_pending != lease;
02921                      lp = lp -> next_pending)
02922                         ;
02923 
02924                 if (!lp)
02925                         return;
02926 
02927                 lease_dereference (&lp -> next_pending, MDL);
02928                 if (lease -> next_pending) {
02929                         lease_reference (&lp -> next_pending,
02930                                          lease -> next_pending, MDL);
02931                         lease_dereference (&lease -> next_pending, MDL);
02932                 } else {
02933                         lease_dereference (&state -> ack_queue_tail, MDL);
02934                         if (lp -> next_pending) {
02935                                 log_error ("state -> ack_queue_tail");
02936                                 abort ();
02937                         }
02938                         lease_reference (&state -> ack_queue_tail, lp, MDL);
02939                 }
02940         }
02941 
02942         lease -> flags &= ~ON_ACK_QUEUE;
02943         /* Multiple acks on one XID is an error and may cause badness. */
02944         lease->last_xid = 0;
02945         /* XXX: this violates draft-failover.  We can't send another
02946          * update just because we forgot about an old one that hasn't
02947          * been acked yet.
02948          */
02949         state -> cur_unacked_updates--;
02950 
02951         /*
02952          * When updating leases as a result of an ack, we defer the commit
02953          * for performance reasons.  When there are no more acks pending,
02954          * do a commit.
02955          */
02956         if (state -> cur_unacked_updates == 0) {
02957                 commit_leases();
02958         }
02959 }
02960 
02961 isc_result_t dhcp_failover_state_set_value (omapi_object_t *h,
02962                                             omapi_object_t *id,
02963                                             omapi_data_string_t *name,
02964                                             omapi_typed_data_t *value)
02965 {
02966         isc_result_t status;
02967 
02968         if (h -> type != dhcp_type_failover_state)
02969                 return DHCP_R_INVALIDARG;
02970 
02971         /* This list of successful returns is completely wrong, but the
02972            fastest way to make dhcpctl do something vaguely sane when
02973            you try to change the local state. */
02974 
02975         if (!omapi_ds_strcmp (name, "name")) {
02976                 return ISC_R_SUCCESS;
02977         } else if (!omapi_ds_strcmp (name, "partner-address")) {
02978                 return ISC_R_SUCCESS;
02979         } else if (!omapi_ds_strcmp (name, "local-address")) {
02980                 return ISC_R_SUCCESS;
02981         } else if (!omapi_ds_strcmp (name, "partner-port")) {
02982                 return ISC_R_SUCCESS;
02983         } else if (!omapi_ds_strcmp (name, "local-port")) {
02984                 return ISC_R_SUCCESS;
02985         } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
02986                 return ISC_R_SUCCESS;
02987         } else if (!omapi_ds_strcmp (name, "mclt")) {
02988                 return ISC_R_SUCCESS;
02989         } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
02990                 return ISC_R_SUCCESS;
02991         } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
02992                 return ISC_R_SUCCESS;
02993         } else if (!omapi_ds_strcmp (name, "partner-state")) {
02994                 return ISC_R_SUCCESS;
02995         } else if (!omapi_ds_strcmp (name, "local-state")) {
02996                 unsigned long l;
02997                 status = omapi_get_int_value (&l, value);
02998                 if (status != ISC_R_SUCCESS)
02999                         return status;
03000                 return dhcp_failover_set_state ((dhcp_failover_state_t *)h, l);
03001         } else if (!omapi_ds_strcmp (name, "partner-stos")) {
03002                 return ISC_R_SUCCESS;
03003         } else if (!omapi_ds_strcmp (name, "local-stos")) {
03004                 return ISC_R_SUCCESS;
03005         } else if (!omapi_ds_strcmp (name, "hierarchy")) {
03006                 return ISC_R_SUCCESS;
03007         } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
03008                 return ISC_R_SUCCESS;
03009         } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
03010                 return ISC_R_SUCCESS;
03011         } else if (!omapi_ds_strcmp (name, "skew")) {
03012                 return ISC_R_SUCCESS;
03013         } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
03014                 return ISC_R_SUCCESS;
03015         } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
03016                 return ISC_R_SUCCESS;
03017         }
03018                 
03019         if (h -> inner && h -> inner -> type -> set_value)
03020                 return (*(h -> inner -> type -> set_value))
03021                         (h -> inner, id, name, value);
03022         return ISC_R_NOTFOUND;
03023 }
03024 
03025 void dhcp_failover_keepalive (void *vs)
03026 {
03027 }
03028 
03029 void dhcp_failover_reconnect (void *vs)
03030 {
03031         dhcp_failover_state_t *state = vs;
03032         isc_result_t status;
03033         struct timeval tv;
03034 
03035 #if defined (DEBUG_FAILOVER_TIMING)
03036         log_info ("dhcp_failover_reconnect");
03037 #endif
03038         /* If we already connected the other way, let the connection
03039            recovery code initiate any retry that may be required. */
03040         if (state -> link_to_peer)
03041                 return;
03042 
03043         status = dhcp_failover_link_initiate ((omapi_object_t *)state);
03044         if (status != ISC_R_SUCCESS && status != DHCP_R_INCOMPLETE) {
03045                 log_info ("failover peer %s: %s", state -> name,
03046                           isc_result_totext (status));
03047 #if defined (DEBUG_FAILOVER_TIMING)
03048                 log_info("add_timeout +90 dhcp_failover_reconnect");
03049 #endif
03050                 tv . tv_sec = cur_time + 90;
03051                 tv . tv_usec = 0;
03052                 add_timeout(&tv, dhcp_failover_reconnect, state,
03053                             (tvref_t)dhcp_failover_state_reference,
03054                             (tvunref_t)dhcp_failover_state_dereference);
03055         }
03056 }
03057 
03058 void dhcp_failover_startup_timeout (void *vs)
03059 {
03060         dhcp_failover_state_t *state = vs;
03061 
03062 #if defined (DEBUG_FAILOVER_TIMING)
03063         log_info ("dhcp_failover_startup_timeout");
03064 #endif
03065 
03066         dhcp_failover_state_transition (state, "disconnect");
03067 }
03068 
03069 void dhcp_failover_link_startup_timeout (void *vl)
03070 {
03071         dhcp_failover_link_t *link = vl;
03072         omapi_object_t *p;
03073 
03074         for (p = (omapi_object_t *)link; p -> inner; p = p -> inner)
03075                 ;
03076         for (; p; p = p -> outer)
03077                 if (p -> type == omapi_type_connection)
03078                         break;
03079         if (p) {
03080                 log_info ("failover: link startup timeout");
03081                 omapi_disconnect (p, 1);
03082         }
03083 }
03084 
03085 void dhcp_failover_listener_restart (void *vs)
03086 {
03087         dhcp_failover_state_t *state = vs;
03088         isc_result_t status;
03089         struct timeval tv;
03090 
03091 #if defined (DEBUG_FAILOVER_TIMING)
03092         log_info ("dhcp_failover_listener_restart");
03093 #endif
03094 
03095         status = dhcp_failover_listen ((omapi_object_t *)state);
03096         if (status != ISC_R_SUCCESS) {
03097                 log_info ("failover peer %s: %s", state -> name,
03098                           isc_result_totext (status));
03099 #if defined (DEBUG_FAILOVER_TIMING)
03100                 log_info ("add_timeout +90 %s",
03101                           "dhcp_failover_listener_restart");
03102 #endif
03103                 tv . tv_sec = cur_time + 90;
03104                 tv . tv_usec = 0;
03105                 add_timeout (&tv,
03106                              dhcp_failover_listener_restart, state,
03107                              (tvref_t)dhcp_failover_state_reference,
03108                              (tvunref_t)dhcp_failover_state_dereference);
03109         }
03110 }
03111 
03112 void
03113 dhcp_failover_auto_partner_down(void *vs)
03114 {
03115         dhcp_failover_state_t *state = vs;
03116 
03117 #if defined (DEBUG_FAILOVER_TIMING)
03118         log_info("dhcp_failover_auto_partner_down");
03119 #endif
03120 
03121         dhcp_failover_set_state(state, partner_down);
03122 }
03123 
03124 isc_result_t dhcp_failover_state_get_value (omapi_object_t *h,
03125                                             omapi_object_t *id,
03126                                             omapi_data_string_t *name,
03127                                             omapi_value_t **value)
03128 {
03129         dhcp_failover_state_t *s;
03130         struct option_cache *oc;
03131         struct data_string ds;
03132         isc_result_t status;
03133 
03134         if (h -> type != dhcp_type_failover_state)
03135                 return DHCP_R_INVALIDARG;
03136         s = (dhcp_failover_state_t *)h;
03137         
03138         if (!omapi_ds_strcmp (name, "name")) {
03139                 if (s -> name)
03140                         return omapi_make_string_value (value,
03141                                                         name, s -> name, MDL);
03142                 return ISC_R_NOTFOUND;
03143         } else if (!omapi_ds_strcmp (name, "partner-address")) {
03144                 oc = s -> partner.address;
03145               getaddr:
03146                 memset (&ds, 0, sizeof ds);
03147                 if (!evaluate_option_cache (&ds, (struct packet *)0,
03148                                             (struct lease *)0,
03149                                             (struct client_state *)0,
03150                                             (struct option_state *)0,
03151                                             (struct option_state *)0,
03152                                             &global_scope, oc, MDL)) {
03153                         return ISC_R_NOTFOUND;
03154                 }
03155                 status = omapi_make_const_value (value,
03156                                                  name, ds.data, ds.len, MDL);
03157                 /* Disgusting kludge: */
03158                 if (oc == s -> me.address && !s -> server_identifier.len)
03159                         data_string_copy (&s -> server_identifier, &ds, MDL);
03160                 data_string_forget (&ds, MDL);
03161                 return status;
03162         } else if (!omapi_ds_strcmp (name, "local-address")) {
03163                 oc = s -> me.address;
03164                 goto getaddr;
03165         } else if (!omapi_ds_strcmp (name, "partner-port")) {
03166                 return omapi_make_int_value (value, name,
03167                                              s -> partner.port, MDL);
03168         } else if (!omapi_ds_strcmp (name, "local-port")) {
03169                 return omapi_make_int_value (value,
03170                                              name, s -> me.port, MDL);
03171         } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
03172                 return omapi_make_uint_value (value, name,
03173                                               s -> me.max_flying_updates,
03174                                               MDL);
03175         } else if (!omapi_ds_strcmp (name, "mclt")) {
03176                 return omapi_make_uint_value (value, name, s -> mclt, MDL);
03177         } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
03178                 return omapi_make_int_value (value, name,
03179                                              s -> load_balance_max_secs, MDL);
03180         } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
03181                 if (s -> hba)
03182                         return omapi_make_const_value (value, name,
03183                                                        s -> hba, 32, MDL);
03184                 return ISC_R_NOTFOUND;
03185         } else if (!omapi_ds_strcmp (name, "partner-state")) {
03186                 return omapi_make_uint_value (value, name,
03187                                              s -> partner.state, MDL);
03188         } else if (!omapi_ds_strcmp (name, "local-state")) {
03189                 return omapi_make_uint_value (value, name,
03190                                               s -> me.state, MDL);
03191         } else if (!omapi_ds_strcmp (name, "partner-stos")) {
03192                 return omapi_make_int_value (value, name,
03193                                              s -> partner.stos, MDL);
03194         } else if (!omapi_ds_strcmp (name, "local-stos")) {
03195                 return omapi_make_int_value (value, name,
03196                                              s -> me.stos, MDL);
03197         } else if (!omapi_ds_strcmp (name, "hierarchy")) {
03198                 return omapi_make_uint_value (value, name, s -> i_am, MDL);
03199         } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
03200                 return omapi_make_int_value (value, name,
03201                                              s -> last_packet_sent, MDL);
03202         } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
03203                 return omapi_make_int_value (value, name,
03204                                              s -> last_timestamp_received,
03205                                              MDL);
03206         } else if (!omapi_ds_strcmp (name, "skew")) {
03207                 return omapi_make_int_value (value, name, s -> skew, MDL);
03208         } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
03209                 return omapi_make_uint_value (value, name,
03210                                              s -> me.max_response_delay,
03211                                               MDL);
03212         } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
03213                 return omapi_make_int_value (value, name,
03214                                              s -> cur_unacked_updates, MDL);
03215         }
03216                 
03217         if (h -> inner && h -> inner -> type -> get_value)
03218                 return (*(h -> inner -> type -> get_value))
03219                         (h -> inner, id, name, value);
03220         return ISC_R_NOTFOUND;
03221 }
03222 
03223 isc_result_t dhcp_failover_state_destroy (omapi_object_t *h,
03224                                               const char *file, int line)
03225 {
03226         dhcp_failover_state_t *s;
03227 
03228         if (h -> type != dhcp_type_failover_state)
03229                 return DHCP_R_INVALIDARG;
03230         s = (dhcp_failover_state_t *)h;
03231 
03232         if (s -> link_to_peer)
03233             dhcp_failover_link_dereference (&s -> link_to_peer, file, line);
03234         if (s -> name) {
03235                 dfree (s -> name, MDL);
03236                 s -> name = (char *)0;
03237         }
03238         if (s -> partner.address)
03239                 option_cache_dereference (&s -> partner.address, file, line);
03240         if (s -> me.address)
03241                 option_cache_dereference (&s -> me.address, file, line);
03242         if (s -> hba) {
03243                 dfree (s -> hba, file, line);
03244                 s -> hba = (u_int8_t *)0;
03245         }
03246         if (s -> update_queue_head)
03247                 lease_dereference (&s -> update_queue_head, file, line);
03248         if (s -> update_queue_tail)
03249                 lease_dereference (&s -> update_queue_tail, file, line);
03250         if (s -> ack_queue_head)
03251                 lease_dereference (&s -> ack_queue_head, file, line);
03252         if (s -> ack_queue_tail)
03253                 lease_dereference (&s -> ack_queue_tail, file, line);
03254         if (s -> send_update_done)
03255                 lease_dereference (&s -> send_update_done, file, line);
03256         if (s -> toack_queue_head)
03257                 failover_message_dereference (&s -> toack_queue_head,
03258                                               file, line);
03259         if (s -> toack_queue_tail)
03260                 failover_message_dereference (&s -> toack_queue_tail,
03261                                               file, line);
03262         return ISC_R_SUCCESS;
03263 }
03264 
03265 /* Write all the published values associated with the object through the
03266    specified connection. */
03267 
03268 isc_result_t dhcp_failover_state_stuff (omapi_object_t *c,
03269                                         omapi_object_t *id,
03270                                         omapi_object_t *h)
03271 {
03272         /* In this function c should be a (omapi_connection_object_t *) */
03273 
03274         dhcp_failover_state_t *s;
03275         isc_result_t status;
03276 
03277         if (c -> type != omapi_type_connection)
03278                 return DHCP_R_INVALIDARG;
03279 
03280         if (h -> type != dhcp_type_failover_state)
03281                 return DHCP_R_INVALIDARG;
03282         s = (dhcp_failover_state_t *)h;
03283         
03284         status = omapi_connection_put_name (c, "name");
03285         if (status != ISC_R_SUCCESS)
03286                 return status;
03287         status = omapi_connection_put_string (c, s -> name);
03288         if (status != ISC_R_SUCCESS)
03289                 return status;
03290 
03291         status = omapi_connection_put_name (c, "partner-address");
03292         if (status != ISC_R_SUCCESS)
03293                 return status;
03294         status = omapi_connection_put_uint32 (c, sizeof s -> partner.address);
03295         if (status != ISC_R_SUCCESS)
03296                 return status;
03297         status = omapi_connection_copyin (c, (u_int8_t *)&s -> partner.address,
03298                                           sizeof s -> partner.address);
03299         if (status != ISC_R_SUCCESS)
03300                 return status;
03301         
03302         status = omapi_connection_put_name (c, "partner-port");
03303         if (status != ISC_R_SUCCESS)
03304                 return status;
03305         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
03306         if (status != ISC_R_SUCCESS)
03307                 return status;
03308         status = omapi_connection_put_uint32 (c, (u_int32_t)s -> partner.port);
03309         if (status != ISC_R_SUCCESS)
03310                 return status;
03311         
03312         status = omapi_connection_put_name (c, "local-address");
03313         if (status != ISC_R_SUCCESS)
03314                 return status;
03315         status = omapi_connection_put_uint32 (c, sizeof s -> me.address);
03316         if (status != ISC_R_SUCCESS)
03317                 return status;
03318         status = omapi_connection_copyin (c, (u_int8_t *)&s -> me.address,
03319                                           sizeof s -> me.address);
03320         if (status != ISC_R_SUCCESS)
03321                 return status;
03322         
03323         status = omapi_connection_put_name (c, "local-port");
03324         if (status != ISC_R_SUCCESS)
03325                 return status;
03326         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
03327         if (status != ISC_R_SUCCESS)
03328                 return status;
03329         status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.port);
03330         if (status != ISC_R_SUCCESS)
03331                 return status;
03332         
03333         status = omapi_connection_put_name (c, "max-outstanding-updates");
03334         if (status != ISC_R_SUCCESS)
03335                 return status;
03336         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
03337         if (status != ISC_R_SUCCESS)
03338                 return status;
03339         status = omapi_connection_put_uint32 (c,
03340                                               s -> me.max_flying_updates);
03341         if (status != ISC_R_SUCCESS)
03342                 return status;
03343 
03344         status = omapi_connection_put_name (c, "mclt");
03345         if (status != ISC_R_SUCCESS)
03346                 return status;
03347         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
03348         if (status != ISC_R_SUCCESS)
03349                 return status;
03350         status = omapi_connection_put_uint32 (c, s -> mclt);
03351         if (status != ISC_R_SUCCESS)
03352                 return status;
03353 
03354         status = omapi_connection_put_name (c, "load-balance-max-secs");
03355         if (status != ISC_R_SUCCESS)
03356                 return status;
03357         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
03358         if (status != ISC_R_SUCCESS)
03359                 return status;
03360         status = (omapi_connection_put_uint32
03361                   (c, (u_int32_t)s -> load_balance_max_secs));
03362         if (status != ISC_R_SUCCESS)
03363                 return status;
03364 
03365         
03366         if (s -> hba) {
03367                 status = omapi_connection_put_name (c, "load-balance-hba");
03368                 if (status != ISC_R_SUCCESS)
03369                         return status;
03370                 status = omapi_connection_put_uint32 (c, 32);
03371                 if (status != ISC_R_SUCCESS)
03372                         return status;
03373                 status = omapi_connection_copyin (c, s -> hba, 32);
03374                 if (status != ISC_R_SUCCESS)
03375                         return status;
03376         }
03377 
03378         status = omapi_connection_put_name (c, "partner-state");
03379         if (status != ISC_R_SUCCESS)
03380                 return status;
03381         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
03382         if (status != ISC_R_SUCCESS)
03383                 return status;
03384         status = omapi_connection_put_uint32 (c, s -> partner.state);
03385         if (status != ISC_R_SUCCESS)
03386                 return status;
03387         
03388         status = omapi_connection_put_name (c, "local-state");
03389         if (status != ISC_R_SUCCESS)
03390                 return status;
03391         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
03392         if (status != ISC_R_SUCCESS)
03393                 return status;
03394         status = omapi_connection_put_uint32 (c, s -> me.state);
03395         if (status != ISC_R_SUCCESS)
03396                 return status;
03397         
03398         status = omapi_connection_put_name (c, "partner-stos");
03399         if (status != ISC_R_SUCCESS)
03400                 return status;
03401         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
03402         if (status != ISC_R_SUCCESS)
03403                 return status;
03404         status = omapi_connection_put_uint32 (c,
03405                                               (u_int32_t)s -> partner.stos);
03406         if (status != ISC_R_SUCCESS)
03407                 return status;
03408 
03409         status = omapi_connection_put_name (c, "local-stos");
03410         if (status != ISC_R_SUCCESS)
03411                 return status;
03412         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
03413         if (status != ISC_R_SUCCESS)
03414                 return status;
03415         status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.stos);
03416         if (status != ISC_R_SUCCESS)
03417                 return status;
03418 
03419         status = omapi_connection_put_name (c, "hierarchy");
03420         if (status != ISC_R_SUCCESS)
03421                 return status;
03422         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
03423         if (status != ISC_R_SUCCESS)
03424                 return status;
03425         status = omapi_connection_put_uint32 (c, s -> i_am);
03426         if (status != ISC_R_SUCCESS)
03427                 return status;
03428 
03429         status = omapi_connection_put_name (c, "last-packet-sent");
03430         if (status != ISC_R_SUCCESS)
03431                 return status;
03432         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
03433         if (status != ISC_R_SUCCESS)
03434                 return status;
03435         status = (omapi_connection_put_uint32
03436                   (c, (u_int32_t)s -> last_packet_sent));
03437         if (status != ISC_R_SUCCESS)
03438                 return status;
03439 
03440         status = omapi_connection_put_name (c, "last-timestamp-received");
03441         if (status != ISC_R_SUCCESS)
03442                 return status;
03443         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
03444         if (status != ISC_R_SUCCESS)
03445                 return status;
03446         status = (omapi_connection_put_uint32
03447                   (c, (u_int32_t)s -> last_timestamp_received));
03448         if (status != ISC_R_SUCCESS)
03449                 return status;
03450 
03451         status = omapi_connection_put_name (c, "skew");
03452         if (status != ISC_R_SUCCESS)
03453                 return status;
03454         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
03455         if (status != ISC_R_SUCCESS)
03456                 return status;
03457         status = omapi_connection_put_uint32 (c, (u_int32_t)s -> skew);
03458         if (status != ISC_R_SUCCESS)
03459                 return status;
03460 
03461         status = omapi_connection_put_name (c, "max-response-delay");
03462         if (status != ISC_R_SUCCESS)
03463                 return status;
03464         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
03465         if (status != ISC_R_SUCCESS)
03466                 return status;
03467         status = (omapi_connection_put_uint32
03468                   (c, (u_int32_t)s -> me.max_response_delay));
03469         if (status != ISC_R_SUCCESS)
03470                 return status;
03471         
03472         status = omapi_connection_put_name (c, "cur-unacked-updates");
03473         if (status != ISC_R_SUCCESS)
03474                 return status;
03475         status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
03476         if (status != ISC_R_SUCCESS)
03477                 return status;
03478         status = (omapi_connection_put_uint32
03479                   (c, (u_int32_t)s -> cur_unacked_updates));
03480         if (status != ISC_R_SUCCESS)
03481                 return status;
03482 
03483         if (h -> inner && h -> inner -> type -> stuff_values)
03484                 return (*(h -> inner -> type -> stuff_values)) (c, id,
03485                                                                 h -> inner);
03486         return ISC_R_SUCCESS;
03487 }
03488 
03489 isc_result_t dhcp_failover_state_lookup (omapi_object_t **sp,
03490                                          omapi_object_t *id,
03491                                          omapi_object_t *ref)
03492 {
03493         omapi_value_t *tv = (omapi_value_t *)0;
03494         isc_result_t status;
03495         dhcp_failover_state_t *s;
03496 
03497         if (!ref)
03498                 return DHCP_R_NOKEYS;
03499 
03500         /* First see if we were sent a handle. */
03501         status = omapi_get_value_str (ref, id, "handle", &tv);
03502         if (status == ISC_R_SUCCESS) {
03503                 status = omapi_handle_td_lookup (sp, tv -> value);
03504 
03505                 omapi_value_dereference (&tv, MDL);
03506                 if (status != ISC_R_SUCCESS)
03507                         return status;
03508 
03509                 /* Don't return the object if the type is wrong. */
03510                 if ((*sp) -> type != dhcp_type_failover_state) {
03511                         omapi_object_dereference (sp, MDL);
03512                         return DHCP_R_INVALIDARG;
03513                 }
03514         }
03515 
03516         /* Look the failover state up by peer name. */
03517         status = omapi_get_value_str (ref, id, "name", &tv);
03518         if (status == ISC_R_SUCCESS) {
03519                 for (s = failover_states; s; s = s -> next) {
03520                         unsigned l = strlen (s -> name);
03521                         if (l == tv -> value -> u.buffer.len &&
03522                             !memcmp (s -> name,
03523                                      tv -> value -> u.buffer.value, l))
03524                                 break;
03525                 }
03526                 omapi_value_dereference (&tv, MDL);
03527 
03528                 /* If we already have a lease, and it's not the same one,
03529                    then the query was invalid. */
03530                 if (*sp && *sp != (omapi_object_t *)s) {
03531                         omapi_object_dereference (sp, MDL);
03532                         return DHCP_R_KEYCONFLICT;
03533                 } else if (!s) {
03534                         if (*sp)
03535                                 omapi_object_dereference (sp, MDL);
03536                         return ISC_R_NOTFOUND;
03537                 } else if (!*sp)
03538                         /* XXX fix so that hash lookup itself creates
03539                            XXX the reference. */
03540                         omapi_object_reference (sp, (omapi_object_t *)s, MDL);
03541         }
03542 
03543         /* If we get to here without finding a lease, no valid key was
03544            specified. */
03545         if (!*sp)
03546                 return DHCP_R_NOKEYS;
03547         return ISC_R_SUCCESS;
03548 }
03549 
03550 isc_result_t dhcp_failover_state_create (omapi_object_t **sp,
03551                                          omapi_object_t *id)
03552 {
03553         return ISC_R_NOTIMPLEMENTED;
03554 }
03555 
03556 isc_result_t dhcp_failover_state_remove (omapi_object_t *sp,
03557                                          omapi_object_t *id)
03558 {
03559         return ISC_R_NOTIMPLEMENTED;
03560 }
03561 
03562 int dhcp_failover_state_match (dhcp_failover_state_t *state,
03563                                u_int8_t *addr, unsigned addrlen)
03564 {
03565         struct data_string ds;
03566         int i;
03567         
03568         memset (&ds, 0, sizeof ds);
03569         if (evaluate_option_cache (&ds, (struct packet *)0,
03570                                    (struct lease *)0,
03571                                    (struct client_state *)0,
03572                                    (struct option_state *)0,
03573                                    (struct option_state *)0,
03574                                    &global_scope,
03575                                    state -> partner.address, MDL)) {
03576                 for (i = 0; i + addrlen - 1 < ds.len; i += addrlen) {
03577                         if (!memcmp (&ds.data [i],
03578                                      addr, addrlen)) {
03579                                 data_string_forget (&ds, MDL);
03580                                 return 1;
03581                         }
03582                 }
03583                 data_string_forget (&ds, MDL);
03584         }
03585         return 0;
03586 }
03587 
03588 int
03589 dhcp_failover_state_match_by_name(state, name)
03590         dhcp_failover_state_t *state;
03591         failover_option_t *name;
03592 {
03593         if ((strlen(state->name) == name->count) &&
03594             (memcmp(state->name, name->data, name->count) == 0))
03595                 return 1;
03596 
03597         return 0;
03598 }
03599 
03600 const char *dhcp_failover_reject_reason_print (int reason)
03601 {
03602     static char resbuf[sizeof("Undefined-255: This reason code is not defined "
03603                               "in the protocol standard.")];
03604 
03605     if ((reason > 0xff) || (reason < 0))
03606         return "Reason code out of range.";
03607 
03608     switch (reason) {
03609       case FTR_ILLEGAL_IP_ADDR:
03610         return "Illegal IP address (not part of any address pool).";
03611 
03612       case FTR_FATAL_CONFLICT:
03613         return "Fatal conflict exists: address in use by other client.";
03614 
03615       case FTR_MISSING_BINDINFO:
03616         return "Missing binding information.";
03617 
03618       case FTR_TIMEMISMATCH:
03619         return "Connection rejected, time mismatch too great.";
03620 
03621       case FTR_INVALID_MCLT:
03622         return "Connection rejected, invalid MCLT.";
03623 
03624       case FTR_MISC_REJECT:
03625         return "Connection rejected, unknown reason.";
03626 
03627       case FTR_DUP_CONNECTION:
03628         return "Connection rejected, duplicate connection.";
03629 
03630       case FTR_INVALID_PARTNER:
03631         return "Connection rejected, invalid failover partner.";
03632 
03633       case FTR_TLS_UNSUPPORTED:
03634         return "TLS not supported.";
03635 
03636       case FTR_TLS_UNCONFIGURED:
03637         return "TLS supported but not configured.";
03638 
03639       case FTR_TLS_REQUIRED:
03640         return "TLS required but not supported by partner.";
03641 
03642       case FTR_DIGEST_UNSUPPORTED:
03643         return "Message digest not supported.";
03644 
03645       case FTR_DIGEST_UNCONFIGURED:
03646         return "Message digest not configured.";
03647 
03648       case FTR_VERSION_MISMATCH:
03649         return "Protocol version mismatch.";
03650 
03651       case FTR_OUTDATED_BIND_INFO:
03652         return "Outdated binding information.";
03653 
03654       case FTR_LESS_CRIT_BIND_INFO:
03655         return "Less critical binding information.";
03656 
03657       case FTR_NO_TRAFFIC:
03658         return "No traffic within sufficient time.";
03659 
03660       case FTR_HBA_CONFLICT:
03661         return "Hash bucket assignment conflict.";
03662 
03663       case FTR_IP_NOT_RESERVED:
03664         return "IP not reserved on this server.";
03665 
03666       case FTR_IP_DIGEST_FAILURE:
03667         return "Message digest failed to compare.";
03668 
03669       case FTR_IP_MISSING_DIGEST:
03670         return "Missing message digest.";
03671 
03672       case FTR_UNKNOWN:
03673         return "Unknown Error.";
03674 
03675       default:
03676         sprintf(resbuf, "Undefined-%d: This reason code is not defined in the "
03677                         "protocol standard.", reason);
03678         return resbuf;
03679     }
03680 }
03681 
03682 const char *dhcp_failover_state_name_print (enum failover_state state)
03683 {
03684         switch (state) {
03685               default:
03686               case unknown_state:
03687                 return "unknown-state";
03688 
03689               case partner_down:
03690                 return "partner-down";
03691 
03692               case normal:
03693                 return "normal";
03694 
03695               case conflict_done:
03696                 return "conflict-done";
03697 
03698               case communications_interrupted:
03699                 return "communications-interrupted";
03700 
03701               case resolution_interrupted:
03702                 return "resolution-interrupted";
03703 
03704               case potential_conflict:
03705                 return "potential-conflict";
03706 
03707               case recover:
03708                 return "recover";
03709 
03710               case recover_done:
03711                 return "recover-done";
03712 
03713               case recover_wait:
03714                 return "recover-wait";
03715 
03716               case shut_down:
03717                 return "shutdown";
03718 
03719               case paused:
03720                 return "paused";
03721 
03722               case startup:
03723                 return "startup";
03724         }
03725 }
03726 
03727 const char *dhcp_failover_message_name (unsigned type)
03728 {
03729         static char messbuf[sizeof("unknown-message-255")];
03730 
03731         if (type > 0xff)
03732                 return "invalid-message";
03733 
03734         switch (type) {
03735               case FTM_POOLREQ:
03736                 return "pool-request";
03737                 
03738               case FTM_POOLRESP:
03739                 return "pool-response";
03740 
03741               case FTM_BNDUPD:
03742                 return "bind-update";
03743 
03744               case FTM_BNDACK:
03745                 return "bind-ack";
03746 
03747               case FTM_CONNECT:
03748                 return "connect";
03749 
03750               case FTM_CONNECTACK:
03751                 return "connect-ack";
03752 
03753               case FTM_UPDREQ:
03754                 return "update-request";
03755 
03756               case FTM_UPDDONE:
03757                 return "update-done";
03758 
03759               case FTM_UPDREQALL:
03760                 return "update-request-all";
03761 
03762               case FTM_STATE:
03763                 return "state";
03764 
03765               case FTM_CONTACT:
03766                 return "contact";
03767 
03768               case FTM_DISCONNECT:
03769                 return "disconnect";
03770 
03771               default:
03772                 sprintf(messbuf, "unknown-message-%u", type);
03773                 return messbuf;
03774         }
03775 }
03776 
03777 const char *dhcp_failover_option_name (unsigned type)
03778 {
03779         static char optbuf[sizeof("unknown-option-65535")];
03780 
03781         if (type > 0xffff)
03782                 return "invalid-option";
03783 
03784         switch (type) {
03785             case FTO_ADDRESSES_TRANSFERRED:
03786                 return "addresses-transferred";
03787 
03788             case FTO_ASSIGNED_IP_ADDRESS:
03789                 return "assigned-ip-address";
03790 
03791             case FTO_BINDING_STATUS:
03792                 return "binding-status";
03793 
03794             case FTO_CLIENT_IDENTIFIER:
03795                 return "client-identifier";
03796 
03797             case FTO_CHADDR:
03798                 return "chaddr";
03799 
03800             case FTO_CLTT:
03801                 return "cltt";
03802 
03803             case FTO_DDNS:
03804                 return "ddns";
03805 
03806             case FTO_DELAYED_SERVICE:
03807                 return "delayed-service";
03808 
03809             case FTO_HBA:
03810                 return "hba";
03811 
03812             case FTO_IP_FLAGS:
03813                 return "ip-flags";
03814 
03815             case FTO_LEASE_EXPIRY:
03816                 return "lease-expiry";
03817 
03818             case FTO_MAX_UNACKED:
03819                 return "max-unacked";
03820 
03821             case FTO_MCLT:
03822                 return "mclt";
03823 
03824             case FTO_MESSAGE:
03825                 return "message";
03826 
03827             case FTO_MESSAGE_DIGEST:
03828                 return "message-digest";
03829 
03830             case FTO_POTENTIAL_EXPIRY:
03831                 return "potential-expiry";
03832 
03833             case FTO_PROTOCOL_VERSION:
03834                 return "protocol-version";
03835 
03836             case FTO_RECEIVE_TIMER:
03837                 return "receive-timer";
03838 
03839             case FTO_REJECT_REASON:
03840                 return "reject-reason";
03841 
03842             case FTO_RELATIONSHIP_NAME:
03843                 return "relationship-name";
03844 
03845             case FTO_REPLY_OPTIONS:
03846                 return "reply-options";
03847 
03848             case FTO_REQUEST_OPTIONS:
03849                 return "request-options";
03850 
03851             case FTO_SERVER_FLAGS:
03852                 return "server-flags";
03853 
03854             case FTO_SERVER_STATE:
03855                 return "server-state";
03856 
03857             case FTO_STOS:
03858                 return "stos";
03859 
03860             case FTO_TLS_REPLY:
03861                 return "tls-reply";
03862 
03863             case FTO_TLS_REQUEST:
03864                 return "tls-request";
03865 
03866             case FTO_VENDOR_CLASS:
03867                 return "vendor-class";
03868 
03869             case FTO_VENDOR_OPTIONS:
03870                 return "vendor-options";
03871 
03872             default:
03873                 sprintf(optbuf, "unknown-option-%u", type);
03874                 return optbuf;
03875         }
03876 }
03877 
03878 failover_option_t *dhcp_failover_option_printf (unsigned code,
03879                                                 char *obuf,
03880                                                 unsigned *obufix,
03881                                                 unsigned obufmax,
03882                                                 const char *fmt, ...)
03883 {
03884         va_list va;
03885         char tbuf [256];
03886 
03887         /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
03888          * It is unclear what the effects of truncation here are, or
03889          * how that condition should be handled.  It seems that this
03890          * function is used for formatting messages in the failover
03891          * command channel.  For now the safest thing is for
03892          * overflow-truncation to cause a fatal log.
03893          */
03894         va_start (va, fmt);
03895         if (vsnprintf (tbuf, sizeof tbuf, fmt, va) >= sizeof tbuf)
03896                 log_fatal ("%s: vsnprintf would truncate",
03897                                 "dhcp_failover_make_option");
03898         va_end (va);
03899 
03900         return dhcp_failover_make_option (code, obuf, obufix, obufmax,
03901                                           strlen (tbuf), tbuf);
03902 }
03903 
03904 failover_option_t *dhcp_failover_make_option (unsigned code,
03905                                               char *obuf, unsigned *obufix,
03906                                               unsigned obufmax, ...)
03907 {
03908         va_list va;
03909         struct failover_option_info *info;
03910         int i;
03911         unsigned size, count;
03912         unsigned val;
03913         u_int8_t *iaddr;
03914         unsigned ilen = 0;
03915         u_int8_t *bval;
03916         char *txt = NULL;
03917 #if defined (DEBUG_FAILOVER_MESSAGES)
03918         char tbuf [256];
03919 #endif
03920 
03921         /* Note that the failover_option structure is used differently on
03922            input than on output - on input, count is an element count, and
03923            on output it's the number of bytes total in the option, including
03924            the option code and option length. */
03925         failover_option_t option, *op;
03926 
03927 
03928         /* Bogus option code? */
03929         if (code < 1 || code > FTO_MAX || ft_options [code].type == FT_UNDEF) {
03930                 return &null_failover_option;
03931         }
03932         info = &ft_options [code];
03933                 
03934         va_start (va, obufmax);
03935 
03936         /* Get the number of elements and the size of the buffer we need
03937            to allocate. */
03938         if (info -> type == FT_DDNS || info -> type == FT_DDNS1) {
03939                 count = info -> type == FT_DDNS ? 1 : 2;
03940                 size = va_arg (va, int) + count;
03941         } else {
03942                 /* Find out how many items in this list. */
03943                 if (info -> num_present)
03944                         count = info -> num_present;
03945                 else
03946                         count = va_arg (va, int);
03947 
03948                 /* Figure out size. */
03949                 switch (info -> type) {
03950                       case FT_UINT8:
03951                       case FT_BYTES:
03952                       case FT_DIGEST:
03953                         size = count;
03954                         break;
03955 
03956                       case FT_TEXT_OR_BYTES:
03957                       case FT_TEXT:
03958                         txt = va_arg (va, char *);
03959                         size = count;
03960                         break;
03961 
03962                       case FT_IPADDR:
03963                         ilen = va_arg (va, unsigned);
03964                         size = count * ilen;
03965                         break;
03966 
03967                       case FT_UINT32:
03968                         size = count * 4;
03969                         break;
03970 
03971                       case FT_UINT16:
03972                         size = count * 2;
03973                         break;
03974 
03975                       default:
03976                         /* shouldn't get here. */
03977                         log_fatal ("bogus type in failover_make_option: %d",
03978                                    info -> type);
03979                         return &null_failover_option;
03980                 }
03981         }
03982         
03983         size += 4;
03984 
03985         /* Allocate a buffer for the option. */
03986         option.count = size;
03987         option.data = dmalloc (option.count, MDL);
03988         if (!option.data) {
03989                 va_end (va);
03990                 return &null_failover_option;
03991         }
03992 
03993         /* Put in the option code and option length. */
03994         putUShort (option.data, code);
03995         putUShort (&option.data [2], size - 4);
03996 
03997 #if defined (DEBUG_FAILOVER_MESSAGES)   
03998         /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
03999          * It is unclear what the effects of truncation here are, or
04000          * how that condition should be handled.  It seems that this
04001          * message may be sent over the failover command channel.
04002          * For now the safest thing is for overflow-truncation to cause
04003          * a fatal log.
04004          */
04005         if (snprintf (tbuf, sizeof tbuf, " (%s<%d>", info -> name,
04006                         option.count) >= sizeof tbuf)
04007                 log_fatal ("dhcp_failover_make_option: tbuf overflow");
04008         failover_print (obuf, obufix, obufmax, tbuf);
04009 #endif
04010 
04011         /* Now put in the data. */
04012         switch (info -> type) {
04013               case FT_UINT8:
04014                 for (i = 0; i < count; i++) {
04015                         val = va_arg (va, unsigned);
04016 #if defined (DEBUG_FAILOVER_MESSAGES)
04017                         /* %Audit% Cannot exceed 24 bytes. %2004.06.17,Safe% */
04018                         sprintf (tbuf, " %d", val);
04019                         failover_print (obuf, obufix, obufmax, tbuf);
04020 #endif
04021                         option.data [i + 4] = val;
04022                 }
04023                 break;
04024 
04025               case FT_IPADDR:
04026                 for (i = 0; i < count; i++) {
04027                         iaddr = va_arg (va, u_int8_t *);
04028                         if (ilen != 4) {
04029                                 dfree (option.data, MDL);
04030                                 log_error ("IP addrlen=%d, should be 4.",
04031                                            ilen);
04032                                 va_end (va);
04033                                 return &null_failover_option;
04034                         }
04035                                 
04036 #if defined (DEBUG_FAILOVER_MESSAGES)
04037                         /*%Audit% Cannot exceed 17 bytes.  %2004.06.17,Safe%*/
04038                         sprintf (tbuf, " %u.%u.%u.%u",
04039                                   iaddr [0], iaddr [1], iaddr [2], iaddr [3]);
04040                         failover_print (obuf, obufix, obufmax, tbuf);
04041 #endif
04042                         memcpy (&option.data [4 + i * ilen], iaddr, ilen);
04043                 }
04044                 break;
04045 
04046               case FT_UINT32:
04047                 for (i = 0; i < count; i++) {
04048                         val = va_arg (va, unsigned);
04049 #if defined (DEBUG_FAILOVER_MESSAGES)
04050                         /*%Audit% Cannot exceed 24 bytes.  %2004.06.17,Safe%*/
04051                         sprintf (tbuf, " %d", val);
04052                         failover_print (obuf, obufix, obufmax, tbuf);
04053 #endif
04054                         putULong (&option.data [4 + i * 4], val);
04055                 }
04056                 break;
04057 
04058               case FT_BYTES:
04059               case FT_DIGEST:
04060                 bval = va_arg (va, u_int8_t *);
04061 #if defined (DEBUG_FAILOVER_MESSAGES)
04062                 for (i = 0; i < count; i++) {
04063                         /* 23 bytes plus nul, safe. */
04064                         sprintf (tbuf, " %d", bval [i]);
04065                         failover_print (obuf, obufix, obufmax, tbuf);
04066                 }
04067 #endif
04068                 memcpy (&option.data [4], bval, count);
04069                 break;
04070 
04071                 /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
04072                    terminated.  Note that the caller should be careful not
04073                    to provide a format and data that amount to more than 256
04074                    bytes of data, since it will cause a fatal error. */
04075               case FT_TEXT_OR_BYTES:
04076               case FT_TEXT:
04077 #if defined (DEBUG_FAILOVER_MESSAGES)
04078                 /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
04079                  * It is unclear what the effects of truncation here are, or
04080                  * how that condition should be handled.  It seems that this
04081                  * function is used for formatting messages in the failover
04082                  * command channel.  For now the safest thing is for
04083                  * overflow-truncation to cause a fatal log.
04084                  */
04085                 if (snprintf (tbuf, sizeof tbuf, "\"%s\"", txt) >= sizeof tbuf)
04086                         log_fatal ("dhcp_failover_make_option: tbuf overflow");
04087                 failover_print (obuf, obufix, obufmax, tbuf);
04088 #endif
04089                 memcpy (&option.data [4], txt, count);
04090                 break;
04091 
04092               case FT_DDNS:
04093               case FT_DDNS1:
04094                 option.data [4] = va_arg (va, unsigned);
04095                 if (count == 2)
04096                         option.data [5] = va_arg (va, unsigned);
04097                 bval = va_arg (va, u_int8_t *);
04098                 memcpy (&option.data [4 + count], bval, size - count - 4);
04099 #if defined (DEBUG_FAILOVER_MESSAGES)
04100                 for (i = 4; i < size; i++) {
04101                         /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
04102                         sprintf (tbuf, " %d", option.data [i]);
04103                         failover_print (obuf, obufix, obufmax, tbuf);
04104                 }
04105 #endif
04106                 break;
04107 
04108               case FT_UINT16:
04109                 for (i = 0; i < count; i++) {
04110                         val = va_arg (va, u_int32_t);
04111 #if defined (DEBUG_FAILOVER_MESSAGES)
04112                         /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
04113                         sprintf (tbuf, " %d", val);
04114                         failover_print (obuf, obufix, obufmax, tbuf);
04115 #endif
04116                         putUShort (&option.data [4 + i * 2], val);
04117                 }
04118                 break;
04119 
04120               case FT_UNDEF:
04121               default:
04122                 break;
04123         }
04124 
04125 #if defined DEBUG_FAILOVER_MESSAGES
04126         failover_print (obuf, obufix, obufmax, ")");
04127 #endif
04128         va_end (va);
04129 
04130         /* Now allocate a place to store what we just set up. */
04131         op = dmalloc (sizeof (failover_option_t), MDL);
04132         if (!op) {
04133                 dfree (option.data, MDL);
04134                 return &null_failover_option;
04135         }
04136 
04137         *op = option;
04138         return op;
04139 }
04140 
04141 /* Send a failover message header. */
04142 
04143 isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link,
04144                                         omapi_object_t *connection,
04145                                         int msg_type, u_int32_t xid, ...)
04146 {
04147         unsigned size = 0;
04148         int bad_option = 0;
04149         int opix = 0;
04150         va_list list;
04151         failover_option_t *option;
04152         unsigned char *opbuf;
04153         isc_result_t status = ISC_R_SUCCESS;
04154         unsigned char cbuf;
04155         struct timeval tv;
04156 
04157         /* Run through the argument list once to compute the length of
04158            the option portion of the message. */
04159         va_start (list, xid);
04160         while ((option = va_arg (list, failover_option_t *))) {
04161                 if (option != &skip_failover_option)
04162                         size += option -> count;
04163                 if (option == &null_failover_option)
04164                         bad_option = 1;
04165         }
04166         va_end (list);
04167 
04168         /* Allocate an option buffer, unless we got an error. */
04169         if (!bad_option && size) {
04170                 opbuf = dmalloc (size, MDL);
04171                 if (!opbuf)
04172                         status = ISC_R_NOMEMORY;
04173         } else
04174                 opbuf = (unsigned char *)0;
04175 
04176         va_start (list, xid);
04177         while ((option = va_arg (list, failover_option_t *))) {
04178                 if (option == &skip_failover_option)
04179                     continue;
04180                 if (!bad_option && opbuf)
04181                         memcpy (&opbuf [opix],
04182                                 option -> data, option -> count);
04183                 if (option != &null_failover_option &&
04184                     option != &skip_failover_option) {
04185                         opix += option -> count;
04186                         dfree (option -> data, MDL);
04187                         dfree (option, MDL);
04188                 }
04189         }
04190         va_end(list);
04191 
04192         if (bad_option)
04193                 return DHCP_R_INVALIDARG;
04194 
04195         /* Now send the message header. */
04196 
04197         /* Message length. */
04198         status = omapi_connection_put_uint16 (connection, size + 12);
04199         if (status != ISC_R_SUCCESS)
04200                 goto err;
04201 
04202         /* Message type. */
04203         cbuf = msg_type;
04204         status = omapi_connection_copyin (connection, &cbuf, 1);
04205         if (status != ISC_R_SUCCESS)
04206                 goto err;
04207 
04208         /* Payload offset. */
04209         cbuf = 12;
04210         status = omapi_connection_copyin (connection, &cbuf, 1);
04211         if (status != ISC_R_SUCCESS)
04212                 goto err;
04213 
04214         /* Current time. */
04215         status = omapi_connection_put_uint32 (connection, (u_int32_t)cur_time);
04216         if (status != ISC_R_SUCCESS)
04217                 goto err;
04218 
04219         /* Transaction ID. */
04220         status = omapi_connection_put_uint32(connection, xid);
04221         if (status != ISC_R_SUCCESS)
04222                 goto err;
04223 
04224         /* Payload. */
04225         if (opbuf) {
04226                 status = omapi_connection_copyin (connection, opbuf, size);
04227                 if (status != ISC_R_SUCCESS)
04228                         goto err;
04229                 dfree (opbuf, MDL);
04230         }
04231         if (link -> state_object &&
04232             link -> state_object -> link_to_peer == link) {
04233 #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
04234                 log_info ("add_timeout +%d %s",
04235                           (int)(link -> state_object ->
04236                                 partner.max_response_delay) / 3,
04237                           "dhcp_failover_send_contact");
04238 #endif
04239                 tv . tv_sec = cur_time +
04240                         (int)(link -> state_object ->
04241                               partner.max_response_delay) / 3;
04242                 tv . tv_usec = 0;
04243                 add_timeout (&tv,
04244                              dhcp_failover_send_contact, link -> state_object,
04245                              (tvref_t)dhcp_failover_state_reference,
04246                              (tvunref_t)dhcp_failover_state_dereference);
04247         }
04248         return status;
04249 
04250       err:
04251         if (opbuf)
04252                 dfree (opbuf, MDL);
04253         log_info ("dhcp_failover_put_message: something went wrong.");
04254         omapi_disconnect (connection, 1);
04255         return status;
04256 }
04257 
04258 void dhcp_failover_timeout (void *vstate)
04259 {
04260         dhcp_failover_state_t *state = vstate;
04261         dhcp_failover_link_t *link;
04262 
04263 #if defined (DEBUG_FAILOVER_TIMING)
04264         log_info ("dhcp_failover_timeout");
04265 #endif
04266 
04267         if (!state || state -> type != dhcp_type_failover_state)
04268                 return;
04269         link = state -> link_to_peer;
04270         if (!link ||
04271             !link -> outer ||
04272             link -> outer -> type != omapi_type_connection)
04273                 return;
04274 
04275         log_error ("timeout waiting for failover peer %s", state -> name);
04276 
04277         /* If we haven't gotten a timely response, blow away the connection.
04278            This will cause the state to change automatically. */
04279         omapi_disconnect (link -> outer, 1);
04280 }
04281 
04282 void dhcp_failover_send_contact (void *vstate)
04283 {
04284         dhcp_failover_state_t *state = vstate;
04285         dhcp_failover_link_t *link;
04286         isc_result_t status;
04287 
04288 #if defined(DEBUG_FAILOVER_MESSAGES) && \
04289     defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
04290         char obuf [64];
04291         unsigned obufix = 0;
04292 
04293         failover_print(obuf, &obufix, sizeof(obuf), "(contact");
04294 #endif
04295 
04296 #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
04297         log_info ("dhcp_failover_send_contact");
04298 #endif
04299 
04300         if (!state || state -> type != dhcp_type_failover_state)
04301                 return;
04302         link = state -> link_to_peer;
04303         if (!link ||
04304             !link -> outer ||
04305             link -> outer -> type != omapi_type_connection)
04306                 return;
04307 
04308         status = (dhcp_failover_put_message
04309                   (link, link -> outer,
04310                    FTM_CONTACT, link->xid++,
04311                    (failover_option_t *)0));
04312 
04313 #if defined(DEBUG_FAILOVER_MESSAGES) && \
04314     defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
04315         if (status != ISC_R_SUCCESS)
04316                 failover_print(obuf, &obufix, sizeof(obuf), " (failed)");
04317         failover_print(obuf, &obufix, sizeof(obuf), ")");
04318         if (obufix) {
04319                 log_debug ("%s", obuf);
04320         }
04321 #else
04322         IGNORE_UNUSED(status);
04323 #endif
04324         return;
04325 }
04326 
04327 isc_result_t dhcp_failover_send_state (dhcp_failover_state_t *state)
04328 {
04329         dhcp_failover_link_t *link;
04330         isc_result_t status;
04331 
04332 #if defined (DEBUG_FAILOVER_MESSAGES)   
04333         char obuf [64];
04334         unsigned obufix = 0;
04335         
04336 # define FMA obuf, &obufix, sizeof obuf
04337         failover_print (FMA, "(state");
04338 #else
04339 # define FMA (char *)0, (unsigned *)0, 0
04340 #endif
04341 
04342         if (!state || state -> type != dhcp_type_failover_state)
04343                 return DHCP_R_INVALIDARG;
04344         link = state -> link_to_peer;
04345         if (!link ||
04346             !link -> outer ||
04347             link -> outer -> type != omapi_type_connection)
04348                 return DHCP_R_INVALIDARG;
04349 
04350         status = (dhcp_failover_put_message
04351                   (link, link -> outer,
04352                    FTM_STATE, link->xid++,
04353                    dhcp_failover_make_option (FTO_SERVER_STATE, FMA,
04354                                               (state -> me.state == startup
04355                                                ? state -> saved_state
04356                                                : state -> me.state)),
04357                    dhcp_failover_make_option
04358                    (FTO_SERVER_FLAGS, FMA,
04359                     (state -> service_state == service_startup
04360                      ? FTF_SERVER_STARTUP : 0)),
04361                    dhcp_failover_make_option (FTO_STOS, FMA, state -> me.stos),
04362                    (failover_option_t *)0));
04363 
04364 #if defined (DEBUG_FAILOVER_MESSAGES)
04365         if (status != ISC_R_SUCCESS)
04366                 failover_print (FMA, " (failed)");
04367         failover_print (FMA, ")");
04368         if (obufix) {
04369                 log_debug ("%s", obuf);
04370         }
04371 #else
04372         IGNORE_UNUSED(status);
04373 #endif
04374         return ISC_R_SUCCESS;
04375 }
04376 
04377 /* Send a connect message. */
04378 
04379 isc_result_t dhcp_failover_send_connect (omapi_object_t *l)
04380 {
04381         dhcp_failover_link_t *link;
04382         dhcp_failover_state_t *state;
04383         isc_result_t status;
04384 #if defined (DEBUG_FAILOVER_MESSAGES)   
04385         char obuf [64];
04386         unsigned obufix = 0;
04387         
04388 # define FMA obuf, &obufix, sizeof obuf
04389         failover_print (FMA, "(connect");
04390 #else
04391 # define FMA (char *)0, (unsigned *)0, 0
04392 #endif
04393 
04394         if (!l || l -> type != dhcp_type_failover_link)
04395                 return DHCP_R_INVALIDARG;
04396         link = (dhcp_failover_link_t *)l;
04397         state = link -> state_object;
04398         if (!l -> outer || l -> outer -> type != omapi_type_connection)
04399                 return DHCP_R_INVALIDARG;
04400 
04401         status =
04402             (dhcp_failover_put_message
04403              (link, l -> outer,
04404               FTM_CONNECT, link->xid++,
04405               dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA,
04406                                         strlen(state->name), state->name),
04407               dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
04408                                          state -> me.max_flying_updates),
04409               dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
04410                                          state -> me.max_response_delay),
04411               dhcp_failover_option_printf(FTO_VENDOR_CLASS, FMA,
04412                                           "isc-%s", PACKAGE_VERSION),
04413               dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
04414                                          DHCP_FAILOVER_VERSION),
04415               dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
04416                                          0, 0),
04417               dhcp_failover_make_option (FTO_MCLT, FMA,
04418                                          state -> mclt),
04419               (state -> hba
04420                ? dhcp_failover_make_option (FTO_HBA, FMA, 32, state -> hba)
04421                : &skip_failover_option),
04422               (failover_option_t *)0));
04423         
04424 #if defined (DEBUG_FAILOVER_MESSAGES)
04425         if (status != ISC_R_SUCCESS)
04426                 failover_print (FMA, " (failed)");
04427         failover_print (FMA, ")");
04428         if (obufix) {
04429                 log_debug ("%s", obuf);
04430         }
04431 #endif
04432         return status;
04433 }
04434 
04435 isc_result_t dhcp_failover_send_connectack (omapi_object_t *l,
04436                                             dhcp_failover_state_t *state,
04437                                             int reason, const char *errmsg)
04438 {
04439         dhcp_failover_link_t *link;
04440         isc_result_t status;
04441 #if defined (DEBUG_FAILOVER_MESSAGES)   
04442         char obuf [64];
04443         unsigned obufix = 0;
04444         
04445 # define FMA obuf, &obufix, sizeof obuf
04446         failover_print (FMA, "(connectack");
04447 #else
04448 # define FMA (char *)0, (unsigned *)0, 0
04449 #endif
04450 
04451         if (!l || l -> type != dhcp_type_failover_link)
04452                 return DHCP_R_INVALIDARG;
04453         link = (dhcp_failover_link_t *)l;
04454         if (!l -> outer || l -> outer -> type != omapi_type_connection)
04455                 return DHCP_R_INVALIDARG;
04456 
04457         status =
04458             (dhcp_failover_put_message
04459              (link, l -> outer,
04460               FTM_CONNECTACK, link->imsg->xid,
04461               state
04462                ? dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA,
04463                                            strlen(state->name), state->name)
04464                : (link->imsg->options_present & FTB_RELATIONSHIP_NAME)
04465                   ? dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA,
04466                                               link->imsg->relationship_name.count,
04467                                               link->imsg->relationship_name.data)
04468                   : &skip_failover_option,
04469               state
04470                ? dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
04471                                             state -> me.max_flying_updates)
04472                : &skip_failover_option,
04473               state
04474                ? dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
04475                                             state -> me.max_response_delay)
04476                : &skip_failover_option,
04477               dhcp_failover_option_printf(FTO_VENDOR_CLASS, FMA,
04478                                           "isc-%s", PACKAGE_VERSION),
04479               dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
04480                                          DHCP_FAILOVER_VERSION),
04481               (link->imsg->options_present & FTB_TLS_REQUEST)
04482                ? dhcp_failover_make_option(FTO_TLS_REPLY, FMA,
04483                                            0, 0)
04484                : &skip_failover_option,
04485               reason
04486                ? dhcp_failover_make_option (FTO_REJECT_REASON,
04487                                             FMA, reason)
04488                : &skip_failover_option,
04489               (reason && errmsg)
04490                ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
04491                                             strlen (errmsg), errmsg)
04492                : &skip_failover_option,
04493               (failover_option_t *)0));
04494 
04495 #if defined (DEBUG_FAILOVER_MESSAGES)
04496         if (status != ISC_R_SUCCESS)
04497                 failover_print (FMA, " (failed)");
04498         failover_print (FMA, ")");
04499         if (obufix) {
04500                 log_debug ("%s", obuf);
04501         }
04502 #endif
04503         return status;
04504 }
04505 
04506 isc_result_t dhcp_failover_send_disconnect (omapi_object_t *l,
04507                                             int reason,
04508                                             const char *message)
04509 {
04510         dhcp_failover_link_t *link;
04511         isc_result_t status;
04512 #if defined (DEBUG_FAILOVER_MESSAGES)   
04513         char obuf [64];
04514         unsigned obufix = 0;
04515         
04516 # define FMA obuf, &obufix, sizeof obuf
04517         failover_print (FMA, "(disconnect");
04518 #else
04519 # define FMA (char *)0, (unsigned *)0, 0
04520 #endif
04521 
04522         if (!l || l -> type != dhcp_type_failover_link)
04523                 return DHCP_R_INVALIDARG;
04524         link = (dhcp_failover_link_t *)l;
04525         if (!l -> outer || l -> outer -> type != omapi_type_connection)
04526                 return DHCP_R_INVALIDARG;
04527 
04528         if (!message && reason)
04529                 message = dhcp_failover_reject_reason_print (reason);
04530 
04531         status = (dhcp_failover_put_message
04532                   (link, l -> outer,
04533                    FTM_DISCONNECT, link->xid++,
04534                    dhcp_failover_make_option (FTO_REJECT_REASON,
04535                                               FMA, reason),
04536                    (message
04537                     ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
04538                                                  strlen (message), message)
04539                     : &skip_failover_option),
04540                    (failover_option_t *)0));
04541 
04542 #if defined (DEBUG_FAILOVER_MESSAGES)
04543         if (status != ISC_R_SUCCESS)
04544                 failover_print (FMA, " (failed)");
04545         failover_print (FMA, ")");
04546         if (obufix) {
04547                 log_debug ("%s", obuf);
04548         }
04549 #endif
04550         return status;
04551 }
04552 
04553 /* Send a Bind Update message. */
04554 
04555 isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state,
04556                                              struct lease *lease)
04557 {
04558         dhcp_failover_link_t *link;
04559         isc_result_t status;
04560         int flags = 0;
04561         binding_state_t transmit_state;
04562 #if defined (DEBUG_FAILOVER_MESSAGES)   
04563         char obuf [64];
04564         unsigned obufix = 0;
04565         
04566 # define FMA obuf, &obufix, sizeof obuf
04567         failover_print (FMA, "(bndupd");
04568 #else
04569 # define FMA (char *)0, (unsigned *)0, 0
04570 #endif
04571 
04572         if (!state -> link_to_peer ||
04573             state -> link_to_peer -> type != dhcp_type_failover_link)
04574                 return DHCP_R_INVALIDARG;
04575         link = (dhcp_failover_link_t *)state -> link_to_peer;
04576 
04577         if (!link -> outer || link -> outer -> type != omapi_type_connection)
04578                 return DHCP_R_INVALIDARG;
04579 
04580         transmit_state = lease->desired_binding_state;
04581         if (lease->flags & RESERVED_LEASE) {
04582                 /* If we are listing an allocable (not yet ACTIVE etc) lease
04583                  * as reserved, toggle to the peer's 'free state', per the
04584                  * draft.  This gives the peer permission to alloc it to the
04585                  * chaddr/uid-named client.
04586                  */
04587                 if ((state->i_am == primary) && (transmit_state == FTS_FREE))
04588                         transmit_state = FTS_BACKUP;
04589                 else if ((state->i_am == secondary) &&
04590                          (transmit_state == FTS_BACKUP))
04591                         transmit_state = FTS_FREE;
04592 
04593                 flags |= FTF_IP_FLAG_RESERVE;
04594         }
04595         if (lease->flags & BOOTP_LEASE)
04596                 flags |= FTF_IP_FLAG_BOOTP;
04597 
04598         /* last_xid == 0 is illegal, seek past zero if we hit it. */
04599         if (link->xid == 0)
04600                 link->xid = 1;
04601 
04602         lease->last_xid = link->xid++;
04603 
04604         /*
04605          * Our very next action is to transmit a binding update relating to
04606          * this lease over the wire, and although there is a BNDACK, there is
04607          * no BNDACKACK or BNDACKACKACK...the basic issue as we send a BNDUPD,
04608          * we may not receive a BNDACK.  This non-reception does not imply the
04609          * peer did not receive and process the BNDUPD.  So at this point, we
04610          * must divest any state that would be dangerous to retain under the
04611          * impression the peer has been updated.  Normally state changes like
04612          * this are processed in supersede_lease(), but in this case we need a
04613          * very late binding.
04614          *
04615          * In failover rules, a server is permitted to work forward in certain
04616          * directions from a given lease's state; active leases may be
04617          * extended, so forth.  There is an 'optimization' in the failover
04618          * draft that permits a server to 'rewind' any work they have not
04619          * informed the peer.  Since we can't know if the peer received our
04620          * update but was unable to acknowledge it, we make this change on
04621          * transmit rather than upon receiving the acknowledgement.
04622          *
04623          * XXX: Frequent lease commits are undesirable.  This should hopefully
04624          * only trigger when a server is sending a lease /state change/, and
04625          * not merely an update such as with a renewal.
04626          */
04627         if (lease->rewind_binding_state != lease->binding_state) {
04628                 lease->rewind_binding_state = lease->binding_state;
04629 
04630                 write_lease(lease);
04631                 commit_leases();
04632         }
04633 
04634         /* Send the update. */
04635         status = (dhcp_failover_put_message
04636                   (link, link -> outer,
04637                    FTM_BNDUPD, lease->last_xid,
04638                    dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
04639                                               lease -> ip_addr.len,
04640                                               lease -> ip_addr.iabuf),
04641                    dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
04642                                               lease -> desired_binding_state),
04643                    lease -> uid_len
04644                    ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
04645                                                 lease -> uid_len,
04646                                                 lease -> uid)
04647                    : &skip_failover_option,
04648                    lease -> hardware_addr.hlen
04649                    ? dhcp_failover_make_option (FTO_CHADDR, FMA,
04650                                                 lease -> hardware_addr.hlen,
04651                                                 lease -> hardware_addr.hbuf)
04652                    : &skip_failover_option,
04653                    dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
04654                                               lease -> ends),
04655                    dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
04656                                               lease -> tstp),
04657                    dhcp_failover_make_option (FTO_STOS, FMA,
04658                                               lease -> starts),
04659                    (lease->cltt != 0) ? 
04660                         dhcp_failover_make_option(FTO_CLTT, FMA, lease->cltt) :
04661                         &skip_failover_option, /* No CLTT */
04662                    flags ? dhcp_failover_make_option(FTO_IP_FLAGS, FMA,
04663                                                      flags) :
04664                            &skip_failover_option, /* No IP_FLAGS */
04665                    &skip_failover_option,       /* XXX DDNS */
04666                    &skip_failover_option,       /* XXX request options */
04667                    &skip_failover_option,       /* XXX reply options */
04668                    (failover_option_t *)0));
04669 
04670 #if defined (DEBUG_FAILOVER_MESSAGES)
04671         if (status != ISC_R_SUCCESS)
04672                 failover_print (FMA, " (failed)");
04673         failover_print (FMA, ")");
04674         if (obufix) {
04675                 log_debug ("%s", obuf);
04676         }
04677 #endif
04678         return status;
04679 }
04680 
04681 /* Send a Bind ACK message. */
04682 
04683 isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *state,
04684                                           failover_message_t *msg,
04685                                           int reason, const char *message)
04686 {
04687         dhcp_failover_link_t *link;
04688         isc_result_t status;
04689 #if defined (DEBUG_FAILOVER_MESSAGES)   
04690         char obuf [64];
04691         unsigned obufix = 0;
04692         
04693 # define FMA obuf, &obufix, sizeof obuf
04694         failover_print (FMA, "(bndack");
04695 #else
04696 # define FMA (char *)0, (unsigned *)0, 0
04697 #endif
04698 
04699         if (!state -> link_to_peer ||
04700             state -> link_to_peer -> type != dhcp_type_failover_link)
04701                 return DHCP_R_INVALIDARG;
04702         link = (dhcp_failover_link_t *)state -> link_to_peer;
04703 
04704         if (!link -> outer || link -> outer -> type != omapi_type_connection)
04705                 return DHCP_R_INVALIDARG;
04706 
04707         if (!message && reason)
04708                 message = dhcp_failover_reject_reason_print (reason);
04709 
04710         /* Send the update. */
04711         status = (dhcp_failover_put_message
04712                   (link, link -> outer,
04713                    FTM_BNDACK, msg->xid,
04714                    dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
04715                                               sizeof msg -> assigned_addr,
04716                                               &msg -> assigned_addr),
04717 #ifdef DO_BNDACK_SHOULD_NOT
04718                    dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
04719                                               msg -> binding_status),
04720                    (msg -> options_present & FTB_CLIENT_IDENTIFIER)
04721                    ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
04722                                                 msg -> client_identifier.count,
04723                                                 msg -> client_identifier.data)
04724                    : &skip_failover_option,
04725                    (msg -> options_present & FTB_CHADDR)
04726                    ? dhcp_failover_make_option (FTO_CHADDR, FMA,
04727                                                 msg -> chaddr.count,
04728                                                 msg -> chaddr.data)
04729                    : &skip_failover_option,
04730                    dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
04731                                               msg -> expiry),
04732                    dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
04733                                               msg -> potential_expiry),
04734                    dhcp_failover_make_option (FTO_STOS, FMA,
04735                                               msg -> stos),
04736                    (msg->options_present & FTB_CLTT) ?
04737                         dhcp_failover_make_option(FTO_CLTT, FMA, msg->cltt) :
04738                         &skip_failover_option, /* No CLTT in the msg to ack. */
04739                    ((msg->options_present & FTB_IP_FLAGS) && msg->ip_flags) ? 
04740                         dhcp_failover_make_option(FTO_IP_FLAGS, FMA,
04741                                                   msg->ip_flags)
04742                         : &skip_failover_option,
04743 #endif /* DO_BNDACK_SHOULD_NOT */
04744                    reason
04745                     ? dhcp_failover_make_option(FTO_REJECT_REASON, FMA, reason)
04746                     : &skip_failover_option,
04747                    (reason && message)
04748                     ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
04749                                                  strlen (message), message)
04750                     : &skip_failover_option,
04751 #ifdef DO_BNDACK_SHOULD_NOT
04752                    &skip_failover_option,       /* XXX DDNS */
04753                    &skip_failover_option,       /* XXX request options */
04754                    &skip_failover_option,       /* XXX reply options */
04755 #endif /* DO_BNDACK_SHOULD_NOT */
04756                    (failover_option_t *)0));
04757 
04758 #if defined (DEBUG_FAILOVER_MESSAGES)
04759         if (status != ISC_R_SUCCESS)
04760                 failover_print (FMA, " (failed)");
04761         failover_print (FMA, ")");
04762         if (obufix) {
04763                 log_debug ("%s", obuf);
04764         }
04765 #endif
04766         return status;
04767 }
04768 
04769 isc_result_t dhcp_failover_send_poolreq (dhcp_failover_state_t *state)
04770 {
04771         dhcp_failover_link_t *link;
04772         isc_result_t status;
04773 #if defined (DEBUG_FAILOVER_MESSAGES)   
04774         char obuf [64];
04775         unsigned obufix = 0;
04776         
04777 # define FMA obuf, &obufix, sizeof obuf
04778         failover_print (FMA, "(poolreq");
04779 #else
04780 # define FMA (char *)0, (unsigned *)0, 0
04781 #endif
04782 
04783         if (!state -> link_to_peer ||
04784             state -> link_to_peer -> type != dhcp_type_failover_link)
04785                 return DHCP_R_INVALIDARG;
04786         link = (dhcp_failover_link_t *)state -> link_to_peer;
04787 
04788         if (!link -> outer || link -> outer -> type != omapi_type_connection)
04789                 return DHCP_R_INVALIDARG;
04790 
04791         status = (dhcp_failover_put_message
04792                   (link, link -> outer,
04793                    FTM_POOLREQ, link->xid++,
04794                    (failover_option_t *)0));
04795 
04796 #if defined (DEBUG_FAILOVER_MESSAGES)
04797         if (status != ISC_R_SUCCESS)
04798                 failover_print (FMA, " (failed)");
04799         failover_print (FMA, ")");
04800         if (obufix) {
04801                 log_debug ("%s", obuf);
04802         }
04803 #endif
04804         return status;
04805 }
04806 
04807 isc_result_t dhcp_failover_send_poolresp (dhcp_failover_state_t *state,
04808                                           int leases)
04809 {
04810         dhcp_failover_link_t *link;
04811         isc_result_t status;
04812 #if defined (DEBUG_FAILOVER_MESSAGES)   
04813         char obuf [64];
04814         unsigned obufix = 0;
04815         
04816 # define FMA obuf, &obufix, sizeof obuf
04817         failover_print (FMA, "(poolresp");
04818 #else
04819 # define FMA (char *)0, (unsigned *)0, 0
04820 #endif
04821 
04822         if (!state -> link_to_peer ||
04823             state -> link_to_peer -> type != dhcp_type_failover_link)
04824                 return DHCP_R_INVALIDARG;
04825         link = (dhcp_failover_link_t *)state -> link_to_peer;
04826 
04827         if (!link -> outer || link -> outer -> type != omapi_type_connection)
04828                 return DHCP_R_INVALIDARG;
04829 
04830         status = (dhcp_failover_put_message
04831                   (link, link -> outer,
04832                    FTM_POOLRESP, link->imsg->xid,
04833                    dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED, FMA,
04834                                               leases),
04835                    (failover_option_t *)0));
04836 
04837 #if defined (DEBUG_FAILOVER_MESSAGES)
04838         if (status != ISC_R_SUCCESS)
04839                 failover_print (FMA, " (failed)");
04840         failover_print (FMA, ")");
04841         if (obufix) {
04842                 log_debug ("%s", obuf);
04843         }
04844 #endif
04845         return status;
04846 }
04847 
04848 isc_result_t dhcp_failover_send_update_request (dhcp_failover_state_t *state)
04849 {
04850         dhcp_failover_link_t *link;
04851         isc_result_t status;
04852 #if defined (DEBUG_FAILOVER_MESSAGES)   
04853         char obuf [64];
04854         unsigned obufix = 0;
04855 
04856 # define FMA obuf, &obufix, sizeof obuf
04857         failover_print (FMA, "(updreq");
04858 #else
04859 # define FMA (char *)0, (unsigned *)0, 0
04860 #endif
04861 
04862         if (!state -> link_to_peer ||
04863             state -> link_to_peer -> type != dhcp_type_failover_link)
04864                 return DHCP_R_INVALIDARG;
04865         link = (dhcp_failover_link_t *)state -> link_to_peer;
04866 
04867         if (!link -> outer || link -> outer -> type != omapi_type_connection)
04868                 return DHCP_R_INVALIDARG;
04869 
04870         if (state -> curUPD)
04871                 return ISC_R_ALREADYRUNNING;
04872 
04873         status = (dhcp_failover_put_message
04874                   (link, link -> outer,
04875                    FTM_UPDREQ, link->xid++,
04876                    (failover_option_t *)0));
04877 
04878         if (status == ISC_R_SUCCESS)
04879                 state -> curUPD = FTM_UPDREQ;
04880 
04881 #if defined (DEBUG_FAILOVER_MESSAGES)
04882         if (status != ISC_R_SUCCESS)
04883                 failover_print (FMA, " (failed)");
04884         failover_print (FMA, ")");
04885         if (obufix) {
04886                 log_debug ("%s", obuf);
04887         }
04888 #endif
04889         log_info ("Sent update request message to %s", state -> name);
04890         return status;
04891 }
04892 
04893 isc_result_t dhcp_failover_send_update_request_all (dhcp_failover_state_t
04894                                                     *state)
04895 {
04896         dhcp_failover_link_t *link;
04897         isc_result_t status;
04898 #if defined (DEBUG_FAILOVER_MESSAGES)   
04899         char obuf [64];
04900         unsigned obufix = 0;
04901         
04902 # define FMA obuf, &obufix, sizeof obuf
04903         failover_print (FMA, "(updreqall");
04904 #else
04905 # define FMA (char *)0, (unsigned *)0, 0
04906 #endif
04907 
04908         if (!state -> link_to_peer ||
04909             state -> link_to_peer -> type != dhcp_type_failover_link)
04910                 return DHCP_R_INVALIDARG;
04911         link = (dhcp_failover_link_t *)state -> link_to_peer;
04912 
04913         if (!link -> outer || link -> outer -> type != omapi_type_connection)
04914                 return DHCP_R_INVALIDARG;
04915 
04916         /* If there is an UPDREQ in progress, then upgrade to UPDREQALL. */
04917         if (state -> curUPD && (state -> curUPD != FTM_UPDREQ))
04918                 return ISC_R_ALREADYRUNNING;
04919 
04920         status = (dhcp_failover_put_message
04921                   (link, link -> outer,
04922                    FTM_UPDREQALL, link->xid++,
04923                    (failover_option_t *)0));
04924 
04925         if (status == ISC_R_SUCCESS)
04926                 state -> curUPD = FTM_UPDREQALL;
04927 
04928 #if defined (DEBUG_FAILOVER_MESSAGES)
04929         if (status != ISC_R_SUCCESS)
04930                 failover_print (FMA, " (failed)");
04931         failover_print (FMA, ")");
04932         if (obufix) {
04933                 log_debug ("%s", obuf);
04934         }
04935 #endif
04936         log_info ("Sent update request all message to %s", state -> name);
04937         return status;
04938 }
04939 
04940 isc_result_t dhcp_failover_send_update_done (dhcp_failover_state_t *state)
04941 {
04942         dhcp_failover_link_t *link;
04943         isc_result_t status;
04944 #if defined (DEBUG_FAILOVER_MESSAGES)   
04945         char obuf [64];
04946         unsigned obufix = 0;
04947         
04948 # define FMA obuf, &obufix, sizeof obuf
04949         failover_print (FMA, "(upddone");
04950 #else
04951 # define FMA (char *)0, (unsigned *)0, 0
04952 #endif
04953 
04954         if (!state -> link_to_peer ||
04955             state -> link_to_peer -> type != dhcp_type_failover_link)
04956                 return DHCP_R_INVALIDARG;
04957         link = (dhcp_failover_link_t *)state -> link_to_peer;
04958 
04959         if (!link -> outer || link -> outer -> type != omapi_type_connection)
04960                 return DHCP_R_INVALIDARG;
04961 
04962         status = (dhcp_failover_put_message
04963                   (link, link -> outer,
04964                    FTM_UPDDONE, state->updxid,
04965                    (failover_option_t *)0));
04966 
04967 #if defined (DEBUG_FAILOVER_MESSAGES)
04968         if (status != ISC_R_SUCCESS)
04969                 failover_print (FMA, " (failed)");
04970         failover_print (FMA, ")");
04971         if (obufix) {
04972                 log_debug ("%s", obuf);
04973         }
04974 #endif
04975 
04976         log_info ("Sent update done message to %s", state -> name);
04977 
04978         state->updxid--; /* Paranoia, just so it mismatches. */
04979 
04980         /* There may be uncommitted leases at this point (since
04981            dhcp_failover_process_bind_ack() doesn't commit leases);
04982            commit the lease file. */
04983         commit_leases();
04984 
04985         return status;
04986 }
04987 
04988 /*
04989  * failover_lease_is_better() compares the binding update in 'msg' with
04990  * the current lease in 'lease'.  If the determination is that the binding
04991  * update shouldn't be allowed to update/crush more critical binding info
04992  * on the lease, the lease is preferred.  A value of true is returned if the
04993  * local lease is preferred, or false if the remote binding update is
04994  * preferred.
04995  *
04996  * For now this function is hopefully simplistic and trivial.  It may be that
04997  * a more detailed system of preferences is required, so this is something we
04998  * should monitor as we gain experience with these dueling events.
04999  */
05000 static isc_boolean_t
05001 failover_lease_is_better(dhcp_failover_state_t *state, struct lease *lease,
05002                          failover_message_t *msg)
05003 {
05004         binding_state_t local_state;
05005         TIME msg_cltt;
05006 
05007         if (lease->binding_state != lease->desired_binding_state)
05008                 local_state = lease->desired_binding_state;
05009         else
05010                 local_state = lease->binding_state;
05011 
05012         if ((msg->options_present & FTB_CLTT) != 0)
05013                 msg_cltt = msg->cltt;
05014         else
05015                 msg_cltt = 0;
05016 
05017         switch(local_state) {
05018               case FTS_ACTIVE:
05019                 if (msg->binding_status == FTS_ACTIVE) {
05020                         if (msg_cltt < lease->cltt)
05021                                 return ISC_TRUE;
05022                         else if (msg_cltt > lease->cltt)
05023                                 return ISC_FALSE;
05024                         else if (state->i_am == primary)
05025                                 return ISC_TRUE;
05026                         else
05027                                 return ISC_FALSE;
05028                 } else if (msg->binding_status == FTS_EXPIRED) {
05029                         return ISC_FALSE;
05030                 }
05031                 /* FALL THROUGH */
05032 
05033               case FTS_FREE:
05034               case FTS_BACKUP:
05035               case FTS_EXPIRED:
05036               case FTS_RELEASED:
05037               case FTS_ABANDONED:
05038               case FTS_RESET:
05039                 if (msg->binding_status == FTS_ACTIVE)
05040                         return ISC_FALSE;
05041                 else if (state->i_am == primary)
05042                         return ISC_TRUE;
05043                 else
05044                         return ISC_FALSE;
05045                 /* FALL THROUGH to impossible condition */
05046 
05047               default:
05048                 log_fatal("Impossible condition at %s:%d.", MDL);
05049         }
05050 
05051         log_fatal("Impossible condition at %s:%d.", MDL);
05052         /* Silence compiler warning. */
05053         return ISC_FALSE;
05054 }
05055 
05056 isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *state,
05057                                                failover_message_t *msg)
05058 {
05059         struct lease *lt = NULL, *lease = NULL;
05060         struct iaddr ia;
05061         int reason = FTR_MISC_REJECT;
05062         const char *message;
05063         int new_binding_state;
05064         int send_to_backup = 0;
05065         int required_options;
05066         isc_boolean_t chaddr_changed = ISC_FALSE;
05067         isc_boolean_t ident_changed = ISC_FALSE;
05068 
05069         /* Validate the binding update. */
05070         required_options = FTB_ASSIGNED_IP_ADDRESS | FTB_BINDING_STATUS;
05071         if ((msg->options_present & required_options) != required_options) {
05072                 message = "binding update lacks required options";
05073                 reason = FTR_MISSING_BINDINFO;
05074                 goto bad;
05075         }
05076 
05077         ia.len = sizeof msg -> assigned_addr;
05078         memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
05079 
05080         if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
05081                 message = "unknown IP address";
05082                 reason = FTR_ILLEGAL_IP_ADDR;
05083                 goto bad;
05084         }
05085 
05086         /*
05087          * If this lease is covered by a different failover peering
05088          * relationship, assert an error.
05089          */
05090         if ((lease->pool == NULL) || (lease->pool->failover_peer == NULL) ||
05091             (lease->pool->failover_peer != state)) {
05092                 message = "IP address is covered by a different failover "
05093                           "relationship state";
05094                 reason = FTR_ILLEGAL_IP_ADDR;
05095                 goto bad;
05096         }
05097 
05098         /*
05099          * Dueling updates:  This happens when both servers send a BNDUPD
05100          * at the same time.  We want the best update to win, which means
05101          * we reject if we think ours is better, or cancel if we think the
05102          * peer's is better.  We only assert a problem if the lease is on
05103          * the ACK queue, not on the UPDATE queue.  This means that after
05104          * accepting this server's BNDUPD, we will send our own BNDUPD
05105          * /after/ sending the BNDACK (this order was recently enforced in
05106          * queue processing).
05107          */
05108         if ((lease->flags & ON_ACK_QUEUE) != 0) {
05109                 if (failover_lease_is_better(state, lease, msg)) {
05110                         message = "incoming update is less critical than "
05111                                   "outgoing update";
05112                         reason = FTR_LESS_CRIT_BIND_INFO;
05113                         goto bad;
05114                 } else {
05115                         /* This makes it so we ignore any spurious ACKs. */
05116                         dhcp_failover_ack_queue_remove(state, lease);
05117                 }
05118         }
05119 
05120         /* Install the new info.  Start by taking a copy to markup. */
05121         if (!lease_copy (&lt, lease, MDL)) {
05122                 message = "no memory";
05123                 goto bad;
05124         }
05125 
05126         if (msg -> options_present & FTB_CHADDR) {
05127                 if (msg->binding_status == FTS_ABANDONED) {
05128                         message = "BNDUPD to ABANDONED with a CHADDR";
05129                         goto bad;
05130                 }
05131                 if (msg -> chaddr.count > sizeof lt -> hardware_addr.hbuf) {
05132                         message = "chaddr too long";
05133                         goto bad;
05134                 }
05135 
05136                 if ((lt->hardware_addr.hlen != msg->chaddr.count) ||
05137                     (memcmp(lt->hardware_addr.hbuf, msg->chaddr.data,
05138                             msg->chaddr.count) != 0))
05139                         chaddr_changed = ISC_TRUE;
05140 
05141                 lt -> hardware_addr.hlen = msg -> chaddr.count;
05142                 memcpy (lt -> hardware_addr.hbuf, msg -> chaddr.data,
05143                         msg -> chaddr.count);
05144         } else if (msg->binding_status == FTS_ACTIVE ||
05145                    msg->binding_status == FTS_EXPIRED ||
05146                    msg->binding_status == FTS_RELEASED) {
05147                 message = "BNDUPD without CHADDR";
05148                 reason = FTR_MISSING_BINDINFO;
05149                 goto bad;
05150         } else if (msg->binding_status == FTS_ABANDONED) {
05151                 chaddr_changed = ISC_TRUE;
05152                 lt->hardware_addr.hlen = 0;
05153                 if (lt->scope)
05154                         binding_scope_dereference(&lt->scope, MDL);
05155         }
05156 
05157         /* There is no explicit message content to indicate that the client
05158          * supplied no client-identifier.  So if we don't hear of a value,
05159          * we discard the last one.
05160          */
05161         if (msg->options_present & FTB_CLIENT_IDENTIFIER) {
05162                 if (msg->binding_status == FTS_ABANDONED) {
05163                         message = "BNDUPD to ABANDONED with client-id";
05164                         goto bad;
05165                 }
05166 
05167                 if ((lt->uid_len != msg->client_identifier.count) ||
05168                     (lt->uid == NULL) || /* Sanity; should never happen. */
05169                     (memcmp(lt->uid, msg->client_identifier.data,
05170                             lt->uid_len) != 0))
05171                         ident_changed = ISC_TRUE;
05172 
05173                 lt->uid_len = msg->client_identifier.count;
05174 
05175                 /* Allocate the lt->uid buffer if we haven't already, or
05176                  * re-allocate the lt-uid buffer if we have one that is not
05177                  * large enough.  Otherwise, just use the extant buffer.
05178                  */
05179                 if (!lt->uid || lt->uid == lt->uid_buf ||
05180                     lt->uid_len > lt->uid_max) {
05181                         if (lt->uid && lt->uid != lt->uid_buf)
05182                                 dfree(lt->uid, MDL);
05183 
05184                         if (lt->uid_len > sizeof(lt->uid_buf)) {
05185                                 lt->uid_max = lt->uid_len;
05186                                 lt->uid = dmalloc(lt->uid_len, MDL);
05187                                 if (!lt->uid) {
05188                                         message = "no memory";
05189                                         goto bad;
05190                                 }
05191                         } else {
05192                                 lt->uid_max = sizeof(lt->uid_buf);
05193                                 lt->uid = lt->uid_buf;
05194                         }
05195                 }
05196                 memcpy (lt -> uid,
05197                         msg -> client_identifier.data, lt -> uid_len);
05198         } else if (lt->uid && msg->binding_status != FTS_RESET &&
05199                    msg->binding_status != FTS_FREE &&
05200                    msg->binding_status != FTS_BACKUP) {
05201                 ident_changed = ISC_TRUE;
05202                 if (lt->uid != lt->uid_buf)
05203                         dfree (lt->uid, MDL);
05204                 lt->uid = NULL;
05205                 lt->uid_max = lt->uid_len = 0;
05206         }
05207 
05208         /*
05209          * A server's configuration can assign a 'binding scope';
05210          *
05211          *      set var = "value";
05212          *
05213          * The problem with these binding scopes is that they are refreshed
05214          * when the server processes a client's DHCP packet.  A local binding
05215          * scope is trash, then, when the lease has been assigned by the
05216          * partner server.  There is no real way to detect this, a peer may
05217          * be updating us (as through potential conflict) with a binding we
05218          * sent them, but we can trivially detect the /problematic/ case;
05219          *
05220          *      lease is free.
05221          *      primary allocates lease to client A, assigns ddns name A.
05222          *      primary fails.
05223          *      secondary enters partner down.
05224          *      lease expires, and is set free.
05225          *      lease is allocated to client B and given ddns name B.
05226          *      primary recovers.
05227          *
05228          * The binding update in this case will be active->active, but the
05229          * client identification on the lease will have changed.  The ddns
05230          * update on client A will have leaked if we just remove the binding
05231          * scope blindly.
05232          */
05233         if (msg->binding_status == FTS_ACTIVE &&
05234             (chaddr_changed || ident_changed)) {
05235                 (void) ddns_removals(lease, NULL, NULL, ISC_FALSE);
05236 
05237                 if (lease->scope != NULL)
05238                         binding_scope_dereference(&lease->scope, MDL);
05239         }
05240 
05241         /* XXX Times may need to be adjusted based on clock skew! */
05242         if (msg -> options_present & FTB_STOS) {
05243                 lt -> starts = msg -> stos;
05244         }
05245         if (msg -> options_present & FTB_LEASE_EXPIRY) {
05246                 lt -> ends = msg -> expiry;
05247         }
05248         if (msg->options_present & FTB_POTENTIAL_EXPIRY) {
05249                 lt->atsfp = lt->tsfp = msg->potential_expiry;
05250         }
05251         if (msg->options_present & FTB_IP_FLAGS) {
05252                 if (msg->ip_flags & FTF_IP_FLAG_RESERVE) {
05253                         if ((((state->i_am == primary) &&
05254                               (lease->binding_state == FTS_FREE)) ||
05255                              ((state->i_am == secondary) &&
05256                               (lease->binding_state == FTS_BACKUP))) &&
05257                             !(lease->flags & RESERVED_LEASE)) {
05258                                 message = "Address is not reserved.";
05259                                 reason = FTR_IP_NOT_RESERVED;
05260                                 goto bad;
05261                         }
05262 
05263                         lt->flags |= RESERVED_LEASE;
05264                 } else
05265                         lt->flags &= ~RESERVED_LEASE;
05266 
05267                 if (msg->ip_flags & FTF_IP_FLAG_BOOTP) {
05268                         if ((((state->i_am == primary) &&
05269                               (lease->binding_state == FTS_FREE)) ||
05270                              ((state->i_am == secondary) &&
05271                               (lease->binding_state == FTS_BACKUP))) &&
05272                             !(lease->flags & BOOTP_LEASE)) {
05273                                 message = "Address is not allocated to BOOTP.";
05274                                 goto bad;
05275                         }
05276                         lt->flags |= BOOTP_LEASE;
05277                 } else
05278                         lt->flags &= ~BOOTP_LEASE;
05279 
05280                 if (msg->ip_flags & ~(FTF_IP_FLAG_RESERVE | FTF_IP_FLAG_BOOTP))
05281                         log_info("Unknown IP-flags set in BNDUPD (0x%x).",
05282                                  msg->ip_flags);
05283         } else /* Flags may only not appear if the values are zero. */
05284                 lt->flags &= ~(RESERVED_LEASE | BOOTP_LEASE);
05285 
05286 #if defined (DEBUG_LEASE_STATE_TRANSITIONS)
05287         log_info ("processing state transition for %s: %s to %s",
05288                   piaddr (lease -> ip_addr),
05289                   binding_state_print (lease -> binding_state),
05290                   binding_state_print (msg -> binding_status));
05291 #endif
05292 
05293         /* If we're in normal state, make sure the state transition
05294            we got is valid. */
05295         if (state -> me.state == normal) {
05296                 new_binding_state =
05297                         (normal_binding_state_transition_check
05298                          (lease, state, msg -> binding_status,
05299                           msg -> potential_expiry));
05300                 /* XXX if the transition the peer asked for isn't
05301                    XXX allowed, maybe we should make the transition
05302                    XXX into potential-conflict at this point. */
05303         } else {
05304                 new_binding_state =
05305                         (conflict_binding_state_transition_check
05306                          (lease, state, msg -> binding_status,
05307                           msg -> potential_expiry));
05308         }
05309         if (new_binding_state != msg -> binding_status) {
05310                 char outbuf [100];
05311 
05312                 if (snprintf (outbuf, sizeof outbuf,
05313                           "%s: invalid state transition: %s to %s",
05314                           piaddr (lease -> ip_addr),
05315                           binding_state_print (lease -> binding_state),
05316                           binding_state_print (msg -> binding_status))
05317                                         >= sizeof outbuf)
05318                         log_fatal ("%s: impossible outbuf overflow",
05319                                 "dhcp_failover_process_bind_update");
05320 
05321                 dhcp_failover_send_bind_ack (state, msg,
05322                                              FTR_FATAL_CONFLICT,
05323                                              outbuf);
05324                 goto out;
05325         }
05326         if (new_binding_state == FTS_EXPIRED ||
05327             new_binding_state == FTS_RELEASED ||
05328             new_binding_state == FTS_RESET) {
05329                 lt -> next_binding_state = FTS_FREE;
05330 
05331                 /* Mac address affinity.  Assign the lease to
05332                  * BACKUP state if we are the primary and the
05333                  * peer is more likely to reallocate this lease
05334                  * to a returning client.
05335                  */
05336                 if ((state->i_am == primary) &&
05337                     !(lt->flags & (RESERVED_LEASE | BOOTP_LEASE)))
05338                         send_to_backup = peer_wants_lease(lt);
05339         } else {
05340                 lt -> next_binding_state = new_binding_state;
05341         }
05342         msg -> binding_status = lt -> next_binding_state;
05343 
05344         /*
05345          * If we accept a peer's binding update, then we can't rewind a
05346          * lease behind the peer's state.
05347          */
05348         lease->rewind_binding_state = lt->next_binding_state;
05349 
05350         /* Try to install the new information. */
05351         if (!supersede_lease (lease, lt, 0, 0, 0) ||
05352             !write_lease (lease)) {
05353                 message = "database update failed";
05354               bad:
05355                 dhcp_failover_send_bind_ack (state, msg, reason, message);
05356                 goto out;
05357         } else {
05358                 dhcp_failover_queue_ack (state, msg);
05359         }
05360 
05361         /* If it is probably wise, assign lease to backup state if the peer
05362          * is not already hoarding leases.
05363          */
05364         if (send_to_backup && secondary_not_hoarding(state, lease->pool)) {
05365                 lease->next_binding_state = FTS_BACKUP;
05366                 lease->tstp = cur_time;
05367                 lease->starts = cur_time;
05368 
05369                 if (!supersede_lease(lease, NULL, 0, 1, 0) ||
05370                     !write_lease(lease))
05371                         log_error("can't commit lease %s for mac addr "
05372                                   "affinity", piaddr(lease->ip_addr));
05373 
05374                 dhcp_failover_send_updates(state);
05375         }
05376 
05377       out:
05378         if (lt)
05379                 lease_dereference (&lt, MDL);
05380         if (lease)
05381                 lease_dereference (&lease, MDL);
05382 
05383         return ISC_R_SUCCESS;
05384 }
05385 
05386 /* This was hairy enough I didn't want to do it all in an if statement.
05387  *
05388  * Returns: Truth is the secondary is allowed to get more leases based upon
05389  * MAC address affinity.  False otherwise.
05390  */
05391 static inline int
05392 secondary_not_hoarding(dhcp_failover_state_t *state, struct pool *p) {
05393         int total;
05394         int hold;
05395         int lts;
05396 
05397         total = p->free_leases + p->backup_leases;
05398 
05399         /* How many leases is one side or the other allowed to "hold"? */
05400         hold = ((total * state->max_lease_ownership) + 50) / 100;
05401 
05402         /* If we were to send leases (or if the secondary were to send us
05403          * leases in the negative direction), how many would that be?
05404          */
05405         lts = (p->free_leases - p->backup_leases) / 2;
05406 
05407         /* The peer is not hoarding leases if we would send them more leases
05408          * (or they would take fewer leases) than the maximum they are allowed
05409          * to hold (the negative hold).
05410          */
05411         return(lts > -hold);
05412 }
05413 
05414 isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *state,
05415                                              failover_message_t *msg)
05416 {
05417         struct lease *lt = (struct lease *)0;
05418         struct lease *lease = (struct lease *)0;
05419         struct iaddr ia;
05420         const char *message = "no memory";
05421         u_int32_t pot_expire;
05422         int send_to_backup = ISC_FALSE;
05423         struct timeval tv;
05424 
05425         ia.len = sizeof msg -> assigned_addr;
05426         memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
05427 
05428         if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
05429                 message = "no such lease";
05430                 goto bad;
05431         }
05432 
05433         /* XXX check for conflicts. */
05434         if (msg -> options_present & FTB_REJECT_REASON) {
05435                 log_error ("bind update on %s from %s rejected: %.*s",
05436                            piaddr (ia), state -> name,
05437                            (int)((msg -> options_present & FTB_MESSAGE)
05438                                  ? msg -> message.count
05439                                  : strlen (dhcp_failover_reject_reason_print
05440                                            (msg -> reject_reason))),
05441                            (msg -> options_present & FTB_MESSAGE)
05442                            ? (const char *)(msg -> message.data)
05443                            : (dhcp_failover_reject_reason_print
05444                               (msg -> reject_reason)));
05445                 goto unqueue;
05446         }
05447 
05448         /* Silently discard acks for leases we did not update (or multiple
05449          * acks).
05450          */
05451         if (!lease->last_xid)
05452                 goto unqueue;
05453 
05454         if (lease->last_xid != msg->xid) {
05455                 message = "xid mismatch";
05456                 goto bad;
05457         }
05458 
05459         /* XXX Times may need to be adjusted based on clock skew! */
05460         if (msg->options_present & FTO_POTENTIAL_EXPIRY)
05461                 pot_expire = msg->potential_expiry;
05462         else
05463                 pot_expire = lease->tstp;
05464 
05465         /* If the lease was desired to enter a binding state, we set
05466          * such a value upon transmitting a bndupd.  We do not clear it
05467          * if we receive a bndupd in the meantime (or change the state
05468          * of the lease again ourselves), but we do set binding_state
05469          * if we get a bndupd.
05470          *
05471          * So desired_binding_state tells us what we sent a bndupd for,
05472          * and binding_state tells us what we have since determined in
05473          * the meantime.
05474          */
05475         if (lease->desired_binding_state == FTS_EXPIRED ||
05476             lease->desired_binding_state == FTS_RESET ||
05477             lease->desired_binding_state == FTS_RELEASED)
05478         {
05479                 /* It is not a problem to do this directly as we call
05480                  * supersede_lease immediately after: the lease is requeued
05481                  * even if its sort order (tsfp) has changed.
05482                  */
05483                 lease->atsfp = lease->tsfp = pot_expire;
05484                 if ((state->i_am == secondary) &&
05485                     (lease->flags & RESERVED_LEASE))
05486                         lease->next_binding_state = FTS_BACKUP;
05487                 else
05488                         lease->next_binding_state = FTS_FREE;
05489 
05490                 /* Clear this condition for the next go-round. */
05491                 lease->desired_binding_state = lease->next_binding_state;
05492 
05493                 /* The peer will have made this state change, so set rewind. */
05494                 lease->rewind_binding_state = lease->next_binding_state;
05495 
05496                 supersede_lease(lease, (struct lease *)0, 0, 0, 0);
05497                 write_lease(lease);
05498 
05499                 /* Lease has returned to FREE state from the
05500                  * transitional states.  If the lease 'belongs'
05501                  * to a client that would be served by the
05502                  * peer, process a binding update now to send
05503                  * the lease to backup state.  But not if we
05504                  * think we already have.
05505                  */
05506                 if (state->i_am == primary &&
05507                     !(lease->flags & (RESERVED_LEASE | BOOTP_LEASE)) &&
05508                     peer_wants_lease(lease))
05509                         send_to_backup = ISC_TRUE;
05510 
05511                 if (!send_to_backup && state->me.state == normal)
05512                         commit_leases();
05513         } else {
05514                 /* XXX It could be a problem to do this directly if the lease
05515                  * XXX is sorted by tsfp.
05516                  */
05517                 lease->atsfp = lease->tsfp = pot_expire;
05518                 if (lease->desired_binding_state != lease->binding_state) {
05519                         lease->next_binding_state =
05520                                 lease->desired_binding_state;
05521                         supersede_lease(lease,
05522                                         (struct lease *)0, 0, 0, 0);
05523                 }
05524                 write_lease(lease);
05525                 /* Commit the lease only after a two-second timeout,
05526                    so that if we get a bunch of acks in quick
05527                    succession (e.g., when stealing leases from the
05528                    secondary), we do not do an immediate commit for
05529                    each one. */
05530                 tv.tv_sec = cur_time + 2;
05531                 tv.tv_usec = 0;
05532                 add_timeout(&tv, commit_leases_timeout, (void *)0, 0, 0);
05533         }
05534 
05535       unqueue:
05536         dhcp_failover_ack_queue_remove (state, lease);
05537 
05538         /* If we are supposed to send an update done after we send
05539            this lease, go ahead and send it. */
05540         if (state -> send_update_done == lease) {
05541                 lease_dereference (&state -> send_update_done, MDL);
05542                 dhcp_failover_send_update_done (state);
05543         }
05544 
05545         /* Now that the lease is off the ack queue, consider putting it
05546          * back on the update queue for mac address affinity.
05547          */
05548         if (send_to_backup && secondary_not_hoarding(state, lease->pool)) {
05549                 lease->next_binding_state = FTS_BACKUP;
05550                 lease->tstp = lease->starts = cur_time;
05551 
05552                 if (!supersede_lease(lease, NULL, 0, 1, 0) ||
05553                     !write_lease(lease))
05554                         log_error("can't commit lease %s for "
05555                                   "client affinity", piaddr(lease->ip_addr));
05556 
05557                 if (state->me.state == normal)
05558                         commit_leases();
05559         }
05560 
05561         /* If there are updates pending, we've created space to send at
05562            least one. */
05563         dhcp_failover_send_updates (state);
05564 
05565       out:
05566         lease_dereference (&lease, MDL);
05567         if (lt)
05568                 lease_dereference (&lt, MDL);
05569 
05570         return ISC_R_SUCCESS;
05571 
05572       bad:
05573         log_info ("bind update on %s got ack from %s: %s.",
05574                   piaddr (ia), state -> name, message);
05575         goto out;
05576 }
05577 
05578 isc_result_t dhcp_failover_generate_update_queue (dhcp_failover_state_t *state,
05579                                                   int everythingp)
05580 {
05581         struct shared_network *s;
05582         struct pool *p;
05583         struct lease *l;
05584         int i;
05585 #define FREE_LEASES 0
05586 #define ACTIVE_LEASES 1
05587 #define EXPIRED_LEASES 2
05588 #define ABANDONED_LEASES 3
05589 #define BACKUP_LEASES 4
05590 #define RESERVED_LEASES 5
05591         struct lease **lptr[RESERVED_LEASES+1];
05592 
05593         /* Loop through each pool in each shared network and call the
05594            expiry routine on the pool. */
05595         for (s = shared_networks; s; s = s -> next) {
05596             for (p = s -> pools; p; p = p -> next) {
05597                 if (p->failover_peer != state)
05598                         continue;
05599 
05600                 lptr[FREE_LEASES] = &p->free;
05601                 lptr[ACTIVE_LEASES] = &p->active;
05602                 lptr[EXPIRED_LEASES] = &p->expired;
05603                 lptr[ABANDONED_LEASES] = &p->abandoned;
05604                 lptr[BACKUP_LEASES] = &p->backup;
05605                 lptr[RESERVED_LEASES] = &p->reserved;
05606 
05607                 for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
05608                     for (l = *(lptr [i]); l; l = l -> next) {
05609                         if ((l->flags & ON_QUEUE) == 0 &&
05610                             (everythingp ||
05611                              (l->tstp > l->atsfp) ||
05612                              (i == EXPIRED_LEASES))) {
05613                                 l -> desired_binding_state = l -> binding_state;
05614                                 dhcp_failover_queue_update (l, 0);
05615                         }
05616                     }
05617                 }
05618             }
05619         }
05620         return ISC_R_SUCCESS;
05621 }
05622 
05623 isc_result_t
05624 dhcp_failover_process_update_request (dhcp_failover_state_t *state,
05625                                       failover_message_t *msg)
05626 {
05627         if (state->send_update_done) {
05628                 log_info("Received update request while old update still "
05629                          "flying!  Silently discarding old request.");
05630                 lease_dereference(&state->send_update_done, MDL);
05631         }
05632 
05633         /* Generate a fresh update queue. */
05634         dhcp_failover_generate_update_queue (state, 0);
05635 
05636         state->updxid = msg->xid;
05637 
05638         /* If there's anything on the update queue (there shouldn't be
05639            anything on the ack queue), trigger an update done message
05640            when we get an ack for that lease. */
05641         if (state -> update_queue_tail) {
05642                 lease_reference (&state -> send_update_done,
05643                                  state -> update_queue_tail, MDL);
05644                 dhcp_failover_send_updates (state);
05645                 log_info ("Update request from %s: sending update",
05646                            state -> name);
05647         } else {
05648                 /* Otherwise, there are no updates to send, so we can
05649                    just send an UPDDONE message immediately. */
05650                 dhcp_failover_send_update_done (state);
05651                 log_info ("Update request from %s: nothing pending",
05652                            state -> name);
05653         }
05654 
05655         return ISC_R_SUCCESS;
05656 }
05657 
05658 isc_result_t
05659 dhcp_failover_process_update_request_all (dhcp_failover_state_t *state,
05660                                           failover_message_t *msg)
05661 {
05662         if (state->send_update_done) {
05663                 log_info("Received update request while old update still "
05664                          "flying!  Silently discarding old request.");
05665                 lease_dereference(&state->send_update_done, MDL);
05666         }
05667 
05668         /* Generate a fresh update queue that includes every lease. */
05669         dhcp_failover_generate_update_queue (state, 1);
05670 
05671         state->updxid = msg->xid;
05672 
05673         if (state -> update_queue_tail) {
05674                 lease_reference (&state -> send_update_done,
05675                                  state -> update_queue_tail, MDL);
05676                 dhcp_failover_send_updates (state);
05677                 log_info ("Update request all from %s: sending update",
05678                            state -> name);
05679         } else {
05680                 /* This should really never happen, but it could happen
05681                    on a server that currently has no leases configured. */
05682                 dhcp_failover_send_update_done (state);
05683                 log_info ("Update request all from %s: nothing pending",
05684                            state -> name);
05685         }
05686 
05687         return ISC_R_SUCCESS;
05688 }
05689 
05690 isc_result_t
05691 dhcp_failover_process_update_done (dhcp_failover_state_t *state,
05692                                    failover_message_t *msg)
05693 {
05694         struct timeval tv;
05695 
05696         log_info ("failover peer %s: peer update completed.",
05697                   state -> name);
05698 
05699         state -> curUPD = 0;
05700 
05701         switch (state -> me.state) {
05702               case unknown_state:
05703               case partner_down:
05704               case normal:
05705               case communications_interrupted:
05706               case resolution_interrupted:
05707               case shut_down:
05708               case paused:
05709               case recover_done:
05710               case startup:
05711               case recover_wait:
05712                 break;  /* shouldn't happen. */
05713 
05714                 /* We got the UPDDONE, so we can go into normal state! */
05715               case potential_conflict:
05716                 if (state->partner.state == conflict_done) {
05717                         if (state->i_am == secondary) {
05718                                 dhcp_failover_set_state (state, normal);
05719                         } else {
05720                                 log_error("Secondary is in conflict_done "
05721                                           "state after conflict resolution, "
05722                                           "this is illegal.");
05723                                 dhcp_failover_set_state (state, shut_down);
05724                         }
05725                 } else {
05726                         if (state->i_am == primary)
05727                                 dhcp_failover_set_state (state, conflict_done);
05728                         else
05729                                 log_error("Spurious update-done message.");
05730                 }
05731 
05732                 break;
05733 
05734               case conflict_done:
05735                 log_error("Spurious update-done message.");
05736                 break;
05737 
05738               case recover:
05739                 /* Wait for MCLT to expire before moving to recover_done,
05740                    except that if both peers come up in recover, there is
05741                    no point in waiting for MCLT to expire - this probably
05742                    indicates the initial startup of a newly-configured
05743                    failover pair. */
05744                 if (state -> me.stos + state -> mclt > cur_time &&
05745                     state -> partner.state != recover &&
05746                     state -> partner.state != recover_done) {
05747                         dhcp_failover_set_state (state, recover_wait);
05748 #if defined (DEBUG_FAILOVER_TIMING)
05749                         log_info ("add_timeout +%d %s",
05750                                   (int)(cur_time -
05751                                         state -> me.stos + state -> mclt),
05752                                   "dhcp_failover_recover_done");
05753 #endif
05754                         tv . tv_sec = (int)(state -> me.stos + state -> mclt);
05755                         tv . tv_usec = 0;
05756                         add_timeout (&tv,
05757                                      dhcp_failover_recover_done,
05758                                      state,
05759                                      (tvref_t)omapi_object_reference,
05760                                      (tvunref_t)
05761                                      omapi_object_dereference);
05762                 } else
05763                         dhcp_failover_recover_done (state);
05764         }
05765 
05766         return ISC_R_SUCCESS;
05767 }
05768 
05769 void dhcp_failover_recover_done (void *sp)
05770 {
05771         dhcp_failover_state_t *state = sp;
05772 
05773 #if defined (DEBUG_FAILOVER_TIMING)
05774         log_info ("dhcp_failover_recover_done");
05775 #endif
05776 
05777         dhcp_failover_set_state (state, recover_done);
05778 }
05779 
05780 #if defined (DEBUG_FAILOVER_MESSAGES)
05781 /* Print hunks of failover messages, doing line breaks as appropriate.
05782    Note that this assumes syslog is being used, rather than, e.g., the
05783    Windows NT logging facility, where just dumping the whole message in
05784    one hunk would be more appropriate. */
05785 
05786 void failover_print (char *obuf,
05787                      unsigned *obufix, unsigned obufmax, const char *s)
05788 {
05789         int len = strlen (s);
05790 
05791         while (len + *obufix + 1 >= obufmax) {
05792                 log_debug ("%s", obuf);
05793                 if (!*obufix) {
05794                         log_debug ("%s", s);
05795                         *obufix = 0;
05796                         return;
05797                 }
05798                 *obufix = 0;
05799         }
05800         strcpy (&obuf [*obufix], s);
05801         *obufix += len;
05802 }       
05803 #endif /* defined (DEBUG_FAILOVER_MESSAGES) */
05804 
05805 /* Taken from draft-ietf-dhc-loadb-01.txt: */
05806 /* A "mixing table" of 256 distinct values, in pseudo-random order. */
05807 unsigned char loadb_mx_tbl[256] = {
05808     251, 175, 119, 215,  81,  14,  79, 191, 103,  49,
05809     181, 143, 186, 157,   0, 232,  31,  32,  55,  60,
05810     152,  58,  17, 237, 174,  70, 160, 144, 220,  90,
05811     57,  223,  59,   3,  18, 140, 111, 166, 203, 196,
05812     134, 243, 124,  95, 222, 179, 197,  65, 180,  48,
05813      36,  15, 107,  46, 233, 130, 165,  30, 123, 161,
05814     209,  23,  97,  16,  40,  91, 219,  61, 100,  10,
05815     210, 109, 250, 127,  22, 138,  29, 108, 244,  67,
05816     207,   9, 178, 204,  74,  98, 126, 249, 167, 116,
05817     34,   77, 193, 200, 121,   5,  20, 113,  71,  35,
05818     128,  13, 182,  94,  25, 226, 227, 199,  75,  27,
05819      41, 245, 230, 224,  43, 225, 177,  26, 155, 150,
05820     212, 142, 218, 115, 241,  73,  88, 105,  39, 114,
05821      62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
05822     154, 122,  12,  84,  82, 163,  44, 139, 228, 236,
05823     205, 242, 217,  11, 187, 146, 159,  64,  86, 239,
05824     195,  42, 106, 198, 118, 112, 184, 172,  87,   2,
05825     173, 117, 176, 229, 247, 253, 137, 185,  99, 164,
05826     102, 147,  45,  66, 231,  52, 141, 211, 194, 206,
05827     246, 238,  56, 110,  78, 248,  63, 240, 189,  93,
05828      92,  51,  53, 183,  19, 171,  72,  50,  33, 104,
05829     101,  69,   8, 252,  83, 120,  76, 135,  85,  54,
05830     202, 125, 188, 213,  96, 235, 136, 208, 162, 129,
05831     190, 132, 156,  38,  47,   1,   7, 254,  24,   4,
05832     216, 131,  89,  21,  28, 133,  37, 153, 149,  80,
05833     170,  68,   6, 169, 234, 151 };
05834 
05835 static unsigned char loadb_p_hash (const unsigned char *, unsigned);
05836 static unsigned char loadb_p_hash (const unsigned char *key, unsigned len)
05837 {
05838         unsigned char hash = len;
05839         int i;
05840         for(i = len; i > 0;  )
05841                 hash = loadb_mx_tbl [hash ^ (key [--i])];
05842         return hash;
05843 }
05844 
05845 int load_balance_mine (struct packet *packet, dhcp_failover_state_t *state)
05846 {
05847         struct option_cache *oc;
05848         struct data_string ds;
05849         unsigned char hbaix;
05850         int hm;
05851         u_int16_t ec; 
05852 
05853         ec = ntohs(packet->raw->secs);
05854 
05855 #if defined(SECS_BYTEORDER)
05856         /*
05857          * If desired check to see if the secs field may have been byte
05858          * swapped.  We assume it has if the high order byte isn't cleared
05859          * while the low order byte is cleared.  In this case we swap the
05860          * bytes and continue processing.
05861          */
05862         if ((ec > 255) && ((ec & 0xff) == 0)) {
05863                 ec = (ec >> 8) | (ec << 8);
05864         }
05865 #endif
05866 
05867         if (state->load_balance_max_secs < ec) {
05868                 return (1);
05869         }
05870 
05871         /* If we don't have a hash bucket array, we can't tell if this
05872            one's ours, so we assume it's not. */
05873         if (!state->hba)
05874                 return (0);
05875 
05876         oc = lookup_option(&dhcp_universe, packet->options,
05877                            DHO_DHCP_CLIENT_IDENTIFIER);
05878         memset(&ds, 0, sizeof ds);
05879         if (oc &&
05880             evaluate_option_cache(&ds, packet, NULL, NULL,
05881                                   packet->options, NULL,
05882                                   &global_scope, oc, MDL)) {
05883                 hbaix = loadb_p_hash(ds.data, ds.len);
05884 
05885                 data_string_forget(&ds, MDL);
05886         } else {
05887                 hbaix = loadb_p_hash(packet->raw->chaddr,
05888                                      packet->raw->hlen);
05889         }
05890 
05891         hm = state->hba[(hbaix >> 3) & 0x1F] & (1 << (hbaix & 0x07));
05892 
05893         if (state->i_am == primary)
05894                 return (hm);
05895         else
05896                 return (!hm);
05897 }
05898 
05899 /* The inverse of load_balance_mine ("load balance theirs").  We can't
05900  * use the regular load_balance_mine() and invert it because of the case
05901  * where there might not be an HBA, and we want to indicate false here
05902  * in this case only.
05903  */
05904 int
05905 peer_wants_lease(struct lease *lp)
05906 {
05907         dhcp_failover_state_t *state;
05908         unsigned char hbaix;
05909         int hm;
05910 
05911         if (!lp->pool)
05912                 return 0;
05913 
05914         state = lp->pool->failover_peer;
05915 
05916         if (!state || !state->hba)
05917                 return 0;
05918 
05919         if (lp->uid_len)
05920                 hbaix = loadb_p_hash(lp->uid, lp->uid_len);
05921         else if (lp->hardware_addr.hlen > 1)
05922                 /* Skip the first byte, which is the hardware type, and is
05923                  * not included during actual load balancing checks above
05924                  * since it is separate from the packet header chaddr field.
05925                  * The remainder of the hardware address should be identical
05926                  * to the chaddr contents.
05927                  */
05928                 hbaix = loadb_p_hash(lp->hardware_addr.hbuf + 1,
05929                                      lp->hardware_addr.hlen - 1);
05930         else /* impossible to categorize into LBA */
05931                 return 0;
05932 
05933         hm = state->hba[(hbaix >> 3) & 0x1F] & (1 << (hbaix & 0x07));
05934 
05935         if (state->i_am == primary)
05936                 return !hm;
05937         else
05938                 return hm;
05939 }
05940 
05941 /* This deals with what to do with bind updates when
05942    we're in the normal state 
05943 
05944    Note that tsfp had better be set from the latest bind update
05945    _before_ this function is called! */
05946 
05947 binding_state_t
05948 normal_binding_state_transition_check (struct lease *lease,
05949                                        dhcp_failover_state_t *state,
05950                                        binding_state_t binding_state,
05951                                        u_int32_t tsfp)
05952 {
05953         binding_state_t new_state;
05954 
05955         /* If there is no transition, it's no problem. */
05956         if (binding_state == lease -> binding_state)
05957                 return binding_state;
05958 
05959         switch (lease -> binding_state) {
05960               case FTS_FREE:
05961               case FTS_ABANDONED:
05962                 switch (binding_state) {
05963                       case FTS_ACTIVE:
05964                       case FTS_ABANDONED:
05965                       case FTS_BACKUP:
05966                       case FTS_EXPIRED:
05967                       case FTS_RELEASED:
05968                       case FTS_RESET:
05969                         /* If the lease was free, and our peer is primary,
05970                            then it can make it active, or abandoned, or
05971                            backup.    Abandoned is treated like free in
05972                            this case. */
05973                         if (state -> i_am == secondary)
05974                                 return binding_state;
05975 
05976                         /* Otherwise, it can't legitimately do any sort of
05977                            state transition.   Because the lease was free,
05978                            and the error has already been made, we allow the
05979                            peer to change its state anyway, but log a warning
05980                            message in hopes that the error will be fixed. */
05981                       case FTS_FREE: /* for compiler */
05982                         new_state = binding_state;
05983                         goto out;
05984 
05985                       default:
05986                         log_fatal ("Impossible case at %s:%d.", MDL);
05987                         return FTS_RESET;
05988                 }
05989               case FTS_ACTIVE:
05990                 /* The secondary can't change the state of an active
05991                    lease. */
05992                 if (state -> i_am == primary) {
05993                         /* Except that the client may send the DHCPRELEASE
05994                            to the secondary, and we have to accept that. */
05995                         if (binding_state == FTS_RELEASED)
05996                                 return binding_state;
05997                         new_state = lease -> binding_state;
05998                         goto out;
05999                 }
06000 
06001                 /* So this is only for transitions made by the primary: */
06002                 switch (binding_state) {
06003                       case FTS_FREE:
06004                       case FTS_BACKUP:
06005                         /* Can't set a lease to free or backup until the
06006                            peer agrees that it's expired. */
06007                         if (tsfp > cur_time) {
06008                                 new_state = lease -> binding_state;
06009                                 goto out;
06010                         }
06011                         return binding_state;
06012 
06013                       case FTS_EXPIRED:
06014                         /* XXX 65 should be the clock skew between the peers
06015                            XXX plus a fudge factor.   This code will result
06016                            XXX in problems if MCLT is really short or the
06017                            XXX max-lease-time is really short (less than the
06018                            XXX fudge factor. */
06019                         if (lease -> ends - 65 > cur_time) {
06020                                 new_state = lease -> binding_state;
06021                                 goto out;
06022                         }
06023 
06024                       case FTS_RELEASED:
06025                       case FTS_ABANDONED:
06026                       case FTS_RESET:
06027                       case FTS_ACTIVE:
06028                         return binding_state;
06029 
06030                       default:
06031                         log_fatal ("Impossible case at %s:%d.", MDL);
06032                         return FTS_RESET;
06033                 }
06034                 break;
06035               case FTS_EXPIRED:
06036                 switch (binding_state) {
06037                       case FTS_BACKUP:
06038                       case FTS_FREE:
06039                         /* Can't set a lease to free or backup until the
06040                            peer agrees that it's expired. */
06041                         if (tsfp > cur_time) {
06042                                 new_state = lease -> binding_state;
06043                                 goto out;
06044                         }
06045                         return binding_state;
06046 
06047                       case FTS_ACTIVE:
06048                       case FTS_RELEASED:
06049                       case FTS_ABANDONED:
06050                       case FTS_RESET:
06051                       case FTS_EXPIRED:
06052                         return binding_state;
06053 
06054                       default:
06055                         log_fatal ("Impossible case at %s:%d.", MDL);
06056                         return FTS_RESET;
06057                 }
06058               case FTS_RELEASED:
06059                 switch (binding_state) {
06060                       case FTS_FREE:
06061                       case FTS_BACKUP:
06062 
06063                         /* These are invalid state transitions - should we
06064                            prevent them? */
06065                       case FTS_EXPIRED:
06066                       case FTS_ABANDONED:
06067                       case FTS_RESET:
06068                       case FTS_ACTIVE:
06069                       case FTS_RELEASED:
06070                         return binding_state;
06071 
06072                       default:
06073                         log_fatal ("Impossible case at %s:%d.", MDL);
06074                         return FTS_RESET;
06075                 }
06076               case FTS_RESET:
06077                 switch (binding_state) {
06078                       case FTS_FREE:
06079                       case FTS_BACKUP:
06080                         /* Can't set a lease to free or backup until the
06081                            peer agrees that it's expired. */
06082                         if (tsfp > cur_time) {
06083                                 new_state = lease -> binding_state;
06084                                 goto out;
06085                         }
06086                         return binding_state;
06087 
06088                       case FTS_ACTIVE:
06089                       case FTS_EXPIRED:
06090                       case FTS_RELEASED:
06091                       case FTS_ABANDONED:
06092                       case FTS_RESET:
06093                         return binding_state;
06094 
06095                       default:
06096                         log_fatal ("Impossible case at %s:%d.", MDL);
06097                         return FTS_RESET;
06098                 }
06099               case FTS_BACKUP:
06100                 switch (binding_state) {
06101                       case FTS_ACTIVE:
06102                       case FTS_ABANDONED:
06103                       case FTS_EXPIRED:
06104                       case FTS_RELEASED:
06105                       case FTS_RESET:
06106                         /* If the lease was in backup, and our peer
06107                            is secondary, then it can make it active
06108                            or abandoned. */
06109                         if (state -> i_am == primary)
06110                                 return binding_state;
06111 
06112                         /* Either the primary or the secondary can
06113                            reasonably move a lease from the backup
06114                            state to the free state. */
06115                       case FTS_FREE:
06116                         return binding_state;
06117 
06118                       case FTS_BACKUP:
06119                         new_state = lease -> binding_state;
06120                         goto out;
06121 
06122                       default:
06123                         log_fatal ("Impossible case at %s:%d.", MDL);
06124                         return FTS_RESET;
06125                 }
06126 
06127               default:
06128                 log_fatal ("Impossible case at %s:%d.", MDL);
06129                 return FTS_RESET;
06130         }
06131       out:
06132         return new_state;
06133 }
06134 
06135 /* Determine whether the state transition is okay when we're potentially
06136    in conflict with the peer. */
06137 binding_state_t
06138 conflict_binding_state_transition_check (struct lease *lease,
06139                                          dhcp_failover_state_t *state,
06140                                          binding_state_t binding_state,
06141                                          u_int32_t tsfp)
06142 {
06143         binding_state_t new_state;
06144 
06145         /* If there is no transition, it's no problem. */
06146         if (binding_state == lease -> binding_state)
06147                 new_state = binding_state;
06148         else {
06149                 switch (lease -> binding_state) {
06150                         /* If we think the lease is not in use, then the
06151                            state into which the partner put it is just fine,
06152                            whatever it is. */
06153                       case FTS_FREE:
06154                       case FTS_ABANDONED:
06155                       case FTS_EXPIRED:
06156                       case FTS_RELEASED:
06157                       case FTS_RESET:
06158                       case FTS_BACKUP:
06159                         new_state = binding_state;
06160                         break;
06161 
06162                         /* If we think the lease *is* in use, then we're not
06163                            going to take the partner's change if the partner
06164                            thinks it's free. */
06165                       case FTS_ACTIVE:
06166                         switch (binding_state) {
06167                               case FTS_FREE:
06168                               case FTS_BACKUP:
06169                                 new_state = lease -> binding_state;
06170                                 break;
06171 
06172                               case FTS_EXPIRED:
06173                                 /* If we don't agree about expiry, it's
06174                                  * invalid.  65 should allow for max
06175                                  * clock skew (60) plus some fudge.
06176                                  * XXX: should we refetch cur_time?
06177                                  */
06178                                 if ((lease->ends - 65) > cur_time)
06179                                         new_state = lease->binding_state;
06180                                 else
06181                                         new_state = binding_state;
06182                                 break;
06183 
06184                                 /* RELEASED, RESET, and ABANDONED indicate
06185                                  * that our partner has information about
06186                                  * this lease that we did not witness.  Our
06187                                  * partner wins.
06188                                  */
06189                               case FTS_RELEASED:
06190                               case FTS_RESET:
06191                               case FTS_ABANDONED:
06192                                 new_state = binding_state;
06193                                 break;
06194 
06195                               default:
06196                                 log_fatal ("Impossible case at %s:%d.", MDL);
06197                                 return FTS_RESET;
06198                         }
06199                         break;
06200 
06201                       default:
06202                         log_fatal ("Impossible case at %s:%d.", MDL);
06203                         return FTS_RESET;
06204                 }
06205         }
06206         return new_state;
06207 }
06208 
06209 /* We can reallocate a lease under the following circumstances:
06210 
06211    (1) It belongs to us - it's FTS_FREE, and we're primary, or it's
06212        FTS_BACKUP, and we're secondary.
06213    (2) We're in partner_down, and the lease is not active, and we
06214        can be sure that the other server didn't make it active.
06215        We can only be sure that the server didn't make it active
06216        when we are in the partner_down state and one of the following
06217        two conditions holds:
06218        (a) in the case that the time sent from the peer is earlier than
06219            the time we entered the partner_down state, at least MCLT has
06220            gone by since we entered partner_down, or
06221        (b) in the case that the time sent from the peer is later than
06222            the time when we entered partner_down, the current time is
06223            later than the time sent from the peer by at least MCLT. */
06224 
06225 int lease_mine_to_reallocate (struct lease *lease)
06226 {
06227         dhcp_failover_state_t *peer;
06228 
06229         if (lease && lease->pool &&
06230             (peer = lease->pool->failover_peer)) {
06231                 /*
06232                  * In addition to the normal rules governing wether a server
06233                  * is allowed to operate changes on a lease, the server is
06234                  * allowed to operate on a lease from the standpoint of the
06235                  * most conservative guess of the peer's state for this lease.
06236                  */
06237                 switch (lease->binding_state) {
06238                       case FTS_ACTIVE:
06239                         /* ACTIVE leases may not be reallocated. */
06240                         return 0;
06241 
06242                       case FTS_FREE:
06243                       case FTS_ABANDONED:
06244                         /* FREE leases may only be allocated by the primary,
06245                          * unless the secondary is acting in partner_down
06246                          * state and stos+mclt or tsfp+mclt has expired,
06247                          * whichever is greater.
06248                          *
06249                          * ABANDONED are treated the same as FREE for all
06250                          * purposes here.  Note that servers will only try
06251                          * for ABANDONED leases as a last resort anyway.
06252                          */
06253                         if (peer -> i_am == primary)
06254                                 return 1;
06255 
06256                         return(peer->service_state == service_partner_down &&
06257                                ((lease->tsfp < peer->me.stos) ?
06258                                 (peer->me.stos + peer->mclt < cur_time) :
06259                                 (lease->tsfp + peer->mclt < cur_time)));
06260 
06261                       case FTS_RELEASED:
06262                       case FTS_EXPIRED:
06263                         /*
06264                          * These leases are generally untouchable until the
06265                          * peer acknowledges their state change.  However, as
06266                          * this is impossible if the peer is offline, the
06267                          * failover protocol permits an 'optimization' to
06268                          * rewind the lease to a previous state that the server
06269                          * is allowed to operate on, if that was the state that
06270                          * was last acknowledged by the peer.
06271                          *
06272                          * So if a lease was free, was allocated by this
06273                          * server, and expired without ever being transmitted
06274                          * to the peer, it can be returned to free and given
06275                          * to any new client legally.
06276                          */
06277                         if ((peer->i_am == primary) &&
06278                             (lease->rewind_binding_state == FTS_FREE))
06279                                 return 1;
06280                         if ((peer->i_am == secondary) &&
06281                             (lease->rewind_binding_state == FTS_BACKUP))
06282                                 return 1;
06283 
06284                         /* FALL THROUGH (released, expired, reset) */
06285                       case FTS_RESET:
06286                         /*
06287                          * Released, expired, and reset leases go onto the
06288                          * 'expired' queue all together.  Upon entry into
06289                          * partner-down state, this queue of leases has their
06290                          * tsfp values modified to equal stos+mclt, the point
06291                          * at which the server is allowed to remove them from
06292                          * these transitional states.
06293                          *
06294                          * Note that although tsfp has been possibly extended
06295                          * past the actual tsfp we received from the peer, we
06296                          * don't have to take any special action.  Since tsfp
06297                          * will be equal to the current time when the lease
06298                          * transitions to free, tsfp will not be used to grant
06299                          * lease-times longer than the MCLT to clients, which
06300                          * is the only danger for this sort of modification.
06301                          */
06302                         return((peer->service_state == service_partner_down) &&
06303                                (lease->tsfp < cur_time));
06304 
06305                       case FTS_BACKUP:
06306                         /* Only the secondary may allocate BACKUP leases,
06307                          * unless in partner_down state in which case at
06308                          * least TSFP+MCLT or STOS+MCLT must have expired,
06309                          * whichever is greater.
06310                          */
06311                         if (peer->i_am == secondary)
06312                                 return 1;
06313 
06314                         return((peer->service_state == service_partner_down) &&
06315                                ((lease->tsfp < peer->me.stos) ?
06316                                 (peer->me.stos + peer->mclt < cur_time) :
06317                                 (lease->tsfp + peer->mclt < cur_time)));
06318 
06319                       default:
06320                         /* All lease states appear above. */
06321                         log_fatal("Impossible case at %s:%d.", MDL);
06322                         break;
06323                 }
06324                 return 0;
06325         }
06326         if (lease)
06327                 return(lease->binding_state == FTS_FREE ||
06328                        lease->binding_state == FTS_BACKUP);
06329         else
06330                 return 0;
06331 }
06332 
06333 static isc_result_t failover_message_reference (failover_message_t **mp,
06334                                                 failover_message_t *m,
06335                                                 const char *file, int line)
06336 {
06337         *mp = m;
06338         m -> refcnt++;
06339         return ISC_R_SUCCESS;
06340 }
06341 
06342 static isc_result_t failover_message_dereference (failover_message_t **mp,
06343                                                   const char *file, int line)
06344 {
06345         failover_message_t *m;
06346         m = (*mp);
06347         m -> refcnt--;
06348         if (m -> refcnt == 0) {
06349                 if (m -> next)
06350                         failover_message_dereference (&m -> next,
06351                                                       file, line);
06352                 if (m -> chaddr.data)
06353                         dfree (m -> chaddr.data, file, line);
06354                 if (m -> client_identifier.data)
06355                         dfree (m -> client_identifier.data, file, line);
06356                 if (m -> hba.data)
06357                         dfree (m -> hba.data, file, line);
06358                 if (m -> message.data)
06359                         dfree (m -> message.data, file, line);
06360                 if (m -> relationship_name.data)
06361                         dfree (m -> relationship_name.data, file, line);
06362                 if (m -> reply_options.data)
06363                         dfree (m -> reply_options.data, file, line);
06364                 if (m -> request_options.data)
06365                         dfree (m -> request_options.data, file, line);
06366                 if (m -> vendor_class.data)
06367                         dfree (m -> vendor_class.data, file, line);
06368                 if (m -> vendor_options.data)
06369                         dfree (m -> vendor_options.data, file, line);
06370                 if (m -> ddns.data)
06371                         dfree (m -> ddns.data, file, line);
06372                 dfree (*mp, file, line);
06373         }
06374         *mp = 0;
06375         return ISC_R_SUCCESS;
06376 }
06377 
06378 OMAPI_OBJECT_ALLOC (dhcp_failover_state, dhcp_failover_state_t,
06379                     dhcp_type_failover_state)
06380 OMAPI_OBJECT_ALLOC (dhcp_failover_listener, dhcp_failover_listener_t,
06381                     dhcp_type_failover_listener)
06382 OMAPI_OBJECT_ALLOC (dhcp_failover_link, dhcp_failover_link_t,
06383                     dhcp_type_failover_link)
06384 #endif /* defined (FAILOVER_PROTOCOL) */
06385 
06386 const char *binding_state_print (enum failover_state state)
06387 {
06388         switch (state) {
06389               case FTS_FREE:
06390                 return "free";
06391                 break;
06392 
06393               case FTS_ACTIVE:
06394                 return "active";
06395                 break;
06396 
06397               case FTS_EXPIRED:
06398                 return "expired";
06399                 break;
06400 
06401               case FTS_RELEASED:
06402                 return "released";
06403                 break;
06404 
06405               case FTS_ABANDONED:
06406                 return "abandoned";
06407                 break;
06408 
06409               case FTS_RESET:
06410                 return "reset";
06411                 break;
06412 
06413               case FTS_BACKUP:
06414                 return "backup";
06415                 break;
06416 
06417               default:
06418                 return "unknown";
06419                 break;
06420         }
06421 }

Generated on 5 Apr 2014 for ISC DHCP by  doxygen 1.6.1