Blob Blame History Raw
From c491296241396d0144750f178ffd5c0e0b089a80 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Thu, 6 Feb 2020 22:09:30 +0000
Subject: [PATCH] Extend 79aba0f10ad0157fb4f48afbbcb03f094caff97a for multiple
 IPv6 addresses.

(cherry picked from commit 137286e9baecf6a3ba97722ef1b49c851b531810)
---
 man/dnsmasq.8     |   7 +-
 src/dhcp-common.c |  55 ++++--
 src/dhcp6.c       |  13 +-
 src/dnsmasq.h     |  13 +-
 src/option.c      | 416 +++++++++++++++++++++++++---------------------
 src/rfc3315.c     |  88 +++++-----
 6 files changed, 330 insertions(+), 262 deletions(-)

diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index 95cd3ca..d1caeed 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -1008,14 +1008,15 @@ allowed to specify the client ID as text, like this:
 
 A single
 .B --dhcp-host
-may contain an IPv4 address or an IPv6 address, or both. IPv6 addresses must be bracketed by square brackets thus:
+may contain an IPv4 address or one or more IPv6 addresses, or both. IPv6 addresses must be bracketed by square brackets thus:
 .B --dhcp-host=laptop,[1234::56]
 IPv6 addresses may contain only the host-identifier part:
 .B --dhcp-host=laptop,[::56]
 in which case they act as wildcards in constructed dhcp ranges, with
-the appropriate network part inserted. For IPv6, the address may include a prefix length:
+the appropriate network part inserted. For IPv6, an address may include a prefix length:
 .B --dhcp-host=laptop,[1234:50/126]
-which (in this case) specifies four addresses, 1234::50 to 1234::53. This is useful
+which (in this case) specifies four addresses, 1234::50 to 1234::53. This (an the ability
+to specify multiple addresses) is useful
 when a host presents either a consistent name or hardware-ID, but varying DUIDs, since it allows
 dnsmasq to honour the static address allocation but assign a different adddress for each DUID. This
 typically occurs when chain netbooting, as each stage of the chain gets in turn allocates an address.
diff --git a/src/dhcp-common.c b/src/dhcp-common.c
index 99d34c8..2933343 100644
--- a/src/dhcp-common.c
+++ b/src/dhcp-common.c
@@ -271,26 +271,35 @@ static int is_config_in_context(struct dhcp_context *context, struct dhcp_config
 {
   if (!context) /* called via find_config() from lease_update_from_configs() */
     return 1; 
-
-  if (!(config->flags & (CONFIG_ADDR | CONFIG_ADDR6)))
-    return 1;
   
 #ifdef HAVE_DHCP6
-  if ((context->flags & CONTEXT_V6) && (config->flags & CONFIG_WILDCARD))
-    return 1;
-#endif
+  if (context->flags & CONTEXT_V6)
+    {
+       struct addrlist *addr_list;
 
-  for (; context; context = context->current)
-#ifdef HAVE_DHCP6
-    if (context->flags & CONTEXT_V6) 
-      {
-	if ((config->flags & CONFIG_ADDR6) && is_same_net6(&config->addr6, &context->start6, context->prefix))
-	  return 1;
-      }
-    else 
+       if (!(config->flags & CONFIG_ADDR6))
+	 return 1;
+       
+        for (; context; context = context->current)
+	  for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
+	    {
+	      if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64)
+		return 1;
+	      
+	      if (is_same_net6(&addr_list->addr.addr6, &context->start6, context->prefix))
+		return 1;
+	    }
+    }
+  else
 #endif
-      if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask))
+    {
+      if (!(config->flags & CONFIG_ADDR))
 	return 1;
+      
+      for (; context; context = context->current)
+	if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask))
+	  return 1;
+    }
 
   return 0;
 }
