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