diff options
author | David Hankins <dhankins@isc.org> | 2006-04-27 17:26:42 +0000 |
---|---|---|
committer | David Hankins <dhankins@isc.org> | 2006-04-27 17:26:42 +0000 |
commit | a55ccdd0371568e01a6bbab7b99e427ab66e1aef (patch) | |
tree | 057d9ab6464115f011246b072b43ce1d04993e1c /server/failover.c | |
parent | 91e17fc3aa6f6f96063b1eef7d877b779be5683c (diff) | |
download | isc-dhcp-a55ccdd0371568e01a6bbab7b99e427ab66e1aef.tar.gz |
- Support for 'reserved' (psuedo-static) and BOOTP leases via failover
was introduced. [ISC-Bugs #13672]
- The failover implementation was updated to comply with revision 12 of
the protocol draft. [ISC-Bugs #13672]
Diffstat (limited to 'server/failover.c')
-rw-r--r-- | server/failover.c | 771 |
1 files changed, 476 insertions, 295 deletions
diff --git a/server/failover.c b/server/failover.c index cc42d5d5..bffd06f3 100644 --- a/server/failover.c +++ b/server/failover.c @@ -34,7 +34,7 @@ #ifndef lint static char copyright[] = -"$Id: failover.c,v 1.59 2006/02/24 23:16:32 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n"; +"$Id: failover.c,v 1.60 2006/04/27 17:26:42 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -410,9 +410,10 @@ isc_result_t dhcp_failover_link_signal (omapi_object_t *h, /* Get the length: */ omapi_connection_get_uint16 (c, &link -> imsg_len); link -> imsg_count = 0; /* Bytes read. */ - - /* Maximum of 2048 bytes in any failover message. */ - if (link -> imsg_len > DHCP_FAILOVER_MAX_MESSAGE_SIZE) { + + /* Ensure the message is of valid length. */ + if (link->imsg_len < DHCP_FAILOVER_MIN_MESSAGE_SIZE || + link->imsg_len > DHCP_FAILOVER_MAX_MESSAGE_SIZE) { status = ISC_R_UNEXPECTED; goto dhcp_flink_fail; } @@ -477,13 +478,20 @@ isc_result_t dhcp_failover_link_signal (omapi_object_t *h, if (link -> imsg -> type == FTM_CONNECT) { const char *errmsg; int reason; + + if (!(link->imsg->options_present & + FTB_RELATIONSHIP_NAME)) { + errmsg = "missing relationship-name"; + reason = FTR_INVALID_PARTNER; + goto badconnect; + } + /* See if we can find a failover_state object that matches this connection. This message should only be received by a secondary from a primary. */ for (s = failover_states; s; s = s -> next) { - if (dhcp_failover_state_match - (s, (u_int8_t *)&link -> imsg -> server_addr, - sizeof link -> imsg -> server_addr)) + if (dhcp_failover_state_match_by_name(s, + &link->imsg->relationship_name)) state = s; } @@ -496,16 +504,8 @@ isc_result_t dhcp_failover_link_signal (omapi_object_t *h, badconnect: /* XXX Send a refusal message first? XXX Look in protocol spec for guidance. */ - log_error ("Failover CONNECT from %u.%u.%u.%u: %s", - ((u_int8_t *) - (&link -> imsg -> server_addr)) [0], - ((u_int8_t *) - (&link -> imsg -> server_addr)) [1], - ((u_int8_t *) - (&link -> imsg -> server_addr)) [2], - ((u_int8_t *) - (&link -> imsg -> server_addr)) [3], - errmsg); + log_error ("Failover CONNECT from %s: %s", + state? state->name : "unknown", errmsg); dhcp_failover_send_connectack ((omapi_object_t *)link, state, reason, errmsg); @@ -1260,8 +1260,7 @@ isc_result_t dhcp_failover_state_signal (omapi_object_t *o, if (link -> imsg -> options_present & FTB_MAX_UNACKED) state -> partner.max_flying_updates = link -> imsg -> max_unacked; - if (link -> imsg -> options_present & - FTB_RECEIVE_TIMER) + if (link -> imsg -> options_present & FTB_RECEIVE_TIMER) state -> partner.max_response_delay = link -> imsg -> receive_timer; state -> mclt = link -> imsg -> mclt; @@ -1275,17 +1274,17 @@ isc_result_t dhcp_failover_state_signal (omapi_object_t *o, cancel_timeout (dhcp_failover_link_startup_timeout, link); - if (link -> imsg -> reject_reason) { - log_error ("Failover CONNECT to %u.%u.%u.%u%s%s", - ((u_int8_t *) - (&link -> imsg -> server_addr)) [0], - ((u_int8_t *) - (&link -> imsg -> server_addr)) [1], - ((u_int8_t *) - (&link -> imsg -> server_addr)) [2], - ((u_int8_t *) - (&link -> imsg -> server_addr)) [3], - " rejected: ", + if (!(link->imsg->options_present & + FTB_RELATIONSHIP_NAME)) { + errmsg = "missing relationship-name"; + reason = FTR_INVALID_PARTNER; + goto badconnectack; + } + + if (link->imsg->options_present & FTB_REJECT_REASON) { + /* XXX: add message option to text output. */ + log_error ("Failover CONNECT to %s rejected: %s", + state ? state->name : "unknown", (dhcp_failover_reject_reason_print (link -> imsg -> reject_reason))); /* XXX print message from peer if peer sent message. */ @@ -1293,23 +1292,13 @@ isc_result_t dhcp_failover_state_signal (omapi_object_t *o, return ISC_R_SUCCESS; } - if (!dhcp_failover_state_match - (state, - (u_int8_t *)&link -> imsg -> server_addr, - sizeof link -> imsg -> server_addr)) { + if (!dhcp_failover_state_match_by_name(state, + &link->imsg->relationship_name)) { errmsg = "unknown server"; reason = FTR_INVALID_PARTNER; badconnectack: - log_error ("Failover CONNECTACK from %u.%u.%u.%u: %s", - ((u_int8_t *) - (&link -> imsg -> server_addr)) [0], - ((u_int8_t *) - (&link -> imsg -> server_addr)) [1], - ((u_int8_t *) - (&link -> imsg -> server_addr)) [2], - ((u_int8_t *) - (&link -> imsg -> server_addr)) [3], - errmsg); + log_error ("Failover CONNECTACK from %s: %s", + state ? state->name : "unknown", errmsg); dhcp_failover_send_disconnect ((omapi_object_t *)link, reason, errmsg); omapi_disconnect (link -> outer, 0); @@ -1373,16 +1362,8 @@ isc_result_t dhcp_failover_state_signal (omapi_object_t *o, (tvunref_t)dhcp_failover_state_dereference); } else if (link -> imsg -> type == FTM_DISCONNECT) { if (link -> imsg -> reject_reason) { - log_error ("Failover DISCONNECT from %u.%u.%u.%u%s%s", - ((u_int8_t *) - (&link -> imsg -> server_addr)) [0], - ((u_int8_t *) - (&link -> imsg -> server_addr)) [1], - ((u_int8_t *) - (&link -> imsg -> server_addr)) [2], - ((u_int8_t *) - (&link -> imsg -> server_addr)) [3], - ": ", + log_error ("Failover DISCONNECT from %s: %s", + state ? state->name : "unknown", (dhcp_failover_reject_reason_print (link -> imsg -> reject_reason))); } @@ -1459,10 +1440,18 @@ isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *state, switch (state -> me.state == startup ? state -> saved_state : state -> me.state) { - case resolution_interrupted: - case partner_down: + /* In these situations, we remain in the current + * state, or if in startup enter those states. + */ case communications_interrupted: + case conflict_done: + case partner_down: + case paused: case recover: + case recover_done: + case recover_wait: + case resolution_interrupted: + case shut_down: /* Already in the right state? */ if (state -> me.state == startup) return (dhcp_failover_set_state @@ -1480,7 +1469,9 @@ isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *state, case unknown_state: return dhcp_failover_set_state (state, resolution_interrupted); - case startup: + + default: + log_fatal("Impossible case at %s:%d.", MDL); break; /* can't happen. */ } } else if (!strcmp (name, "connect")) { @@ -1494,6 +1485,7 @@ isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *state, return dhcp_failover_set_state (state, potential_conflict); + case conflict_done: case partner_down: case potential_conflict: case normal: @@ -1505,6 +1497,10 @@ isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *state, case startup: case recover_wait: return dhcp_failover_send_state (state); + + default: + log_fatal("Impossible case at %s:%d.", MDL); + break; } } else if (!strcmp (name, "startup")) { dhcp_failover_set_state (state, startup); @@ -1514,10 +1510,17 @@ isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *state, case communications_interrupted: case partner_down: case resolution_interrupted: + case paused: + case startup: + case shut_down: + case conflict_done: return ISC_R_SUCCESS; case normal: case recover: + case recover_wait: + case recover_done: + case unknown_state: return dhcp_failover_set_state (state, communications_interrupted); @@ -1525,13 +1528,9 @@ isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *state, return dhcp_failover_set_state (state, resolution_interrupted); - case unknown_state: - return dhcp_failover_set_state - (state, communications_interrupted); - default: - return dhcp_failover_set_state - (state, resolution_interrupted); + log_fatal("Impossible case at %s:%d.", MDL); + break; } } return ISC_R_INVALIDARG; @@ -1562,6 +1561,7 @@ isc_result_t dhcp_failover_set_service_state (dhcp_failover_state_t *state) case resolution_interrupted: case potential_conflict: + case conflict_done: state -> service_state = not_responding; state -> nrr = " (resolving conflicts)"; break; @@ -1595,6 +1595,10 @@ isc_result_t dhcp_failover_set_service_state (dhcp_failover_state_t *state) state -> service_state = service_startup; state -> nrr = " (startup)"; break; + + default: + log_fatal("Impossible case at %s:%d.\n", MDL); + break; } /* Some peer states can require us not to respond, even if our @@ -1604,12 +1608,14 @@ isc_result_t dhcp_failover_set_service_state (dhcp_failover_state_t *state) switch (state -> partner.state) { case partner_down: state -> service_state = not_responding; - state -> nrr = " (recovering)"; + state -> nrr = " (peer demands: recovering)"; break; case potential_conflict: + case conflict_done: + case resolution_interrupted: state -> service_state = not_responding; - state -> nrr = " (resolving conflicts)"; + state -> nrr = " (peer demands: resolving conflicts)"; break; /* Other peer states don't affect our behaviour. */ @@ -1630,11 +1636,14 @@ isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *state, struct shared_network *s; struct lease *l; - /* First make the transition out of the current state. */ + /* If we're in certain states where we're sending updates, and the peer + * state changes, we need to re-schedule any pending updates just to + * be on the safe side. This results in retransmission. + */ switch (state -> me.state) { case normal: - /* Any updates that haven't been acked yet, we have to - resend, just in case. */ + case potential_conflict: + case partner_down: if (state -> ack_queue_tail) { struct lease *lp; @@ -1642,7 +1651,7 @@ isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *state, for (lp = state -> ack_queue_head; lp; lp = lp -> next_pending) lp -> flags = ((lp -> flags & ~ON_ACK_QUEUE) | ON_UPDATE_QUEUE); - + /* Now hook the ack queue to the beginning of the update queue. */ if (state -> update_queue_head) { @@ -1666,17 +1675,10 @@ isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *state, lease_dereference (&state -> ack_queue_head, MDL); state -> cur_unacked_updates = 0; } + /* We will re-queue a timeout later, if applicable. */ cancel_timeout (dhcp_failover_keepalive, state); break; - case recover: - case recover_wait: - case recover_done: - case potential_conflict: - case partner_down: - case communications_interrupted: - case resolution_interrupted: - case startup: default: break; } @@ -1774,6 +1776,10 @@ isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *state, break; case recover: + /* XXX: We're supposed to calculate if updreq or updreqall is + * needed. In theory, we should only have to updreqall if we + * are positive we lost our stable storage. + */ if (state -> link_to_peer) dhcp_failover_send_update_request_all (state); break; @@ -1805,7 +1811,7 @@ isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *state, } } break; - + default: break; @@ -1823,7 +1829,7 @@ isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state, isc_result_t status; new_state = msg -> server_state; - startupp = (msg -> server_flags & FTF_STARTUP) ? 1 : 0; + startupp = (msg -> server_flags & FTF_SERVER_STARTUP) ? 1 : 0; if (state -> partner.state == new_state && state -> me.state) { switch (state -> me.state) { @@ -1846,6 +1852,11 @@ isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state, case communications_interrupted: case resolution_interrupted: case recover: + case conflict_done: + break; + + default: + log_fatal("Impossible case at %s:%d.", MDL); break; } } @@ -1865,23 +1876,43 @@ isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state, state -> name); } + /* Quickly validate the new state as being one of the 13 known + * states. + */ + switch (new_state) { + case unknown_state: + case startup: + case normal: + case communications_interrupted: + case partner_down: + case potential_conflict: + case recover: + case paused: + case shut_down: + case recover_done: + case resolution_interrupted: + case conflict_done: + case recover_wait: + break; + + default: + log_error("failover peer %s: Invalid state: %d", state->name, + new_state); + dhcp_failover_set_state(state, shut_down); + return ISC_R_SUCCESS; + } + /* Do any state transitions that are required as a result of the peer's state transition. */ switch (state -> me.state == startup ? state -> saved_state : state -> me.state) { - case startup: /* can't happen. */ - break; - case normal: switch (new_state) { case normal: dhcp_failover_state_pool_check (state); break; - case communications_interrupted: - break; - case partner_down: if (state -> me.state == startup) dhcp_failover_set_state (state, recover); @@ -1892,18 +1923,17 @@ isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state, case potential_conflict: case resolution_interrupted: + case conflict_done: /* None of these transitions should ever occur. */ + log_error("Peer %s: Invalid state transition %s " + "to %s.", state->name, + dhcp_failover_state_name_print(previous_state), + dhcp_failover_state_name_print(new_state)); dhcp_failover_set_state (state, shut_down); break; - - case recover: - dhcp_failover_set_state (state, partner_down); - break; + case recover: case shut_down: - /* XXX This one is specified, but it's specified in - XXX the documentation for the shut_down state, - XXX not the normal state. */ dhcp_failover_set_state (state, partner_down); break; @@ -1912,13 +1942,10 @@ isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state, communications_interrupted); break; - case recover_wait: - case recover_done: - /* We probably don't need to do anything here. */ - break; - - case unknown_state: - case startup: + default: + /* recover_wait, recover_done, unknown_state, startup, + * communications_interrupted + */ break; } break; @@ -1937,6 +1964,7 @@ isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state, case potential_conflict: case resolution_interrupted: + case conflict_done: case normal: dhcp_failover_set_state (state, potential_conflict); break; @@ -1953,7 +1981,7 @@ isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state, * the reason, we send an UPDREQALL. So, it makes * the most sense to stick to that until something * better is done. - * Furthermore, we only went to send the update + * Furthermore, we only want to send the update * request if we are not in startup state. */ if (state -> me.state == recover) @@ -1968,16 +1996,10 @@ isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state, break; /* We can't really do anything in this case. */ - case paused: - break; - - /* We should have asked for an update already. */ - case recover_done: - case recover_wait: - break; - - case unknown_state: - case startup: + default: + /* paused, recover_done, recover_wait, unknown_state, + * startup. + */ break; } break; @@ -1985,9 +2007,19 @@ isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state, case potential_conflict: switch (new_state) { case normal: - if (previous_state == potential_conflict && - state -> i_am == secondary) + /* This is an illegal transition. */ + log_error("Peer %s moves to normal during conflict " + "resolution - panic, shutting down.", + state->name); + dhcp_failover_set_state(state, shut_down); + break; + + case conflict_done: + if (previous_state == potential_conflict) dhcp_failover_send_update_request (state); + else + log_error("Peer %s: Unexpected move to " + "conflict-done.", state->name); break; case recover_done: @@ -2007,12 +2039,28 @@ isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state, dhcp_failover_set_state (state, partner_down); break; - case unknown_state: - case startup: + default: + /* unknown_state, startup */ break; } break; + case conflict_done: + switch (new_state) { + case normal: + case shut_down: + dhcp_failover_set_state(state, new_state); + break; + + default: + log_fatal("Peer %s: Invalid attempt to move from %s " + "to %s while local state is conflict-done.", + state->name, + dhcp_failover_state_name_print(previous_state), + dhcp_failover_state_name_print(new_state)); + } + break; + case partner_down: /* Take no action if other server is starting up. */ if (startupp) @@ -2033,16 +2081,12 @@ isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state, case partner_down: case communications_interrupted: case resolution_interrupted: + case conflict_done: dhcp_failover_set_state (state, potential_conflict); break; - /* These don't change anything. */ - case shut_down: - case paused: - break; - - case unknown_state: - case startup: + default: + /* shut_down, paused, unknown_state, startup */ break; } break; @@ -2073,6 +2117,7 @@ isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state, case potential_conflict: case partner_down: case resolution_interrupted: + case conflict_done: dhcp_failover_set_state (state, potential_conflict); break; @@ -2080,8 +2125,8 @@ isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state, dhcp_failover_set_state (state, partner_down); break; - case unknown_state: - case startup: + default: + /* unknown_state, startup */ break; } break; @@ -2094,6 +2139,7 @@ isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state, case partner_down: case communications_interrupted: case resolution_interrupted: + case conflict_done: case recover_done: case recover_wait: dhcp_failover_set_state (state, potential_conflict); @@ -2103,37 +2149,34 @@ isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state, dhcp_failover_set_state (state, partner_down); break; - case paused: - break; - - case unknown_state: - case startup: + default: + /* paused, unknown_state, startup */ break; } break; + /* Make no transitions while in recover_wait...just wait. */ + case recover_wait: + break; + case recover_done: switch (new_state) { - case normal: case recover_done: + log_error("Both servers have entered recover-done!"); + case normal: dhcp_failover_set_state (state, normal); break; - case potential_conflict: - case partner_down: - case communications_interrupted: - case resolution_interrupted: - case paused: - case recover: - case recover_wait: - break; - case shut_down: dhcp_failover_set_state (state, partner_down); break; - case unknown_state: - case startup: + default: + /* potential_conflict, partner_down, + * communications_interrupted, resolution_interrupted, + * paused, recover, recover_wait, unknown_state, + * startup. + */ break; } break; @@ -2145,12 +2188,14 @@ isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state, case paused: break; - /* We still have to wait... */ - case recover_wait: + /* XXX: Shouldn't this be a fatal condition? */ + case unknown_state: + break; + + default: + log_fatal("Impossible condition at %s:%d.", MDL); break; - case unknown_state: - break; } /* If we didn't make a transition out of startup as a result of @@ -3185,8 +3230,26 @@ int dhcp_failover_state_match (dhcp_failover_state_t *state, return 0; } +int +dhcp_failover_state_match_by_name(state, name) + dhcp_failover_state_t *state; + failover_option_t *name; +{ + if ((strlen(state->name) == name->count) && + (memcmp(state->name, name->data, name->count) == 0)) + return 1; + + return 0; +} + const char *dhcp_failover_reject_reason_print (int reason) { + static char resbuf[sizeof("Undefined-255: This reason code is not defined " + "in the protocol standard.")]; + + if ((reason > 0xff) || (reason < 0)) + return "Reason code out of range."; + switch (reason) { case FTR_ILLEGAL_IP_ADDR: return "Illegal IP address (not part of any address pool)."; @@ -3230,9 +3293,6 @@ const char *dhcp_failover_reject_reason_print (int reason) case FTR_VERSION_MISMATCH: return "Protocol version mismatch."; - case FTR_MISSING_BIND_INFO: - return "Missing binding information."; - case FTR_OUTDATED_BIND_INFO: return "Outdated binding information."; @@ -3245,9 +3305,22 @@ const char *dhcp_failover_reject_reason_print (int reason) case FTR_HBA_CONFLICT: return "Hash bucket assignment conflict."; - default: + case FTR_IP_NOT_RESERVED: + return "IP not reserved on this server."; + + case FTR_IP_DIGEST_FAILURE: + return "Message digest failed to compare."; + + case FTR_IP_MISSING_DIGEST: + return "Missing message digest."; + case FTR_UNKNOWN: - return "Unknown: Error occurred but does not match any reason code."; + return "Unknown Error."; + + default: + sprintf(resbuf, "Undefined-%d: This reason code is not defined in the " + "protocol standard.", reason); + return resbuf; } } @@ -3295,6 +3368,11 @@ const char *dhcp_failover_state_name_print (enum failover_state state) const char *dhcp_failover_message_name (unsigned type) { + static char messbuf[sizeof("unknown-message-255")]; + + if (type > 0xff) + return "invalid-message"; + switch (type) { case FTM_POOLREQ: return "pool-request"; @@ -3333,99 +3411,109 @@ const char *dhcp_failover_message_name (unsigned type) return "disconnect"; default: - return "<unknown message type>"; + sprintf(messbuf, "unknown-message-%u", type); + return messbuf; } } const char *dhcp_failover_option_name (unsigned type) { + static char optbuf[sizeof("unknown-option-65535")]; + + if (type > 0xffff) + return "invalid-option"; + switch (type) { - case FTO_BINDING_STATUS: - return "binding-status"; + case FTO_ADDRESSES_TRANSFERRED: + return "addresses-transferred"; - case FTO_ASSIGNED_IP_ADDRESS: + case FTO_ASSIGNED_IP_ADDRESS: return "assigned-ip-address"; - case FTO_SERVER_ADDR: - return "server-addr"; - - case FTO_ADDRESSES_TRANSFERRED: - return "addresses-transferred"; + case FTO_BINDING_STATUS: + return "binding-status"; - case FTO_CLIENT_IDENTIFIER: + case FTO_CLIENT_IDENTIFIER: return "client-identifier"; - case FTO_CHADDR: + case FTO_CHADDR: return "chaddr"; - case FTO_DDNS: - return "ddns"; + case FTO_CLTT: + return "cltt"; - case FTO_REJECT_REASON: - return "reject-reason"; + case FTO_DDNS: + return "ddns"; - case FTO_MESSAGE: - return "message"; + case FTO_DELAYED_SERVICE: + return "delayed-service"; - case FTO_MCLT: - return "mclt"; + case FTO_HBA: + return "hba"; - case FTO_VENDOR_CLASS: - return "vendor-class"; + case FTO_IP_FLAGS: + return "ip-flags"; - case FTO_LEASE_EXPIRY: + case FTO_LEASE_EXPIRY: return "lease-expiry"; - case FTO_POTENTIAL_EXPIRY: - return "potential-expiry"; + case FTO_MAX_UNACKED: + return "max-unacked"; - case FTO_GRACE_EXPIRY: - return "grace-expiry"; + case FTO_MCLT: + return "mclt"; - case FTO_CLTT: - return "cltt"; + case FTO_MESSAGE: + return "message"; - case FTO_STOS: - return "stos"; + case FTO_MESSAGE_DIGEST: + return "message-digest"; - case FTO_SERVER_STATE: - return "server-state"; + case FTO_POTENTIAL_EXPIRY: + return "potential-expiry"; - case FTO_SERVER_FLAGS: - return "server-flags"; + case FTO_PROTOCOL_VERSION: + return "protocol-version"; - case FTO_VENDOR_OPTIONS: - return "vendor-options"; + case FTO_RECEIVE_TIMER: + return "receive-timer"; - case FTO_MAX_UNACKED: - return "max-unacked"; + case FTO_REJECT_REASON: + return "reject-reason"; - case FTO_RECEIVE_TIMER: - return "receive-timer"; + case FTO_RELATIONSHIP_NAME: + return "relationship-name"; - case FTO_HBA: - return "hba"; + case FTO_REPLY_OPTIONS: + return "reply-options"; - case FTO_MESSAGE_DIGEST: - return "message-digest"; + case FTO_REQUEST_OPTIONS: + return "request-options"; - case FTO_PROTOCOL_VERSION: - return "protocol-version"; + case FTO_SERVER_FLAGS: + return "server-flags"; - case FTO_TLS_REQUEST: - return "tls-request"; + case FTO_SERVER_STATE: + return "server-state"; + + case FTO_STOS: + return "stos"; - case FTO_TLS_REPLY: + case FTO_TLS_REPLY: return "tls-reply"; - case FTO_REQUEST_OPTIONS: - return "request-options"; + case FTO_TLS_REQUEST: + return "tls-request"; - case FTO_REPLY_OPTIONS: - return "reply-options"; + case FTO_VENDOR_CLASS: + return "vendor-class"; - default: - return "<unknown option>"; + case FTO_VENDOR_OPTIONS: + return "vendor-options"; + + default: + sprintf(optbuf, "unknown-option-%u", type); + return optbuf; } } @@ -3696,7 +3784,7 @@ failover_option_t *dhcp_failover_make_option (unsigned code, isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link, omapi_object_t *connection, - int msg_type, ...) + int msg_type, u_int32_t xid, ...) { unsigned count = 0; unsigned size = 0; @@ -3710,7 +3798,7 @@ isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link, /* Run through the argument list once to compute the length of the option portion of the message. */ - va_start (list, msg_type); + va_start (list, xid); while ((option = va_arg (list, failover_option_t *))) { if (option != &skip_failover_option) size += option -> count; @@ -3727,7 +3815,7 @@ isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link, } else opbuf = (unsigned char *)0; - va_start (list, msg_type); + va_start (list, xid); while ((option = va_arg (list, failover_option_t *))) { if (option == &skip_failover_option) continue; @@ -3741,6 +3829,7 @@ isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link, dfree (option, MDL); } } + va_end(list); if (bad_option) return ISC_R_INVALIDARG; @@ -3770,11 +3859,10 @@ isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link, goto err; /* Transaction ID. */ - status = omapi_connection_put_uint32 (connection, link -> xid++); + status = omapi_connection_put_uint32(connection, xid); if (status != ISC_R_SUCCESS) goto err; - /* Payload. */ if (opbuf) { status = omapi_connection_copyin (connection, opbuf, size); @@ -3862,7 +3950,7 @@ void dhcp_failover_send_contact (void *vstate) status = (dhcp_failover_put_message (link, link -> outer, - FTM_CONTACT, + FTM_CONTACT, link->xid++, (failover_option_t *)0)); #if defined (DEBUG_FAILOVER_MESSAGES) @@ -3901,7 +3989,7 @@ isc_result_t dhcp_failover_send_state (dhcp_failover_state_t *state) status = (dhcp_failover_put_message (link, link -> outer, - FTM_STATE, + FTM_STATE, link->xid++, dhcp_failover_make_option (FTO_SERVER_STATE, FMA, (state -> me.state == startup ? state -> saved_state @@ -3909,7 +3997,7 @@ isc_result_t dhcp_failover_send_state (dhcp_failover_state_t *state) dhcp_failover_make_option (FTO_SERVER_FLAGS, FMA, (state -> service_state == service_startup - ? FTF_STARTUP : 0)), + ? FTF_SERVER_STARTUP : 0)), dhcp_failover_make_option (FTO_STOS, FMA, state -> me.stos), (failover_option_t *)0)); @@ -3952,10 +4040,9 @@ isc_result_t dhcp_failover_send_connect (omapi_object_t *l) status = (dhcp_failover_put_message (link, l -> outer, - FTM_CONNECT, - dhcp_failover_make_option (FTO_SERVER_ADDR, FMA, - state -> server_identifier.len, - state -> server_identifier.data), + FTM_CONNECT, link->xid++, + dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA, + strlen(state->name), state->name), dhcp_failover_make_option (FTO_MAX_UNACKED, FMA, state -> me.max_flying_updates), dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA, @@ -4009,35 +4096,37 @@ isc_result_t dhcp_failover_send_connectack (omapi_object_t *l, status = (dhcp_failover_put_message (link, l -> outer, - FTM_CONNECTACK, - (state - ? (dhcp_failover_make_option - (FTO_SERVER_ADDR, FMA, - state -> server_identifier.len, - state -> server_identifier.data)) - : &skip_failover_option), - (state + FTM_CONNECTACK, link->imsg->xid, + state + ? dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA, + strlen(state->name), state->name) + : (link->imsg->options_present & FTB_RELATIONSHIP_NAME) + ? &link->imsg->relationship_name + : &skip_failover_option, + state ? dhcp_failover_make_option (FTO_MAX_UNACKED, FMA, state -> me.max_flying_updates) - : &skip_failover_option), - (state + : &skip_failover_option, + state ? dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA, state -> me.max_response_delay) - : &skip_failover_option), + : &skip_failover_option, dhcp_failover_option_printf (FTO_VENDOR_CLASS, FMA, "isc-%s", DHCP_VERSION), dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA, DHCP_FAILOVER_VERSION), - dhcp_failover_make_option (FTO_TLS_REQUEST, FMA, - 0, 0), - (reason + (link->imsg->options_present & FTB_TLS_REQUEST) + ? dhcp_failover_make_option(FTO_TLS_REPLY, FMA, + 0, 0) + : &skip_failover_option, + reason ? dhcp_failover_make_option (FTO_REJECT_REASON, FMA, reason) - : &skip_failover_option), - (errmsg + : &skip_failover_option, + (reason && errmsg) ? dhcp_failover_make_option (FTO_MESSAGE, FMA, strlen (errmsg), errmsg) - : &skip_failover_option), + : &skip_failover_option, (failover_option_t *)0)); #if defined (DEBUG_FAILOVER_MESSAGES) @@ -4080,7 +4169,7 @@ isc_result_t dhcp_failover_send_disconnect (omapi_object_t *l, status = (dhcp_failover_put_message (link, l -> outer, - FTM_DISCONNECT, + FTM_DISCONNECT, link->xid++, dhcp_failover_make_option (FTO_REJECT_REASON, FMA, reason), (message @@ -4107,6 +4196,8 @@ isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state, { dhcp_failover_link_t *link; isc_result_t status; + int flags = 0; + binding_state_t transmit_state; #if defined (DEBUG_FAILOVER_MESSAGES) char obuf [64]; unsigned obufix = 0; @@ -4125,10 +4216,34 @@ isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state, if (!link -> outer || link -> outer -> type != omapi_type_connection) return ISC_R_INVALIDARG; + transmit_state = lease->desired_binding_state; + if (lease->flags & RESERVED_LEASE) { + /* If we are listing an allocable (not yet ACTIVE etc) lease + * as reserved, toggle to the peer's 'free state', per the + * draft. This gives the peer permission to alloc it to the + * chaddr/uid-named client. + */ + if ((state->i_am == primary) && (transmit_state == FTS_FREE)) + transmit_state = FTS_BACKUP; + else if ((state->i_am == secondary) && + (transmit_state == FTS_BACKUP)) + transmit_state = FTS_FREE; + + flags |= FTF_IP_FLAG_RESERVE; + } + if (lease->flags & BOOTP_LEASE) + flags |= FTF_IP_FLAG_BOOTP; + + /* last_xid == 0 is illegal, seek past zero if we hit it. */ + if (link->xid == 0) + link->xid = 1; + + lease->last_xid = link->xid++; + /* Send the update. */ status = (dhcp_failover_put_message (link, link -> outer, - FTM_BNDUPD, + FTM_BNDUPD, lease->last_xid, dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA, lease -> ip_addr.len, lease -> ip_addr.iabuf), @@ -4152,6 +4267,9 @@ isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state, lease -> starts), dhcp_failover_make_option (FTO_CLTT, FMA, lease -> cltt), + flags ? dhcp_failover_make_option(FTO_IP_FLAGS, FMA, + flags) : + &skip_failover_option, /* No IP_FLAGS */ &skip_failover_option, /* XXX DDNS */ &skip_failover_option, /* XXX request options */ &skip_failover_option, /* XXX reply options */ @@ -4200,10 +4318,11 @@ isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *state, /* Send the update. */ status = (dhcp_failover_put_message (link, link -> outer, - FTM_BNDACK, + FTM_BNDACK, msg->xid, dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA, sizeof msg -> assigned_addr, &msg -> assigned_addr), +#ifdef DO_BNDACK_SHOULD_NOT dhcp_failover_make_option (FTO_BINDING_STATUS, FMA, msg -> binding_status), (msg -> options_present & FTB_CLIENT_IDENTIFIER) @@ -4223,18 +4342,24 @@ isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *state, dhcp_failover_make_option (FTO_STOS, FMA, msg -> stos), dhcp_failover_make_option (FTO_CLTT, FMA, - msg -> client_ltt), + msg -> cltt), + ((msg->options_present & FTB_IP_FLAGS) && msg->ip_flags) ? + dhcp_failover_make_option(FTO_IP_FLAGS, FMA, + msg->ip_flags) + : &skip_failover_option, +#endif /* DO_BNDACK_SHOULD_NOT */ reason - ? dhcp_failover_make_option (FTO_REJECT_REASON, - FMA, reason) - : &skip_failover_option, - (message + ? dhcp_failover_make_option(FTO_REJECT_REASON, FMA, reason) + : &skip_failover_option, + (reason && message) ? dhcp_failover_make_option (FTO_MESSAGE, FMA, strlen (message), message) - : &skip_failover_option), + : &skip_failover_option, +#ifdef DO_BNDACK_SHOULD_NOT &skip_failover_option, /* XXX DDNS */ &skip_failover_option, /* XXX request options */ &skip_failover_option, /* XXX reply options */ +#endif /* DO_BNDACK_SHOULD_NOT */ (failover_option_t *)0)); #if defined (DEBUG_FAILOVER_MESSAGES) @@ -4272,7 +4397,7 @@ isc_result_t dhcp_failover_send_poolreq (dhcp_failover_state_t *state) status = (dhcp_failover_put_message (link, link -> outer, - FTM_POOLREQ, + FTM_POOLREQ, link->xid++, (failover_option_t *)0)); #if defined (DEBUG_FAILOVER_MESSAGES) @@ -4311,7 +4436,7 @@ isc_result_t dhcp_failover_send_poolresp (dhcp_failover_state_t *state, status = (dhcp_failover_put_message (link, link -> outer, - FTM_POOLRESP, + FTM_POOLRESP, link->imsg->xid, dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED, FMA, leases), (failover_option_t *)0)); @@ -4354,7 +4479,7 @@ isc_result_t dhcp_failover_send_update_request (dhcp_failover_state_t *state) status = (dhcp_failover_put_message (link, link -> outer, - FTM_UPDREQ, + FTM_UPDREQ, link->xid++, (failover_option_t *)0)); if (status == ISC_R_SUCCESS) @@ -4401,7 +4526,7 @@ isc_result_t dhcp_failover_send_update_request_all (dhcp_failover_state_t status = (dhcp_failover_put_message (link, link -> outer, - FTM_UPDREQALL, + FTM_UPDREQALL, link->xid++, (failover_option_t *)0)); if (status == ISC_R_SUCCESS) @@ -4443,7 +4568,7 @@ isc_result_t dhcp_failover_send_update_done (dhcp_failover_state_t *state) status = (dhcp_failover_put_message (link, link -> outer, - FTM_UPDDONE, + FTM_UPDDONE, state->updxid, (failover_option_t *)0)); #if defined (DEBUG_FAILOVER_MESSAGES) @@ -4457,6 +4582,8 @@ isc_result_t dhcp_failover_send_update_done (dhcp_failover_state_t *state) log_info ("Sent update done message to %s", state -> name); + state->updxid--; /* Paranoia, just so it mismatches. */ + /* There may be uncommitted leases at this point (since dhcp_failover_process_bind_ack() doesn't commit leases); commit the lease file. */ @@ -4572,11 +4699,45 @@ isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *state, lt -> ends = msg -> expiry; } if (msg -> options_present & FTB_CLTT) { - lt -> cltt = msg -> client_ltt; + lt -> cltt = msg -> cltt; } if (msg->options_present & FTB_POTENTIAL_EXPIRY) { lt->atsfp = lt->tsfp = msg->potential_expiry; } + if (msg->options_present & FTB_IP_FLAGS) { + if (msg->ip_flags & FTF_IP_FLAG_RESERVE) { + if ((((state->i_am == primary) && + (lease->binding_state == FTS_FREE)) || + ((state->i_am == secondary) && + (lease->binding_state == FTS_BACKUP))) && + !(lease->flags & RESERVED_LEASE)) { + message = "Address is not reserved."; + reason = FTR_IP_NOT_RESERVED; + goto bad; + } + + lt->flags |= RESERVED_LEASE; + } else + lt->flags &= ~RESERVED_LEASE; + + if (msg->ip_flags & FTF_IP_FLAG_BOOTP) { + if ((((state->i_am == primary) && + (lease->binding_state == FTS_FREE)) || + ((state->i_am == secondary) && + (lease->binding_state == FTS_BACKUP))) && + !(lease->flags & BOOTP_LEASE)) { + message = "Address is not allocated to BOOTP."; + goto bad; + } + lt->flags |= BOOTP_LEASE; + } else + lt->flags &= ~BOOTP_LEASE; + + if (msg->ip_flags & ~(FTF_IP_FLAG_RESERVE | FTF_IP_FLAG_BOOTP)) + log_info("Unknown IP-flags set in BNDUPD (0x%x).", + msg->ip_flags); + } else /* Flags may only not appear if the values are zero. */ + lt->flags &= ~(RESERVED_LEASE | BOOTP_LEASE); if (msg -> options_present & FTB_BINDING_STATUS) { #if defined (DEBUG_LEASE_STATE_TRANSITIONS) @@ -4621,9 +4782,9 @@ isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *state, } if (new_binding_state == FTS_EXPIRED || new_binding_state == FTS_RELEASED || - new_binding_state == FTS_RESET) + new_binding_state == FTS_RESET) { lt -> next_binding_state = FTS_FREE; - else + } else lt -> next_binding_state = new_binding_state; msg -> binding_status = lt -> next_binding_state; } @@ -4635,7 +4796,6 @@ isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *state, bad: dhcp_failover_send_bind_ack (state, msg, reason, message); } else { - dhcp_failover_queue_ack (state, msg); } @@ -4655,6 +4815,7 @@ isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *state, struct lease *lease = (struct lease *)0; struct iaddr ia; const char *message = "no memory"; + u_int32_t pot_expire; ia.len = sizeof msg -> assigned_addr; memcpy (ia.iabuf, &msg -> assigned_addr, ia.len); @@ -4680,50 +4841,47 @@ isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *state, } /* XXX Times may need to be adjusted based on clock skew! */ - if (msg -> options_present & FTB_POTENTIAL_EXPIRY) { - /* XXX it could be a problem to do this directly if the - XXX lease is sorted by tsfp. */ - lease->atsfp = lease->tsfp = msg->potential_expiry; - - if ((lease -> binding_state == FTS_EXPIRED || - lease -> binding_state == FTS_RESET || - lease -> binding_state == FTS_RELEASED) && - (msg -> options_present & FTB_BINDING_STATUS) && - msg -> binding_status == FTS_FREE) - { - lease -> next_binding_state = FTS_FREE; - supersede_lease (lease, (struct lease *)0, 0, 0, 0); - write_lease (lease); - if (state -> me.state == normal) - commit_leases (); - } else { - if ((lease -> desired_binding_state != - lease -> binding_state) && - (msg -> options_present & FTB_BINDING_STATUS) && - (msg -> binding_status == - lease -> desired_binding_state)) { - lease -> next_binding_state = - lease -> desired_binding_state; - supersede_lease (lease, - (struct lease *)0, 0, 0, 0); - } - write_lease (lease); - /* Commit the lease only after a two-second timeout, - so that if we get a bunch of acks in quick - successtion (e.g., when stealing leases from the - secondary), we do not do an immediate commit for - each one. */ - add_timeout (cur_time + 2, - commit_leases_timeout, (void *)0, 0, 0); + if (msg->options_present & FTO_POTENTIAL_EXPIRY) + pot_expire = msg->potential_expiry; + else if(lease->last_xid && (msg->xid == lease->last_xid)) + pot_expire = lease->tstp; + else { + message = "peer sent no potential-expiry and mismatched XID."; + goto bad; + } + + /* XXX it could be a problem to do this directly if the + XXX lease is sorted by tsfp. */ + if (lease->binding_state == FTS_EXPIRED || + lease->binding_state == FTS_RESET || + lease->binding_state == FTS_RELEASED) + { + lease->tsfp = pot_expire; + if ((state->i_am == secondary) && + (lease->flags & RESERVED_LEASE)) + lease->next_binding_state = FTS_BACKUP; + else + lease->next_binding_state = FTS_FREE; + supersede_lease(lease, (struct lease *)0, 0, 0, 0); + write_lease(lease); + if (state->me.state == normal) + commit_leases (); + } else { + lease->tsfp = pot_expire; + if (lease->desired_binding_state != lease->binding_state) { + lease->next_binding_state = + lease->desired_binding_state; + supersede_lease(lease, + (struct lease *)0, 0, 0, 0); } - } else if (lease -> desired_binding_state != lease -> binding_state && - (msg -> options_present & FTB_BINDING_STATUS) && - msg -> binding_status == lease -> desired_binding_state) { - lease -> next_binding_state = lease -> desired_binding_state; - supersede_lease (lease, (struct lease *)0, 0, 0, 0); - write_lease (lease); - add_timeout (cur_time + 2, commit_leases_timeout, - (void *)0, 0, 0); + write_lease(lease); + /* Commit the lease only after a two-second timeout, + so that if we get a bunch of acks in quick + successtion (e.g., when stealing leases from the + secondary), we do not do an immediate commit for + each one. */ + add_timeout(cur_time + 2, + commit_leases_timeout, (void *)0, 0, 0); } unqueue: @@ -4847,6 +5005,8 @@ dhcp_failover_process_update_request (dhcp_failover_state_t *state, /* Generate a fresh update queue. */ dhcp_failover_generate_update_queue (state, 0); + state->updxid = msg->xid; + /* If there's anything on the update queue (there shouldn't be anything on the ack queue), trigger an update done message when we get an ack for that lease. */ @@ -4874,6 +5034,8 @@ dhcp_failover_process_update_request_all (dhcp_failover_state_t *state, /* Generate a fresh update queue that includes every lease. */ dhcp_failover_generate_update_queue (state, 1); + state->updxid = msg->xid; + if (state -> update_queue_tail) { lease_reference (&state -> send_update_done, state -> update_queue_tail, MDL); @@ -4915,7 +5077,26 @@ dhcp_failover_process_update_done (dhcp_failover_state_t *state, /* We got the UPDDONE, so we can go into normal state! */ case potential_conflict: - dhcp_failover_set_state (state, normal); + if (state->partner.state == conflict_done) { + if (state->i_am == secondary) { + dhcp_failover_set_state (state, normal); + } else { + log_error("Secondary is in conflict_done " + "state after conflict resolution, " + "this is illegal."); + dhcp_failover_set_state (state, shut_down); + } + } else { + if (state->i_am == primary) + dhcp_failover_set_state (state, conflict_done); + else + log_error("Spurious update-done message."); + } + + break; + + case conflict_done: + log_error("Spurious update-done message."); break; case recover: |