Annotation of embedaddon/dhcp/server/failover.c, revision 1.1

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>