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

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