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