Commit 5ce3e76f authored by Simon Kelley's avatar Simon Kelley

DHCPv4: do ICMP-ping check in all cases other that current lease.

parent 6ec5f5c4
......@@ -108,6 +108,11 @@ version 2.77
the internal interfaces of a router. Thanks to
Vladislav Grishenko for the patch.
Do ICMP-ping check for address-in-use for DHCPv4 when
the client specifies an address in DHCPDISCOVER, and when
an address in configured locally. Thanks to Alin Năstac
for spotting the problem.
version 2.76
Include 0.0.0.0/8 in DNS rebind checks. This range
......
......@@ -643,6 +643,66 @@ struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct i
return NULL;
}
/* Check if and address is in use by sending ICMP ping.
This wrapper handles a cache and load-limiting.
Return is NULL is address in use, or a pointer to a cache entry
recording that it isn't. */
struct ping_result *do_icmp_ping(time_t now, struct in_addr addr, unsigned int hash)
{
static struct ping_result dummy;
struct ping_result *r, *victim = NULL;
int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
((float)PING_WAIT)));
/* check if we failed to ping addr sometime in the last
PING_CACHE_TIME seconds. If so, assume the same situation still exists.
This avoids problems when a stupid client bangs
on us repeatedly. As a final check, if we did more
than 60% of the possible ping checks in the last
PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
for (count = 0, r = daemon->ping_results; r; r = r->next)
if (difftime(now, r->time) > (float)PING_CACHE_TIME)
victim = r; /* old record */
else
{
count++;
if (r->addr.s_addr == addr.s_addr)
return r;
}
/* didn't find cached entry */
if ((count >= max) || option_bool(OPT_NO_PING))
{
/* overloaded, or configured not to check, return "not in use" */
dummy.hash = 0;
return &dummy;
}
else if (icmp_ping(addr))
return NULL; /* address in use. */
else
{
/* at this point victim may hold an expired record */
if (!victim)
{
if ((victim = whine_malloc(sizeof(struct ping_result))))
{
victim->next = daemon->ping_results;
daemon->ping_results = victim;
}
}
/* record that this address is OK for 30s
without more ping checks */
if (victim)
{
victim->addr = addr;
victim->time = now;
victim->hash = hash;
}
return victim;
}
}
int address_allocate(struct dhcp_context *context,
struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
struct dhcp_netid *netids, time_t now)
......@@ -660,6 +720,10 @@ int address_allocate(struct dhcp_context *context,
dispersal even with similarly-valued "strings". */
for (j = 0, i = 0; i < hw_len; i++)
j = hwaddr[i] + (j << 6) + (j << 16) - j;
/* j == 0 is marker */
if (j == 0)
j = 1;
for (pass = 0; pass <= 1; pass++)
for (c = context; c; c = c->current)
......@@ -697,69 +761,27 @@ int address_allocate(struct dhcp_context *context,
(!IN_CLASSC(ntohl(addr.s_addr)) ||
((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
{
struct ping_result *r, *victim = NULL;
int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
((float)PING_WAIT)));
struct ping_result *r;
*addrp = addr;
/* check if we failed to ping addr sometime in the last
PING_CACHE_TIME seconds. If so, assume the same situation still exists.
This avoids problems when a stupid client bangs
on us repeatedly. As a final check, if we did more
than 60% of the possible ping checks in the last
PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
for (count = 0, r = daemon->ping_results; r; r = r->next)
if (difftime(now, r->time) > (float)PING_CACHE_TIME)
victim = r; /* old record */
else
{
count++;
if (r->addr.s_addr == addr.s_addr)
{
/* consec-ip mode: we offered this address for another client
(different hash) recently, don't offer it to this one. */
if (option_bool(OPT_CONSEC_ADDR) && r->hash != j)
break;
return 1;
}
}
if (!r)
{
if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr))
if ((r = do_icmp_ping(now, addr, j)))
{
/* consec-ip mode: we offered this address for another client
(different hash) recently, don't offer it to this one. */
if (!option_bool(OPT_CONSEC_ADDR) || r->hash == j)
{
/* address in use: perturb address selection so that we are
less likely to try this address again. */
if (!option_bool(OPT_CONSEC_ADDR))
c->addr_epoch++;
}
else
{
/* at this point victim may hold an expired record */
if (!victim)
{
if ((victim = whine_malloc(sizeof(struct ping_result))))
{
victim->next = daemon->ping_results;
daemon->ping_results = victim;
}
}
/* record that this address is OK for 30s
without more ping checks */
if (victim)
{
victim->addr = addr;
victim->time = now;
victim->hash = j;
}
*addrp = addr;
return 1;
}
}
else
{
/* address in use: perturb address selection so that we are
less likely to try this address again. */
if (!option_bool(OPT_CONSEC_ADDR))
c->addr_epoch++;
}
}
addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
......
......@@ -1292,6 +1292,8 @@ struct dhcp_context *address_available(struct dhcp_context *context,
struct dhcp_context *narrow_context(struct dhcp_context *context,
struct in_addr taddr,
struct dhcp_netid *netids);
struct ping_result *do_icmp_ping(time_t now, struct in_addr addr,
unsigned int hash);
int address_allocate(struct dhcp_context *context,
struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
struct dhcp_netid *netids, time_t now);
......
......@@ -1029,6 +1029,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
else if (have_config(config, CONFIG_DECLINED) &&
difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
else if (!do_icmp_ping(now, config->addr, 0))
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by another host"), addrs);
else
conf = config->addr;
}
......@@ -1041,7 +1043,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
!config_find_by_address(daemon->dhcp_conf, lease->addr))
mess->yiaddr = lease->addr;
else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) &&
!config_find_by_address(daemon->dhcp_conf, addr))
!config_find_by_address(daemon->dhcp_conf, addr) && do_icmp_ping(now, addr, 0))
mess->yiaddr = addr;
else if (emac_len == 0)
message = _("no unique-id");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment