DHCPv4: do ICMP-ping check in all cases other that current lease.
diff --git a/CHANGELOG b/CHANGELOG
index e9112e1..111764f 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -108,6 +108,11 @@
    	    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 
diff --git a/src/dhcp.c b/src/dhcp.c
index ada1be8..5b8c319 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -643,6 +643,66 @@
   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 @@
      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 @@
 		(!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))
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index ace6b1e..ee2cd4e 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -1292,6 +1292,8 @@
 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);
diff --git a/src/rfc2131.c b/src/rfc2131.c
index 6a8f0db..dfe25da 100644
--- a/src/rfc2131.c
+++ b/src/rfc2131.c
@@ -1029,6 +1029,8 @@
 		  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 @@
 		   !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");