@@ -420,9 +429,19 @@ void dhcp_update_configs(struct dhcp_config *configs)
 	    if (prot == AF_INET6 && 
 		(!(conf_tmp = config_find_by_address6(configs, NULL, 0, &crec->addr.addr.addr.addr6)) || conf_tmp == config))
 	      {
-		memcpy(&config->addr6, &crec->addr.addr.addr.addr6, IN6ADDRSZ);
-		config->flags |= CONFIG_ADDR6 | CONFIG_ADDR_HOSTS;
-		config->flags &= ~CONFIG_PREFIX;
+		/* host must have exactly one address if comming from /etc/hosts. */
+		if (!config->addr6 && (config->addr6 = whine_malloc(sizeof(struct addrlist))))
+		  {
+		    config->addr6->next = NULL;
+		    config->addr6->flags = 0;
+		  }
+
+		if (config->addr6 && !config->addr6->next && !(config->addr6->flags & (ADDRLIST_WILDCARD|ADDRLIST_PREFIX)))
+		  {
+		    memcpy(&config->addr6->addr.addr6, &crec->addr.addr6, IN6ADDRSZ);
+		    config->flags |= CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS;
+		  }
+	    
 		continue;
 	      }
 #endif
diff --git a/src/dhcp6.c b/src/dhcp6.c
index 11a9d83..4e28e61 100644
--- a/src/dhcp6.c
+++ b/src/dhcp6.c
@@ -389,10 +389,15 @@ struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct
   struct dhcp_config *config;
   
   for (config = configs; config; config = config->next)
-    if ((config->flags & CONFIG_ADDR6) &&
-	(!net || is_same_net6(&config->addr6, net, prefix)) &&
-	is_same_net6(&config->addr6, addr, (config->flags & CONFIG_PREFIX) ? config->prefix : 128))
-      return config;
+    if (config->flags & CONFIG_ADDR6)
+      {
+	struct addrlist *addr_list;
+	
+	for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
+	  if ((!net || is_same_net6(&addr_list->addr.addr6, net, prefix) || ((addr_list->flags & ADDRLIST_WILDCARD) && prefix == 64)) &&
+	      is_same_net6(&addr_list->addr.addr6, addr, (addr_list->flags & ADDRLIST_PREFIX) ? addr_list->prefixlen : 128))
+	    return config;
+      }
   
   return NULL;
 }
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 86b8168..08484ba 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -350,9 +350,11 @@ struct ds_config {
   struct ds_config *next;
 };
 
