2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-22 18:07:25 +00:00

- An optimization described in the failover protocol draft is now included,

which permits a DHCP server operating in communications-interrupted state
  to 'rewind' a lease to the state most recently transmitted to its peer,
  greatly increasing a server's endurance in communications-interrupted.
  This is supported using a new 'rewind state' record on the dhcpd.leases
  entry for each lease.  [ISC-Bugs #19601]
This commit is contained in:
David Hankins 2010-02-03 23:25:25 +00:00
parent 176c2a7d2d
commit fdfebedf3e
9 changed files with 398 additions and 136 deletions

View File

@ -52,6 +52,13 @@ work on other platforms. Please report any problems and suggested fixes to
- Cleaned up some compiler warnings
- An optimization described in the failover protocol draft is now included,
which permits a DHCP server operating in communications-interrupted state
to 'rewind' a lease to the state most recently transmitted to its peer,
greatly increasing a server's endurance in communications-interrupted.
This is supported using a new 'rewind state' record on the dhcpd.leases
entry for each lease.
Changes since 4.1.0 (new features)
- Failover port configuration can now be left to defaults (port 647) as

View File

@ -1229,63 +1229,72 @@ intern(char *atom, enum dhcp_token dfv) {
return PAUSED;
break;
case 'r':
if (!strcasecmp (atom + 1, "esolution-interrupted"))
return RESOLUTION_INTERRUPTED;
if (!strcasecmp (atom + 1, "ange"))
if (!strcasecmp(atom + 1, "ange"))
return RANGE;
if (!strcasecmp(atom + 1, "ange6")) {
if (!strcasecmp(atom + 1, "ange6"))
return RANGE6;
if (isascii(atom[1]) && (tolower(atom[1]) == 'e')) {
if (!strcasecmp(atom + 2, "bind"))
return REBIND;
if (!strcasecmp(atom + 2, "boot"))
return REBOOT;
if (!strcasecmp(atom + 2, "contact-interval"))
return RECONTACT_INTERVAL;
if (!strncasecmp(atom + 2, "cover", 5)) {
if (atom[7] == '\0')
return RECOVER;
if (!strcasecmp(atom + 7, "-done"))
return RECOVER_DONE;
if (!strcasecmp(atom + 7, "-wait"))
return RECOVER_WAIT;
break;
}
if (!strcasecmp(atom + 2, "fresh"))
return REFRESH;
if (!strcasecmp(atom + 2, "fused"))
return NS_REFUSED;
if (!strcasecmp(atom + 2, "ject"))
return REJECT;
if (!strcasecmp(atom + 2, "lease"))
return RELEASE;
if (!strcasecmp(atom + 2, "leased"))
return TOKEN_RELEASED;
if (!strcasecmp(atom + 2, "move"))
return REMOVE;
if (!strcasecmp(atom + 2, "new"))
return RENEW;
if (!strcasecmp(atom + 2, "quest"))
return REQUEST;
if (!strcasecmp(atom + 2, "quire"))
return REQUIRE;
if (isascii(atom[2]) && (tolower(atom[2]) == 's')) {
if (!strcasecmp(atom + 3, "erved"))
return TOKEN_RESERVED;
if (!strcasecmp(atom + 3, "et"))
return TOKEN_RESET;
if (!strcasecmp(atom + 3,
"olution-interrupted"))
return RESOLUTION_INTERRUPTED;
break;
}
if (!strcasecmp(atom + 2, "try"))
return RETRY;
if (!strcasecmp(atom + 2, "turn"))
return RETURN;
if (!strcasecmp(atom + 2, "verse"))
return REVERSE;
if (!strcasecmp(atom + 2, "wind"))
return REWIND;
break;
}
if (!strcasecmp (atom + 1, "ecover"))
return RECOVER;
if (!strcasecmp (atom + 1, "ecover-done"))
return RECOVER_DONE;
if (!strcasecmp (atom + 1, "ecover-wait"))
return RECOVER_WAIT;
if (!strcasecmp (atom + 1, "econtact-interval"))
return RECONTACT_INTERVAL;
if (!strcasecmp (atom + 1, "equest"))
return REQUEST;
if (!strcasecmp (atom + 1, "equire"))
return REQUIRE;
if (!strcasecmp (atom + 1, "equire"))
return REQUIRE;
if (!strcasecmp (atom + 1, "etry"))
return RETRY;
if (!strcasecmp (atom + 1, "eturn"))
return RETURN;
if (!strcasecmp (atom + 1, "enew"))
return RENEW;
if (!strcasecmp (atom + 1, "ebind"))
return REBIND;
if (!strcasecmp (atom + 1, "eboot"))
return REBOOT;
if (!strcasecmp (atom + 1, "eject"))
return REJECT;
if (!strcasecmp (atom + 1, "everse"))
return REVERSE;
if (!strcasecmp (atom + 1, "elease"))
return RELEASE;
if (!strcasecmp (atom + 1, "efused"))
return NS_REFUSED;
if (!strcasecmp (atom + 1, "eleased"))
return TOKEN_RELEASED;
if (!strcasecmp (atom + 1, "eset"))
return TOKEN_RESET;
if (!strcasecmp (atom + 1, "eserved"))
return TOKEN_RESERVED;
if (!strcasecmp (atom + 1, "emove"))
return REMOVE;
if (!strcasecmp (atom + 1, "efresh"))
return REFRESH;
break;
case 's':
if (!strcasecmp(atom + 1, "cript"))
return SCRIPT;
if (!strcasecmp(atom + 1, "cript"))
return SCRIPT;
if (isascii(atom[1]) &&
tolower((unsigned char)atom[1]) == 'e') {
if (!strcasecmp(atom + 2, "arch"))
return SEARCH;
if (!strcasecmp(atom + 2, "arch"))
return SEARCH;
if (isascii(atom[2]) &&
tolower((unsigned char)atom[2]) == 'c') {
if (!strncasecmp(atom + 3, "ond", 3)) {

View File

@ -495,13 +495,28 @@ struct lease {
RESERVED_LEASE | \
BOOTP_LEASE)
/*
* The lease's binding state is its current state. The next binding
* state is the next state this lease will move into by expiration,
* or timers in general. The desired binding state is used on lease
* updates; the caller is attempting to move the lease to the desired
* binding state (and this may either succeed or fail, so the binding
* state must be preserved).
*
* The 'rewind' binding state is used in failover processing. It
* is used for an optimization when out of communications; it allows
* the server to "rewind" a lease to the previous state acknowledged
* by the peer, and progress forward from that point.
*/
binding_state_t binding_state;
binding_state_t next_binding_state;
binding_state_t desired_binding_state;
binding_state_t rewind_binding_state;
struct lease_state *state;
/* 'tsfp' is more of an 'effective' tsfp. It may be calculated from
/*
* 'tsfp' is more of an 'effective' tsfp. It may be calculated from
* stos+mclt for example if it's an expired lease and the server is
* in partner-down state. 'atsfp' is zeroed whenever a lease is
* updated - and only set when the peer acknowledges it. This

View File

@ -356,7 +356,8 @@ enum dhcp_token {
ANYCAST_MAC = 659,
CONFLICT_DONE = 660,
AUTO_PARTNER_DOWN = 661,
GETHOSTNAME = 662
GETHOSTNAME = 662,
REWIND = 663
};
#define is_identifier(x) ((x) >= FIRST_TOKEN && \

View File

@ -3095,6 +3095,16 @@ int parse_lease_declaration (struct lease **lp, struct parse *cfile)
}
goto do_binding_state;
case REWIND:
seenbit = 512;
token = next_token(&val, NULL, cfile);
if (token != BINDING) {
parse_warn(cfile, "expecting 'binding'");
skip_to_semi(cfile);
break;
}
goto do_binding_state;
case BINDING:
seenbit = 256;
@ -3152,12 +3162,25 @@ int parse_lease_declaration (struct lease **lp, struct parse *cfile)
if (seenbit == 256) {
lease -> binding_state = new_state;
/* If no next binding state is specified, it's
the same as the current state. */
/*
* Apply default/conservative next/rewind
* binding states if they haven't been set
* yet. These defaults will be over-ridden if
* they are set later in parsing.
*/
if (!(seenmask & 128))
lease -> next_binding_state = new_state;
} else
lease->next_binding_state = new_state;
/* The most conservative rewind state. */
if (!(seenmask & 512))
lease->rewind_binding_state = new_state;
} else if (seenbit == 128)
lease -> next_binding_state = new_state;
else if (seenbit == 512)
lease->rewind_binding_state = new_state;
else
log_fatal("Impossible condition at %s:%d.",
MDL);
parse_semi (cfile);
break;
@ -3406,6 +3429,9 @@ int parse_lease_declaration (struct lease **lp, struct parse *cfile)
lease -> next_binding_state = FTS_FREE;
} else
lease -> next_binding_state = lease -> binding_state;
/* The most conservative rewind state implies no rewind. */
lease->rewind_binding_state = lease->binding_state;
}
if (!(seenmask & 65536))

View File

@ -168,6 +168,20 @@ int write_lease (lease)
: "abandoned")) < 0)
++errors;
/*
* In this case, if the rewind state is not present in the lease file,
* the reader will use the current binding state as the most
* conservative (safest) state. So if the in-memory rewind state is
* for some reason invalid, the best thing to do is not to write a
* state and let the reader take on a safe state.
*/
if ((lease->binding_state != lease->rewind_binding_state) &&
(lease->rewind_binding_state > 0) &&
(lease->rewind_binding_state <= FTS_LAST) &&
(fprintf(db_file, "\n rewind binding state %s;",
binding_state_names[lease->rewind_binding_state-1])) < 0)
++errors;
if (lease->flags & RESERVED_LEASE)
if (fprintf(db_file, "\n reserved;") < 0)
++errors;

