Update decline address handling in DHCPv6 for new multi-address world.

When dhcp-host options can have many IPv6 addresses, we need
to deal with one of them being declined by a client. The other
addresses are still valid.

It seems that this logic never worked, even with only one address, since
the DECLINED flag was never tested.
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index c46bfeb..7349332 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -384,10 +384,12 @@
 #define ADDRLIST_REVONLY  4
 #define ADDRLIST_PREFIX   8
 #define ADDRLIST_WILDCARD 16
+#define ADDRLIST_DECLINED 32
 
 struct addrlist {
   union all_addr addr;
-  int flags, prefixlen; 
+  int flags, prefixlen;
+  time_t decline_time;
   struct addrlist *next;
 };
 
diff --git a/src/rfc3315.c b/src/rfc3315.c
index 99e168f..eec8776 100644
--- a/src/rfc3315.c
+++ b/src/rfc3315.c
@@ -49,8 +49,8 @@
 static void mark_context_used(struct state *state, struct in6_addr *addr);
 static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr);
 static int check_address(struct state *state, struct in6_addr *addr);
-static int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state);
-static int config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr);
+static int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state, time_t now);
+static struct addrlist *config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr);
 static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option, 
 			unsigned int *min_time, struct in6_addr *addr, time_t now);
 static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now);
@@ -679,7 +679,7 @@
 		    /* If the client asks for an address on the same network as a configured address, 
 		       offer the configured address instead, to make moving to newly-configured
 		       addresses automatic. */
-		    if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr, state))
+		    if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr, state, now))
 		      {
 			req_addr = addr;
 			mark_config_used(c, &addr);
@@ -703,7 +703,7 @@
 	    for (c = state->context; c; c = c->current) 
 	      if (!(c->flags & CONTEXT_CONF_USED) &&
 		  match_netid(c->filter, solicit_tags, plain_range) &&
-		  config_valid(config, c, &addr, state))
+		  config_valid(config, c, &addr, state, now))
 		{
 		  mark_config_used(state->context, &addr);
 		  if (have_config(config, CONFIG_TIME))
@@ -847,7 +847,7 @@
 		memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
 		
 		if ((c = address6_valid(state->context, &req_addr, tagif, 1)))
-		  config_ok = config_implies(config, c, &req_addr);
+		  config_ok = (config_implies(config, c, &req_addr) != NULL);
 		
 		if ((dynamic = address6_available(state->context, &req_addr, tagif, 1)) || c)
 		  {
@@ -1186,18 +1186,19 @@
 	      {
 		struct dhcp_lease *lease;
 		struct in6_addr addr;
-
+		struct addrlist *addr_list;
+		
 		/* align */
 		memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
 
-		if (have_config(config, CONFIG_ADDR6) && IN6_ARE_ADDR_EQUAL(&config->addr6, &addr))
+		if ((addr_list = config_implies(config, state->context, &addr)))
 		  {
 		    prettyprint_time(daemon->dhcp_buff3, DECLINE_BACKOFF);
 		    inet_ntop(AF_INET6, &addr, daemon->addrbuff, ADDRSTRLEN);
 		    my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"), 
 			      daemon->addrbuff, daemon->dhcp_buff3);
-		    config->flags |= CONFIG_DECLINED;
-		    config->decline_time = now;
+		    addr_list->flags |= ADDRLIST_DECLINED;
+		    addr_list->decline_time = now;
 		  }
 		else
 		  /* make sure this host gets a different address next time. */
@@ -1669,14 +1670,14 @@
 
 
 /* return true of *addr could have been generated from config. */
-static int config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
+static struct addrlist *config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
 {
   int prefix;
   struct in6_addr wild_addr;
   struct addrlist *addr_list;
   
   if (!config || !(config->flags & CONFIG_ADDR6))
-    return 0;
+    return NULL;
   
   for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
     {
@@ -1692,13 +1693,13 @@
 	continue;
       
       if (is_same_net6(&wild_addr, addr, prefix))
-	return 1;
+	return addr_list;
     }
   
-  return 0;
+  return NULL;
 }
 
-static int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state)
+static int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state, time_t now)
 {
   u64 addrpart, i, addresses;
   struct addrlist *addr_list;
@@ -1707,34 +1708,36 @@
     return 0;
 
   for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
-    {
-      addrpart = addr6part(&addr_list->addr.addr6);
-      addresses = 1;
-
-      if (addr_list->flags & ADDRLIST_PREFIX)
-	addresses = 1<<(128-addr_list->prefixlen);
-		
-      if ((addr_list->flags & ADDRLIST_WILDCARD))
-	{
-	  if (context->prefix != 64)
-	    continue;
-      
-	  *addr = context->start6;
-	}
-      else if (is_same_net6(&context->start6, &addr_list->addr.addr6, context->prefix))
-	*addr = addr_list->addr.addr6;
-      else
-	continue;
-      
-      for (i = 0 ; i < addresses; i++)
-	{
-	  setaddr6part(addr, addrpart+i);
-
-	  if (check_address(state, addr))
-	    return 1;
-	}
-    }
-
+    if (!(addr_list->flags && ADDRLIST_DECLINED) ||
+	difftime(now, addr_list->decline_time) >= (float)DECLINE_BACKOFF)
+      {
+	addrpart = addr6part(&addr_list->addr.addr6);
+	addresses = 1;
+	
+	if (addr_list->flags & ADDRLIST_PREFIX)
+	  addresses = 1<<(128-addr_list->prefixlen);
+	
+	if ((addr_list->flags & ADDRLIST_WILDCARD))
+	  {
+	    if (context->prefix != 64)
+	      continue;
+	    
+	    *addr = context->start6;
+	  }
+	else if (is_same_net6(&context->start6, &addr_list->addr.addr6, context->prefix))
+	  *addr = addr_list->addr.addr6;
+	else
+	  continue;
+	
+	for (i = 0 ; i < addresses; i++)
+	  {
+	    setaddr6part(addr, addrpart+i);
+	    
+	    if (check_address(state, addr))
+	      return 1;
+	  }
+      }
+  
   return 0;
 }