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