View File

@ -312,12 +312,19 @@ void dhcpdiscover (packet, ms_nulltp)
if (lease && lease -> pool && lease -> pool -> failover_peer) {
peer = lease -> pool -> failover_peer;
/* If the lease is ours to allocate, then allocate it.
/*
* If the lease is ours to (re)allocate, then allocate it.
*
* If the lease is active, it belongs to the client. This
* is the right lease, if we are to offer one. We decide
* whether or not to offer later on.
*
* If the lease was last active, and we've reached this
* point, then it was last active with the same client. We
* can safely re-activate the lease with this client.
*/
if (lease->binding_state == FTS_ACTIVE ||
lease->rewind_binding_state == FTS_ACTIVE ||
lease_mine_to_reallocate(lease)) {
; /* This space intentionally left blank. */
@ -521,13 +528,18 @@ void dhcprequest (packet, ms_nulltp, ip_lease)
goto out;
}
/* If the lease is in a transitional state, we can't
renew it. */
if ((lease -> binding_state == FTS_RELEASED ||
lease -> binding_state == FTS_EXPIRED) &&
!lease_mine_to_reallocate (lease)) {
log_debug ("%s: lease in transition state %s", msgbuf,
lease -> binding_state == FTS_RELEASED
/*
* If the lease is in a transitional state, we can't
* renew it unless we can rewind it to a non-transitional
* state (active, free, or backup). lease_mine_to_reallocate()
* checks for free/backup, so we only need to check for active.
*/
if ((lease->binding_state == FTS_RELEASED ||
lease->binding_state == FTS_EXPIRED) &&
lease->rewind_binding_state != FTS_ACTIVE &&
!lease_mine_to_reallocate(lease)) {
log_debug("%s: lease in transition state %s", msgbuf,
(lease->binding_state == FTS_RELEASED)
? "released" : "expired");
goto out;
}
@ -3315,7 +3327,8 @@ int find_lease (struct lease **lp,
goto out;
}
/* If we found leases matching the client identifier, loop through
/*
* If we found leases matching the client identifier, loop through
* the n_uid pointer looking for one that's actually valid. We
* can't do this until we get here because we depend on
* packet -> known, which may be set by either the uid host
@ -3331,14 +3344,20 @@ int find_lease (struct lease **lp,
#endif
#if defined (FAILOVER_PROTOCOL)
/* When failover is active, it's possible that there could
be two "free" leases for the same uid, but only one of
them that's available for this failover peer to allocate. */
if (uid_lease -> binding_state != FTS_ACTIVE &&
!lease_mine_to_reallocate (uid_lease)) {
/*
* When we lookup a lease by uid, we know the client identifier
* matches the lease's record. If it is active, or was last
* active with the same client, we can trivially extend it.
* If is not or was not active, we can allocate it to this
* client if it matches the usual free/backup criteria (which
* is contained in lease_mine_to_reallocate()).
*/
if (uid_lease->binding_state != FTS_ACTIVE &&
uid_lease->rewind_binding_state != FTS_ACTIVE &&
!lease_mine_to_reallocate(uid_lease)) {
#if defined (DEBUG_FIND_LEASE)
log_info ("not mine to allocate: %s",
piaddr (uid_lease -> ip_addr));
log_info("not active or not mine to allocate: %s",
piaddr(uid_lease->ip_addr));
#endif
goto n_uid;
}
@ -3398,19 +3417,32 @@ int find_lease (struct lease **lp,
piaddr (hw_lease -> ip_addr));
#endif
#if defined (FAILOVER_PROTOCOL)
/* When failover is active, it's possible that there could
be two "free" leases for the same uid, but only one of
them that's available for this failover peer to allocate. */
if (hw_lease -> binding_state != FTS_ACTIVE &&
!lease_mine_to_reallocate (hw_lease)) {
/*
* When we lookup a lease by chaddr, we know the MAC address
* matches the lease record (we will check if the lease has a
* client-id the client does not next). If the lease is
* currently active or was last active with this client, we can
* trivially extend it. Otherwise, there are a set of rules
* that govern if we can reallocate this lease to any client
* ("lease_mine_to_reallocate()") including this one.
*/
if (hw_lease->binding_state != FTS_ACTIVE &&
hw_lease->rewind_binding_state != FTS_ACTIVE &&
!lease_mine_to_reallocate(hw_lease)) {
#if defined (DEBUG_FIND_LEASE)
log_info ("not mine to allocate: %s",
piaddr (hw_lease -> ip_addr));
log_info("not active or not mine to allocate: %s",
piaddr(hw_lease->ip_addr));
#endif
goto n_hw;
}
#endif
/*
* This conditional skips "potentially active" leases (leases
* we think are expired may be extended by the peer, etc) that
* may be assigned to a differently /client-identified/ client
* with the same MAC address.
*/
if (hw_lease -> binding_state != FTS_FREE &&
hw_lease -> binding_state != FTS_BACKUP &&
hw_lease -> uid &&
@ -3495,8 +3527,15 @@ int find_lease (struct lease **lp,
lease_dereference (&ip_lease, MDL);
}
/* Toss ip_lease if it hasn't yet expired and doesn't belong to the
client. */
/*
* If the requested address is in use (or potentially in use) by
* a different client, it can't be granted.
*
* This first conditional only detects if the lease is currently
* identified to a different client (client-id and/or chaddr
* mismatch). In this case we may not want to give the client the
* lease, if doing so may potentially be an addressing conflict.
*/
if (ip_lease &&
(ip_lease -> uid ?
(!have_client_identifier ||
@ -3508,11 +3547,14 @@ int find_lease (struct lease **lp,
memcmp (&ip_lease -> hardware_addr.hbuf [1],
packet -> raw -> chaddr,
(unsigned)(ip_lease -> hardware_addr.hlen - 1))))) {
/* If we're not doing failover, the only state in which
we can allocate this lease to the client is FTS_FREE.
If we are doing failover, things are more complicated.
If the lease is free or backup, we let the caller decide
whether or not to give it out. */
/*
* A lease is unavailable for allocation to a new client if
* it is not in the FREE or BACKUP state. There may be
* leases that are in the expired state with a rewinding
* state that is free or backup, but these will be processed
* into the free or backup states by expiration processes, so
* checking for them here is superfluous.
*/
if (ip_lease -> binding_state != FTS_FREE &&
ip_lease -> binding_state != FTS_BACKUP) {
#if defined (DEBUG_FIND_LEASE)
@ -3526,18 +3568,21 @@ int find_lease (struct lease **lp,
}
}
/* If we got an ip_lease and a uid_lease or hw_lease, and ip_lease
is not active, and is not ours to reallocate, forget about it. */
/*
* If we got an ip_lease and a uid_lease or hw_lease, and ip_lease
* is/was not active, and is not ours to reallocate, forget about it.
*/
if (ip_lease && (uid_lease || hw_lease) &&
ip_lease -> binding_state != FTS_ACTIVE &&
ip_lease->binding_state != FTS_ACTIVE &&
ip_lease->rewind_binding_state != FTS_ACTIVE &&
#if defined(FAILOVER_PROTOCOL)
!lease_mine_to_reallocate (ip_lease) &&
!lease_mine_to_reallocate(ip_lease) &&
#endif
packet -> packet_type == DHCPDISCOVER) {
packet->packet_type == DHCPDISCOVER) {
#if defined (DEBUG_FIND_LEASE)
log_info ("ip lease not ours to offer.");
log_info("ip lease not active or not ours to offer.");
#endif
lease_dereference (&ip_lease, MDL);
lease_dereference(&ip_lease, MDL);
}
/* If for some reason the client has more than one lease
@ -3973,6 +4018,7 @@ int allocate_lease (struct lease **lp, struct packet *packet,
}
}
/* Try abandoned leases as a last resort. */
if ((candl == NULL) &&
(pool->abandoned != NULL) &&
lease_mine_to_reallocate(pool->abandoned))
@ -4003,6 +4049,18 @@ int allocate_lease (struct lease **lp, struct packet *packet,
continue;
}
/*
* There are tiers of lease state preference, listed here in
* reverse order (least to most preferential):
*
* ABANDONED
* FREE/BACKUP
*
* If the selected lease and candidate are both of the same
* state, select the oldest (longest ago) expiration time
* between the two. If the candidate lease is of a higher
* preferred grade over the selected lease, use it.
*/
if ((lease -> binding_state == FTS_ABANDONED) &&
((candl -> binding_state != FTS_ABANDONED) ||
(candl -> ends < lease -> ends))) {

View File

@ -4584,6 +4584,36 @@ isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state,
lease->last_xid = link->xid++;
/*
* Our very next action is to transmit a binding update relating to
* this lease over the wire, and although there is a BNDACK, there is
* no BNDACKACK or BNDACKACKACK...the basic issue as we send a BNDUPD,
* we may not receive a BNDACK. This non-reception does not imply the
* peer did not receive and process the BNDUPD. So at this point, we
* must divest any state that would be dangerous to retain under the
* impression the peer has been updated. Normally state changes like
* this are processed in supersede_lease(), but in this case we need a
* very late binding.
*
* In failover rules, a server is permitted to work forward in certain
* directions from a given lease's state; active leases may be
* extended, so forth. There is an 'optimization' in the failover
* draft that permits a server to 'rewind' any work they have not
* informed the peer. Since we can't know if the peer received our
* update but was unable to acknowledge it, we make this change on
* transmit rather than upon receiving the acknowledgement.
*
* XXX: Frequent lease commits are undesirable. This should hopefully
* only trigger when a server is sending a lease /state change/, and
* not merely an update such as with a renewal.
*/
if (lease->rewind_binding_state != lease->binding_state) {
lease->rewind_binding_state = lease->binding_state;
write_lease(lease);
commit_leases();
}
/* Send the update. */
status = (dhcp_failover_put_message
(link, link -> outer,
@ -5296,6 +5326,12 @@ isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *state,
}
msg -> binding_status = lt -> next_binding_state;
/*
* If we accept a peer's binding update, then we can't rewind a
* lease behind the peer's state.
*/
lease->rewind_binding_state = lt->next_binding_state;
/* Try to install the new information. */
if (!supersede_lease (lease, lt, 0, 0, 0) ||
!write_lease (lease)) {
@ -5435,8 +5471,13 @@ isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *state,
lease->next_binding_state = FTS_BACKUP;
else
lease->next_binding_state = FTS_FREE;
/* Clear this condition for the next go-round. */
lease->desired_binding_state = lease->next_binding_state;
/* The peer will have made this state change, so set rewind. */
lease->rewind_binding_state = lease->next_binding_state;
supersede_lease(lease, (struct lease *)0, 0, 0, 0);
write_lease(lease);
@ -6158,6 +6199,12 @@ int lease_mine_to_reallocate (struct lease *lease)
if (lease && lease->pool &&
(peer = lease->pool->failover_peer)) {
/*
* In addition to the normal rules governing wether a server
* is allowed to operate changes on a lease, the server is
* allowed to operate on a lease from the standpoint of the
* most conservative guess of the peer's state for this lease.
*/
switch (lease->binding_state) {
case FTS_ACTIVE:
/* ACTIVE leases may not be reallocated. */
@ -6182,23 +6229,46 @@ int lease_mine_to_reallocate (struct lease *lease)
(peer->me.stos + peer->mclt < cur_time) :
(lease->tsfp + peer->mclt < cur_time)));
case FTS_RESET:
case FTS_RELEASED:
case FTS_EXPIRED:
/* These three lease states go onto the 'expired'
* queue. Upon entry into partner-down state, this
* queue of leases has their tsfp values modified
* to equal stos+mclt, the point at which the server
* is allowed to remove them from these transitional
* states without an acknowledgement.
/*
* These leases are generally untouchable until the
* peer acknowledges their state change. However, as
* this is impossible if the peer is offline, the
* failover protocol permits an 'optimization' to
* rewind the lease to a previous state that the server
* is allowed to operate on, if that was the state that
* was last acknowledged by the peer.
*
* So if a lease was free, was allocated by this
* server, and expired without ever being transmitted
* to the peer, it can be returned to free and given
* to any new client legally.
*/
if ((peer->i_am == primary) &&
(lease->rewind_binding_state == FTS_FREE))
return 1;
if ((peer->i_am == secondary) &&
(lease->rewind_binding_state == FTS_BACKUP))
return 1;
/* FALL THROUGH (released, expired, reset) */
case FTS_RESET:
/*
* Released, expired, and reset leases go onto the
* 'expired' queue all together. Upon entry into
* partner-down state, this queue of leases has their
* tsfp values modified to equal stos+mclt, the point
* at which the server is allowed to remove them from
* these transitional states.
*
* Note that although tsfp has been possibly extended
* past the actual tsfp we received from the peer, we
* don't have to take any special action. Since tsfp
* is now in the past (or now), we can guarantee that
* this server will only allocate a lease time equal
* to MCLT, rather than a TSFP-optimal lease, which is
* the only danger for a lease in one of these states.
* will be equal to the current time when the lease
* transitions to free, tsfp will not be used to grant
* lease-times longer than the MCLT to clients, which
* is the only danger for this sort of modification.
*/
return((peer->service_state == service_partner_down) &&
(lease->tsfp < cur_time));

View File

@ -801,15 +801,15 @@ void new_address_range (cfile, low, high, subnet, pool, lpchain)
i + min)),
isc_result_totext (status));
#endif
lp -> ip_addr = ip_addr (subnet -> net,
subnet -> netmask, i + min);
lp -> starts = MIN_TIME;
lp -> ends = MIN_TIME;
subnet_reference (&lp -> subnet, subnet, MDL);
pool_reference (&lp -> pool, pool, MDL);
lp -> binding_state = FTS_FREE;
lp -> next_binding_state = FTS_FREE;
lp -> flags = 0;
lp->ip_addr = ip_addr(subnet->net, subnet->netmask, i + min);
lp->starts = MIN_TIME;
lp->ends = MIN_TIME;
subnet_reference(&lp->subnet, subnet, MDL);
pool_reference(&lp->pool, pool, MDL);
lp->binding_state = FTS_FREE;
lp->next_binding_state = FTS_FREE;
lp->rewind_binding_state = FTS_FREE;
lp->flags = 0;
/* Remember the lease in the IP address hash. */
if (find_lease_by_ip_addr (&lt, lp -> ip_addr, MDL)) {
@ -1224,7 +1224,8 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
just_move_it:
#if defined (FAILOVER_PROTOCOL)
/* Atsfp should be cleared upon any state change that implies
/*
* Atsfp should be cleared upon any state change that implies
* propagation whether supersede_lease was given a copy lease
* structure or not (often from the pool_timer()).
*/
@ -1356,6 +1357,24 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
}
if (commit) {
#if defined(FAILOVER_PROTOCOL)
/*
* If commit and propogate are set, then we can save a
* possible fsync later in BNDUPD socket transmission by
* stepping the rewind state forward to the new state, in
* case it has changed. This is only worth doing if the
* failover connection is currently connected, as in this
* case it is likely we will be transmitting to the peer very
* shortly.
*/
if (propogate && (comp->pool->failover_peer != NULL) &&
((comp->pool->failover_peer->service_state ==
cooperating) ||
(comp->pool->failover_peer->service_state ==
not_responding)))
comp->rewind_binding_state = comp->binding_state;
#endif
if (!write_lease (comp))
return 0;
if ((server_starting & SS_NOSYNC) == 0) {
@ -1404,11 +1423,10 @@ void make_binding_state_transition (struct lease *lease)
((
#if defined (FAILOVER_PROTOCOL)
peer &&
(lease -> binding_state == FTS_EXPIRED ||
(peer -> i_am == secondary &&
lease -> binding_state == FTS_ACTIVE)) &&
(lease -> next_binding_state == FTS_FREE ||
lease -> next_binding_state == FTS_BACKUP)) ||
(lease->binding_state == FTS_EXPIRED ||
lease->binding_state == FTS_ACTIVE) &&
(lease->next_binding_state == FTS_FREE ||
lease->next_binding_state == FTS_BACKUP)) ||
(!peer &&
#endif
lease -> binding_state == FTS_ACTIVE &&
@ -1564,7 +1582,6 @@ void make_binding_state_transition (struct lease *lease)
piaddr (lease -> ip_addr),
binding_state_print (lease -> next_binding_state));
#endif
}
/* Copy the contents of one lease into another, correctly maintaining
@ -1636,6 +1653,7 @@ int lease_copy (struct lease **lp,
lt->cltt = lease -> cltt;
lt->binding_state = lease->binding_state;
lt->next_binding_state = lease->next_binding_state;
lt->rewind_binding_state = lease->rewind_binding_state;
status = lease_reference(lp, lt, file, line);
lease_dereference(&lt, MDL);
return status == ISC_R_SUCCESS;
@ -1690,7 +1708,20 @@ void release_lease (lease, packet)
lease->tstp = cur_time;
#if defined (FAILOVER_PROTOCOL)
if (lease -> pool && lease -> pool -> failover_peer) {
lease -> next_binding_state = FTS_RELEASED;
dhcp_failover_state_t *peer = NULL;
if (lease->pool != NULL)
peer = lease->pool->failover_peer;
if ((peer->service_state == not_cooperating) &&
(((peer->i_am == primary) &&
(lease->rewind_binding_state == FTS_FREE)) ||
((peer->i_am == secondary) &&
(lease->rewind_binding_state == FTS_BACKUP)))) {
lease->next_binding_state =
lease->rewind_binding_state;
} else
lease -> next_binding_state = FTS_RELEASED;
} else {
lease -> next_binding_state = FTS_FREE;
}
@ -1801,13 +1832,26 @@ void pool_timer (vpool)
continue;
#if defined (FAILOVER_PROTOCOL)
if (pool -> failover_peer &&
pool -> failover_peer -> me.state != partner_down) {
/* The secondary can't remove a lease from the
active state except in partner_down. */
if (i == ACTIVE_LEASES &&
pool -> failover_peer -> i_am == secondary)
if (pool->failover_peer &&
pool->failover_peer->me.state != partner_down) {
/*
* Normally the secondary doesn't initiate expiration
* events (unless in partner-down), but rather relies
* on the primary to expire the lease. However, when
* disconnected from its peer, the server is allowed to
* rewind a lease to the previous state that the peer
* would have recorded it. This means there may be
* opportunities for active->free or active->backup
* expirations while out of contact.
*
* Q: Should we limit this expiration to
* comms-interrupt rather than not-normal?
*/
if ((i == ACTIVE_LEASES) &&
(pool->failover_peer->i_am == secondary) &&
(pool->failover_peer->me.state == normal))
continue;
/* Leases in an expired state don't move to
free because of a timeout unless we're in
partner_down. */
@ -1837,10 +1881,29 @@ void pool_timer (vpool)
state change should happen, just call
supersede_lease on it to make the change
happen. */
if (lease -> next_binding_state !=
lease -> binding_state)
supersede_lease (lease,
(struct lease *)0, 1, 1, 1);
if (lease->next_binding_state != lease->binding_state)
{
#if defined(FAILOVER_PROTOCOL)
dhcp_failover_state_t *peer = NULL;
if (lease->pool != NULL)
peer = lease->pool->failover_peer;
/* Can we rewind the lease to a free state? */
if (peer != NULL &&
peer->service_state == not_cooperating &&
lease->next_binding_state == FTS_EXPIRED &&
((peer->i_am == primary &&
lease->rewind_binding_state == FTS_FREE)
||
(peer->i_am == secondary &&
lease->rewind_binding_state ==
FTS_BACKUP)))
lease->next_binding_state =
lease->rewind_binding_state;
#endif
supersede_lease(lease, NULL, 1, 1, 1);
}
lease_dereference (&lease, MDL);
if (next)
@ -2287,9 +2350,8 @@ int write_leases ()
for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
for (l = *(lptr [i]); l; l = l -> next) {
#if !defined (DEBUG_DUMP_ALL_LEASES)
if (l -> hardware_addr.hlen ||
l -> uid_len ||
(l -> binding_state != FTS_FREE))
if (l->hardware_addr.hlen != 0 || l->uid_len != 0 ||
l->tsfp != 0 || l->binding_state != FTS_FREE)
#endif
{
if (!write_lease (l))