diff -up dhcp-3.0.5/server/dhcp.c.exhaustion dhcp-3.0.5/server/dhcp.c --- dhcp-3.0.5/server/dhcp.c.exhaustion 2010-08-04 15:43:59.000000000 +0200 +++ dhcp-3.0.5/server/dhcp.c 2010-08-04 15:45:29.000000000 +0200 @@ -3087,10 +3087,14 @@ int find_lease (struct lease **lp, } /* 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 - lookup or the haddr host lookup. */ + * 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 + * lookup or the haddr host lookup. + * + * Note that the n_uid lease chain is sorted in order of + * preference, so the first one is the best one. + */ while (uid_lease) { #if defined (DEBUG_FIND_LEASE) log_info ("trying next lease matching client id: %s", @@ -3149,8 +3153,12 @@ int find_lease (struct lease **lp, #endif /* Find a lease whose hardware address matches, whose client - identifier matches, that's permitted, and that's on the - correct subnet. */ + * identifier matches (or equally doesn't have one), that's + * permitted, and that's on the correct subnet. + * + * Note that the n_hw chain is sorted in order of preference, so + * the first one found is the best one. + */ h.hlen = packet -> raw -> hlen + 1; h.hbuf [0] = packet -> raw -> htype; memcpy (&h.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen); diff -up dhcp-3.0.5/server/mdb.c.exhaustion dhcp-3.0.5/server/mdb.c --- dhcp-3.0.5/server/mdb.c.exhaustion 2010-08-04 15:43:59.000000000 +0200 +++ dhcp-3.0.5/server/mdb.c 2010-08-04 15:50:09.000000000 +0200 @@ -787,8 +787,6 @@ int supersede_lease (comp, lease, commit int propogate; int pimmediate; { - int enter_uid = 0; - int enter_hwaddr = 0; struct lease *lp, **lq, *prev; #if defined (FAILOVER_PROTOCOL) @@ -834,28 +832,21 @@ int supersede_lease (comp, lease, commit /* If there's a Unique ID, dissociate it from the hash table and free it if necessary. */ - if (comp -> uid) { - uid_hash_delete (comp); - enter_uid = 1; - if (comp -> uid != &comp -> uid_buf [0]) { - dfree (comp -> uid, MDL); - comp -> uid_max = 0; - comp -> uid_len = 0; + if (comp->uid) { + uid_hash_delete(comp); + if (comp->uid != comp->uid_buf) { + dfree(comp->uid, MDL); + comp->uid_max = 0; + comp->uid_len = 0; } comp -> uid = (unsigned char *)0; - } else - enter_uid = 1; + } - if (comp -> hardware_addr.hlen && - ((comp -> hardware_addr.hlen != - lease -> hardware_addr.hlen) || - memcmp (comp -> hardware_addr.hbuf, - lease -> hardware_addr.hbuf, - comp -> hardware_addr.hlen))) { - hw_hash_delete (comp); - enter_hwaddr = 1; - } else if (!comp -> hardware_addr.hlen) - enter_hwaddr = 1; + /* If there's a hardware address, remove the lease from its + * old position in the hash bucket's ordered list. + */ + if (comp->hardware_addr.hlen) + hw_hash_delete(comp); /* If the lease has been billed to a class, remove the billing. */ if (comp -> billing_class != lease -> billing_class) { @@ -945,14 +936,12 @@ int supersede_lease (comp, lease, commit } /* Record the lease in the uid hash if necessary. */ - if (enter_uid && comp -> uid) { - uid_hash_add (comp); - } + if (comp->uid) + uid_hash_add(comp); /* Record it in the hardware address hash if necessary. */ - if (enter_hwaddr && lease -> hardware_addr.hlen) { - hw_hash_add (comp); - } + if (comp->hardware_addr.hlen) + hw_hash_add(comp); #if defined (FAILOVER_PROTOCOL) comp->cltt = lease->cltt; @@ -1581,29 +1570,117 @@ int find_lease_by_hw_addr (struct lease hwaddr, hwlen, file, line); } -/* Add the specified lease to the uid hash. */ - -void uid_hash_add (lease) - struct lease *lease; +/* If the lease is preferred over the candidate, return truth. The + * 'cand' and 'lease' names are retained to read more clearly against + * the 'uid_hash_add' and 'hw_hash_add' functions (this is common logic + * to those two functions). + * + * 1) ACTIVE leases are preferred. The active lease with + * the longest lifetime is preferred over shortest. + * 2) "transitional states" are next, this time with the + * most recent CLTT. + * 3) free/backup/etc states are next, again with CLTT. In truth we + * should never see reset leases for this. + * 4) Abandoned leases are always dead last. + */ +static isc_boolean_t +client_lease_preferred(struct lease *cand, struct lease *lease) { - struct lease *head = (struct lease *)0; - struct lease *next = (struct lease *)0; + if (cand->binding_state == FTS_ACTIVE) { + if (lease->binding_state == FTS_ACTIVE && + lease->ends >= cand->ends) + return ISC_TRUE; + } else if (cand->binding_state == FTS_EXPIRED || + cand->binding_state == FTS_RELEASED) { + if (lease->binding_state == FTS_ACTIVE) + return ISC_TRUE; + + if ((lease->binding_state == FTS_EXPIRED || + lease->binding_state == FTS_RELEASED) && + lease->cltt >= cand->cltt) + return ISC_TRUE; + } else if (cand->binding_state != FTS_ABANDONED) { + if (lease->binding_state == FTS_ACTIVE || + lease->binding_state == FTS_EXPIRED || + lease->binding_state == FTS_RELEASED) + return ISC_TRUE; + + if (lease->binding_state != FTS_ABANDONED && + lease->cltt >= cand->cltt) + return ISC_TRUE; + } else /* (cand->binding_state == FTS_ABANDONED) */ { + if (lease->binding_state != FTS_ABANDONED || + lease->cltt >= cand->cltt) + return ISC_TRUE; + } + return ISC_FALSE; +} + +/* Add the specified lease to the uid hash. */ +void +uid_hash_add(struct lease *lease) +{ + struct lease *head = NULL; + struct lease *cand = NULL; + struct lease *prev = NULL; + struct lease *next = NULL; /* If it's not in the hash, just add it. */ if (!find_lease_by_uid (&head, lease -> uid, lease -> uid_len, MDL)) lease_hash_add (lease_uid_hash, lease -> uid, lease -> uid_len, lease, MDL); else { - /* Otherwise, attach it to the end of the list. */ - while (head -> n_uid) { - lease_reference (&next, head -> n_uid, MDL); - lease_dereference (&head, MDL); - lease_reference (&head, next, MDL); - lease_dereference (&next, MDL); + /* Otherwise, insert it into the list in order of its + * preference for "resuming allocation to the client." + * + * Because we don't have control of the hash bucket index + * directly, we have to remove and re-insert the client + * id into the hash if we're inserting onto the head. + */ + lease_reference(&cand, head, MDL); + while (cand != NULL) { + if (client_lease_preferred(cand, lease)) + break; + + if (prev != NULL) + lease_dereference(&prev, MDL); + lease_reference(&prev, cand, MDL); + + if (cand->n_uid != NULL) + lease_reference(&next, cand->n_uid, MDL); + + lease_dereference(&cand, MDL); + + if (next != NULL) { + lease_reference(&cand, next, MDL); + lease_dereference(&next, MDL); + } } - lease_reference (&head -> n_uid, lease, MDL); - lease_dereference (&head, MDL); + + /* If we want to insert 'before cand', and prev is NULL, + * then it was the head of the list. Assume that position. + */ + if (prev == NULL) { + lease_reference(&lease->n_uid, head, MDL); + lease_hash_delete(lease_uid_hash, lease->uid, + lease->uid_len, MDL); + lease_hash_add(lease_uid_hash, lease->uid, + lease->uid_len, lease, MDL); + } else /* (prev != NULL) */ { + if(prev->n_uid != NULL) { + lease_reference(&lease->n_uid, prev->n_uid, + MDL); + lease_dereference(&prev->n_uid, MDL); + } + lease_reference(&prev->n_uid, lease, MDL); + + lease_dereference(&prev, MDL); + } + + if (cand != NULL) + lease_dereference(&cand, MDL); + lease_dereference(&head, MDL); } } @@ -1657,11 +1734,13 @@ void uid_hash_delete (lease) /* Add the specified lease to the hardware address hash. */ -void hw_hash_add (lease) - struct lease *lease; +void +hw_hash_add(struct lease *lease) { - struct lease *head = (struct lease *)0; - struct lease *next = (struct lease *)0; + struct lease *head = NULL; + struct lease *cand = NULL; + struct lease *prev = NULL; + struct lease *next = NULL; /* If it's not in the hash, just add it. */ if (!find_lease_by_hw_addr (&head, lease -> hardware_addr.hbuf, @@ -1671,16 +1750,59 @@ void hw_hash_add (lease) lease -> hardware_addr.hlen, lease, MDL); else { - /* Otherwise, attach it to the end of the list. */ - while (head -> n_hw) { - lease_reference (&next, head -> n_hw, MDL); - lease_dereference (&head, MDL); - lease_reference (&head, next, MDL); - lease_dereference (&next, MDL); + /* Otherwise, insert it into the list in order of its + * preference for "resuming allocation to the client." + * + * Because we don't have control of the hash bucket index + * directly, we have to remove and re-insert the client + * id into the hash if we're inserting onto the head. + */ + lease_reference(&cand, head, MDL); + while (cand != NULL) { + if (client_lease_preferred(cand, lease)) + break; + + if (prev != NULL) + lease_dereference(&prev, MDL); + lease_reference(&prev, cand, MDL); + + if (cand->n_hw != NULL) + lease_reference(&next, cand->n_hw, MDL); + + lease_dereference(&cand, MDL); + + if (next != NULL) { + lease_reference(&cand, next, MDL); + lease_dereference(&next, MDL); + } } - lease_reference (&head -> n_hw, lease, MDL); - lease_dereference (&head, MDL); + /* If we want to insert 'before cand', and prev is NULL, + * then it was the head of the list. Assume that position. + */ + if (prev == NULL) { + lease_reference(&lease->n_hw, head, MDL); + lease_hash_delete(lease_hw_addr_hash, + lease->hardware_addr.hbuf, + lease->hardware_addr.hlen, MDL); + lease_hash_add(lease_hw_addr_hash, + lease->hardware_addr.hbuf, + lease->hardware_addr.hlen, + lease, MDL); + } else /* (prev != NULL) */ { + if(prev->n_hw != NULL) { + lease_reference(&lease->n_hw, prev->n_hw, + MDL); + lease_dereference(&prev->n_hw, MDL); + } + lease_reference(&prev->n_hw, lease, MDL); + + lease_dereference(&prev, MDL); + } + + if (cand != NULL) + lease_dereference(&cand, MDL); + lease_dereference(&head, MDL); } }