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

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

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