-#define ADDRLIST_LITERAL 1
-#define ADDRLIST_IPV6    2
-#define ADDRLIST_REVONLY 4
+#define ADDRLIST_LITERAL  1
+#define ADDRLIST_IPV6     2
+#define ADDRLIST_REVONLY  4
+#define ADDRLIST_PREFIX   8
+#define ADDRLIST_WILDCARD 16
 
 struct addrlist {
   struct all_addr addr;
@@ -774,8 +776,7 @@ struct dhcp_config {
   char *hostname, *domain;
   struct dhcp_netid_list *netid;
 #ifdef HAVE_DHCP6
-  struct in6_addr addr6;
-  int prefix;
+  struct addrlist *addr6;
 #endif
   struct in_addr addr;
   time_t decline_time;
@@ -797,8 +798,6 @@ struct dhcp_config {
 #define CONFIG_DECLINED       1024    /* address declined by client */
 #define CONFIG_BANK           2048    /* from dhcp hosts file */
 #define CONFIG_ADDR6          4096
-#define CONFIG_WILDCARD       8192
-#define CONFIG_PREFIX        32768    /* addr6 is a set, size given by prefix */
 
 struct dhcp_opt {
   int opt, len, flags;
diff --git a/src/option.c b/src/option.c
index 389eb02..2bbb11b 100644
--- a/src/option.c
+++ b/src/option.c
@@ -988,6 +988,19 @@ static void dhcp_config_free(struct dhcp_config *config)
       free(list);
     }
 
+#ifdef HAVE_DHCP6
+  if (config->flags & CONFIG_ADDR6)
+    {
+      struct addrlist *addr, *tmp;
+
+      for (addr = config->addr6; addr; addr = tmp)
+        {
+          tmp = addr->next;
+          free(addr);
+        }
+    }
+#endif
+
   if (config->flags & CONFIG_NAME)
     free(config->hostname);
 
@@ -995,6 +1008,11 @@ static void dhcp_config_free(struct dhcp_config *config)
 }
 
 
+      
+      
+
+
+
 /* This is too insanely large to keep in-line in the switch */
 static int parse_dhcp_opt(char *errstr, char *arg, int flags)
 {
@@ -3053,8 +3071,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
     case LOPT_BANK:
     case 'G':  /* --dhcp-host */
       {
-	int j, k = 0;
-	char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
 	struct dhcp_config *new;
 	struct in_addr in;
 	
@@ -3064,203 +3080,227 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
 	new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
 	new->hwaddr = NULL;
 	new->netid = NULL;
+	new->addr6 = NULL;
 
-	if ((a[0] = arg))
-	  for (k = 1; k < 7; k++)
-	    if (!(a[k] = split(a[k-1])))
-	      break;
-	
-	for (j = 0; j < k; j++)
-	  if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
-	    {
-	      char *arg = a[j];
-	      
-	      if ((arg[0] == 'i' || arg[0] == 'I') &&
-		  (arg[1] == 'd' || arg[1] == 'D') &&
-		  arg[2] == ':')
-		{
-		  if (arg[3] == '*')
-		    new->flags |= CONFIG_NOCLID;
-		  else
-		    {
-		      int len;
-		      arg += 3; /* dump id: */
-		      if (strchr(arg, ':'))
-			len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
-		      else
-			{
-			  unhide_metas(arg);
-			  len = (int) strlen(arg);
-			}
-
-		      if (len == -1)
-			ret_err(_("bad hex constant"));
-		      else if ((new->clid = opt_malloc(len)))
-			{
-			  new->flags |= CONFIG_CLID;
-			  new->clid_len = len;
-			  memcpy(new->clid, arg, len);
-			}
-		    }
-		}
-	      /* dhcp-host has strange backwards-compat needs. */
-	      else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
-		{
-		  struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
-		  struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
-		  newtag->net = opt_malloc(strlen(arg + 4) + 1);
-		  newlist->next = new->netid;
-		  new->netid = newlist;
-		  newlist->list = newtag;
-		  strcpy(newtag->net, arg+4);
-		  unhide_metas(newtag->net);
-		}
-	      else if (strstr(arg, "tag:") == arg)
-		ret_err(_("cannot match tags in --dhcp-host"));
+	while (arg)
+	  {
+	    comma = split(arg);
+	    if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */
+	      {
+		if ((arg[0] == 'i' || arg[0] == 'I') &&
+		    (arg[1] == 'd' || arg[1] == 'D') &&
+		    arg[2] == ':')
+		  {
+		    if (arg[3] == '*')
+		      new->flags |= CONFIG_NOCLID;
+		    else
+		      {
+			int len;
+			arg += 3; /* dump id: */
+			if (strchr(arg, ':'))
+			  len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
+			else
+			  {
+			    unhide_metas(arg);
+			    len = (int) strlen(arg);
+			  }
+			
+			if (len == -1)
+			  {
+			    dhcp_config_free(new);
+			    ret_err(_("bad hex constant"));
+			  }
+			else if ((new->clid = opt_malloc(len)))
+			  {
+			    new->flags |= CONFIG_CLID;
+			    new->clid_len = len;
+			    memcpy(new->clid, arg, len);
+			  }
+		      }
+		  }
+		/* dhcp-host has strange backwards-compat needs. */
+		else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
+		  {
+		    struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
+		    struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
+		    newtag->net = opt_malloc(strlen(arg + 4) + 1);
+		    newlist->next = new->netid;
+		    new->netid = newlist;
+		    newlist->list = newtag;
+		    strcpy(newtag->net, arg+4);
+		    unhide_metas(newtag->net);
+		  }
+		else if (strstr(arg, "tag:") == arg)
+		  {
+		    
+		    dhcp_config_free(new);
+		    ret_err(_("cannot match tags in --dhcp-host"));
+		  }
 #ifdef HAVE_DHCP6
-	      else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
-		{
-		  char *pref;
-
-		  arg[strlen(arg)-1] = 0;
-		  arg++;
-		  pref = split_chr(arg, '/');
-		  
-		  if (!inet_pton(AF_INET6, arg, &new->addr6))
-		    ret_err(_("bad IPv6 address"));
-
-		  if (pref)
-		    {
-		      u64 addrpart = addr6part(&new->addr6);
-
-		      if (!atoi_check(pref, &new->prefix) ||
-			  new->prefix > 128 ||
-			  (((1<<(128-new->prefix))-1) & addrpart) != 0)
-			{
-			  dhcp_config_free(new);
-			  ret_err(_("bad IPv6 prefix"));
-			}
-		      
-		      new->flags |= CONFIG_PREFIX;
-		    }
-		  
-		  for (i= 0; i < 8; i++)
-		    if (new->addr6.s6_addr[i] != 0)
-		      break;
+		else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
+		  {
+		    char *pref;
+		    struct in6_addr in6;
+		    struct addrlist *new_addr;
+		    
+		    arg[strlen(arg)-1] = 0;
+		    arg++;
+		    pref = split_chr(arg, '/');
+		    
+		    if (!inet_pton(AF_INET6, arg, &in6))
+		      {
+			dhcp_config_free(new);
+			ret_err(_("bad IPv6 address"));
+		      }
 
-		  /* set WILDCARD if network part all zeros */
-		  if (i == 8)
-		    new->flags |= CONFIG_WILDCARD;
+		    new_addr = opt_malloc(sizeof(struct addrlist));
+		    new_addr->next = new->addr6;
+		    new_addr->flags = 0;
+		    new_addr->addr.addr6 = in6;
+		    new->addr6 = new_addr;
+		    
+		    if (pref)
+		      {
+			u64 addrpart = addr6part(&in6);
+			
+			if (!atoi_check(pref, &new_addr->prefixlen) ||
+			    new_addr->prefixlen > 128 ||
+			    (((1<<(128-new_addr->prefixlen))-1) & addrpart) != 0)
+			  {
+			    dhcp_config_free(new);
+			    ret_err(_("bad IPv6 prefix"));
+			  }
+			
+			new_addr->flags |= ADDRLIST_PREFIX;
+		      }
 		  
-		  new->flags |= CONFIG_ADDR6;
-		}
+		    for (i= 0; i < 8; i++)
+		      if (in6.s6_addr[i] != 0)
+			break;
+		    
+		    /* set WILDCARD if network part all zeros */
+		    if (i == 8)
+		      new_addr->flags |= ADDRLIST_WILDCARD;
+		    
+		    new->flags |= CONFIG_ADDR6;
+		  }
 #endif
-	      else
-		{
-		  struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
-		  if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX, 
-						     &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
-		    ret_err(_("bad hex constant"));
-		  else
-		    {
-		      
-		      newhw->next = new->hwaddr;
-		      new->hwaddr = newhw;
-		    }		    
-		}
-	    }
-	  else if (strchr(a[j], '.') && (inet_pton(AF_INET, a[j], &in) > 0))
-	    {
-	      struct dhcp_config *configs;
-	      
-	      new->addr = in;
-	      new->flags |= CONFIG_ADDR;
-
-	      /* If the same IP appears in more than one host config, then DISCOVER
-		 for one of the hosts will get the address, but REQUEST will be NAKed,
-		 since the address is reserved by the other one -> protocol loop. */
-	      for (configs = daemon->dhcp_conf; configs; configs = configs->next) 
-		if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
+		else
 		  {
-		    sprintf(errstr, _("duplicate dhcp-host IP address %s"),  inet_ntoa(in));
-		    return 0;
-		  }	      
-	    }
-	  else
-	    {
-	      char *cp, *lastp = NULL, last = 0;
-	      int fac = 1, isdig = 0;
-	      
-	      if (strlen(a[j]) > 1)
-		{
-		  lastp = a[j] + strlen(a[j]) - 1;
-		  last = *lastp;
-		  switch (last)
+		    struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
+		    if ((newhw->hwaddr_len = parse_hex(arg, newhw->hwaddr, DHCP_CHADDR_MAX, 
+						       &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
+		      {
+			free(newhw);
+			dhcp_config_free(new);
+			ret_err(_("bad hex constant"));
+		      }
+		    else
+		      {
+		      
+			newhw->next = new->hwaddr;
+			new->hwaddr = newhw;
+		      }		    
+		  }
+	      }
+	    else if (strchr(arg, '.') && (inet_pton(AF_INET, arg, &in) > 0))
+	      {
+		struct dhcp_config *configs;
+		
+		new->addr = in;
+		new->flags |= CONFIG_ADDR;
+		
+		/* If the same IP appears in more than one host config, then DISCOVER
+		   for one of the hosts will get the address, but REQUEST will be NAKed,
+		   since the address is reserved by the other one -> protocol loop. */
+		for (configs = daemon->dhcp_conf; configs; configs = configs->next) 
+		  if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
 		    {
-		    case 'w':
-		    case 'W':
-		      fac *= 7;
-		      /* fall through */
-		    case 'd':
-		    case 'D':
-		      fac *= 24;
-		      /* fall through */
-		    case 'h':
-		    case 'H':
-		      fac *= 60;
-		      /* fall through */
-		    case 'm':
-		    case 'M':
-		      fac *= 60;
-		      /* fall through */
-		    case 's':
-		    case 'S':
-		      *lastp = 0;
-		    }
-		}
-	      
-	      for (cp = a[j]; *cp; cp++)
-		if (isdigit((unsigned char)*cp))
-		  isdig = 1;
-		else if (*cp != ' ')
-		  break;
+		      sprintf(errstr, _("duplicate dhcp-host IP address %s"),  inet_ntoa(in));
+		      return 0;
+		    }	      
+	      }
+	    else
+	      {
+		char *cp, *lastp = NULL, last = 0;
+		int fac = 1, isdig = 0;
+		
+		if (strlen(arg) > 1)
+		  {
+		    lastp = arg + strlen(arg) - 1;
+		    last = *lastp;
+		    switch (last)
+		      {
+		      case 'w':
+		      case 'W':
+			fac *= 7;
+			/* fall through */
+		      case 'd':
+		      case 'D':
+			fac *= 24;
+			/* fall through */
+		      case 'h':
+		      case 'H':
+			fac *= 60;
+			/* fall through */
+		      case 'm':
+		      case 'M':
+			fac *= 60;
+			/* fall through */
+		      case 's':
+		      case 'S':
+			*lastp = 0;
+		      }
+		  }
+		
+		for (cp = arg; *cp; cp++)
+		  if (isdigit((unsigned char)*cp))
+		    isdig = 1;
+		  else if (*cp != ' ')
+		    break;
+
+		if (*cp)
+		  {
+		    if (lastp)
+		      *lastp = last;
+		    if (strcmp(arg, "infinite") == 0)
+		      {
+			new->lease_time = 0xffffffff;
+			new->flags |= CONFIG_TIME;
+		      }
+		    else if (strcmp(arg, "ignore") == 0)
+		      new->flags |= CONFIG_DISABLE;
+		    else
+		      {
+			if (!(new->hostname = canonicalise_opt(arg)) ||
+			    !legal_hostname(new->hostname))
+			  {
+			    dhcp_config_free(new);
+			    ret_err(_("bad DHCP host name"));
+			  }
+			
+			new->flags |= CONFIG_NAME;
+			new->domain = strip_hostname(new->hostname);			
+		      }
+		  }
+		else if (isdig)
+		  {
+		    new->lease_time = atoi(arg) * fac; 
+		    /* Leases of a minute or less confuse
+		       some clients, notably Apple's */
+		    if (new->lease_time < 120)
+		      new->lease_time = 120;
+		    new->flags |= CONFIG_TIME;
+		  }
+	      }
+
+	    arg = comma;
+	  }
 
-	      if (*cp)
-		{
-		  if (lastp)
-		    *lastp = last;
-		  if (strcmp(a[j], "infinite") == 0)
-		    {
-		      new->lease_time = 0xffffffff;
-		      new->flags |= CONFIG_TIME;
-		    }
-		  else if (strcmp(a[j], "ignore") == 0)
-		    new->flags |= CONFIG_DISABLE;
-		  else
-		    {
-		      if (!(new->hostname = canonicalise_opt(a[j])) ||
-			  !legal_hostname(new->hostname))
-			ret_err(_("bad DHCP host name"));
-		     
-		      new->flags |= CONFIG_NAME;
-		      new->domain = strip_hostname(new->hostname);			
-		    }
-		}
-	      else if (isdig)
-		{
-		  new->lease_time = atoi(a[j]) * fac; 
-		  /* Leases of a minute or less confuse
-		     some clients, notably Apple's */
-		  if (new->lease_time < 120)
-		    new->lease_time = 120;
-		  new->flags |= CONFIG_TIME;
-		}
-	    }
-	
 	daemon->dhcp_conf = new;
 	break;
       }
-
+      
     case LOPT_TAG_IF:  /* --tag-if */
       {
 	struct tag_if *new = opt_malloc(sizeof(struct tag_if));
diff --git a/src/rfc3315.c b/src/rfc3315.c
index f4f032e..9dc33f9 100644
--- a/src/rfc3315.c
+++ b/src/rfc3315.c
@@ -1793,68 +1793,72 @@ static int config_implies(struct dhcp_config *config, struct dhcp_context *conte
 {
   int prefix;
   struct in6_addr wild_addr;
-
+  struct addrlist *addr_list;
+  
   if (!config || !(config->flags & CONFIG_ADDR6))
     return 0;
   
-  prefix = (config->flags & CONFIG_PREFIX) ? config->prefix : 128;
-  wild_addr = config->addr6;
-    
-  if (!is_same_net6(&context->start6, addr, context->prefix))
-    return 0;
-
-  if ((config->flags & CONFIG_WILDCARD))
+  for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
     {
-      if (context->prefix != 64)
-	return 0;
+      prefix = (addr_list->flags & ADDRLIST_PREFIX) ? addr_list->prefixlen : 128;
+      wild_addr = addr_list->addr.addr6;
+      
+      if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64)
+	{
+	  wild_addr = context->start6;
+	  setaddr6part(&wild_addr, addr6part(&addr_list->addr.addr6));
+	}
+      else if (!is_same_net6(&context->start6, addr, context->prefix))
+	continue;
       
-      wild_addr = context->start6;
-      setaddr6part(&wild_addr, addr6part(&config->addr6));
+      if (is_same_net6(&wild_addr, addr, prefix))
+	return 1;
     }
   
-  if (is_same_net6(&wild_addr, addr, prefix))
-    return 1;
-
   return 0;
 }
 
 static int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state)
 {
   u64 addrpart;
-
+  struct addrlist *addr_list;
+  
   if (!config || !(config->flags & CONFIG_ADDR6))
     return 0;
 
-  addrpart  = addr6part(&config->addr6);
-
-  if ((config->flags & CONFIG_WILDCARD))
+  for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
     {
-      if (context->prefix != 64)
-	return 0;
+      addrpart = addr6part(&addr_list->addr.addr6);
+
+      if ((addr_list->flags & ADDRLIST_WILDCARD))
+	{
+	  if (context->prefix != 64)
+	    continue;
       
-      *addr = context->start6;
-      setaddr6part(addr, addrpart);
+	  *addr = context->start6;
+	  setaddr6part(addr, addrpart);
+	}
+      else if (is_same_net6(&context->start6, &addr_list->addr.addr6, context->prefix))
+	*addr = addr_list->addr.addr6;
+      else
+	continue;
+      
+      while(1)
+	{
+	  if (check_address(state, addr))
+	    return 1;
+	  
+	  if (!(addr_list->flags & ADDRLIST_PREFIX))
+	    break;
+	  
+	  addrpart++;
+	  setaddr6part(addr, addrpart);
+	  if (!is_same_net6(addr, &addr_list->addr.addr6, addr_list->prefixlen))
+	    break;
+	}
     }
-  else if (is_same_net6(&context->start6, &config->addr6, context->prefix))
-    *addr = config->addr6;
-  else
-   return 0;
 
-  while(1) {
-    if (check_address(state, addr))
-      return 1;
-    
-    if (!(config->flags & CONFIG_PREFIX))
-      return 0;
-    
-    /* config may specify a set of addresses, return first one not in use
-       by another client */
-    
-    addrpart++;
-    setaddr6part(addr, addrpart);
-    if (!is_same_net6(addr, &config->addr6, config->prefix))
-      return 0;
-  }
+  return 0;
 }
 
 /* Calculate valid and preferred times to send in leases/renewals. 
-- 
2.21.1