Blame SOURCES/dnsmasq-2.81-prefix-ranges-or-list-of-ipv6-addresses.patch

8e0267
From 9b200103342c0909def9f8d9b97cfd889be6bfd8 Mon Sep 17 00:00:00 2001
8e0267
From: Simon Kelley <simon@thekelleys.org.uk>
8e0267
Date: Mon, 3 Feb 2020 23:58:45 +0000
8e0267
Subject: [PATCH] Support prefixed ranges of ipv6 addresses in dhcp-host.
8e0267
8e0267
When a request matching the clid or mac address is
8e0267
recieved the server will iterate over all candidate
8e0267
addresses until it find's one that is not already
8e0267
leased to a different clid/iaid and advertise
8e0267
this address.
8e0267
8e0267
Using multiple reservations for a single host makes it
8e0267
possible to maintain a static leases only configuration
8e0267
which support network booting systems with UEFI firmware
8e0267
that request a new address (a new SOLICIT with a new IA_NA
8e0267
option using a new IAID) for different boot modes, for
8e0267
instance 'PXE over IPv6', and 'HTTP-Boot over IPv6'. Open
8e0267
Virtual Machine Firmware (OVMF) and most UEFI firmware
8e0267
build on the EDK2 code base exhibit this behaviour.
8e0267
8e0267
(cherry picked from commit 79aba0f10ad0157fb4f48afbbcb03f094caff97a)
8e0267
8e0267
Conflicts:
8e0267
	CHANGELOG
8e0267
	src/dhcp-common.c
8e0267
	src/dnsmasq.h
8e0267
        src/dhcp6.c
8e0267
8e0267
Extend 79aba0f10ad0157fb4f48afbbcb03f094caff97a for multiple IPv6 addresses.
8e0267
8e0267
(cherry picked from commit 137286e9baecf6a3ba97722ef1b49c851b531810)
8e0267
8e0267
Conflicts:
8e0267
	man/dnsmasq.8
8e0267
	src/dhcp-common.c
8e0267
        src/dhcp6.c
8e0267
        src/rfc3315.c
8e0267
        src/option.c
8e0267
8e0267
Fix bug with prefixed wildcard addresses in 137286e9baecf6a3ba97722ef1b49c851b531810
8e0267
8e0267
(cherry picked from commit f064188032a829efdcf3988b24ac795ff52785ec)
8e0267
8e0267
Conflicts:
8e0267
	src/rfc3315.c
8e0267
---
8e0267
 man/dnsmasq.8     |  13 +-
8e0267
 src/dhcp-common.c |  56 +++++---
8e0267
 src/dhcp6.c       |  51 +++----
8e0267
 src/dnsmasq.h     |  17 +--
8e0267
 src/option.c      | 402 ++++++++++++++++++++++++++++++------------------------
8e0267
 src/rfc3315.c     |  83 ++++++++++-
8e0267
 6 files changed, 370 insertions(+), 252 deletions(-)
8e0267
8e0267
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
8e0267
index f52762f..2c9d9f6 100644
8e0267
--- a/man/dnsmasq.8
8e0267
+++ b/man/dnsmasq.8
8e0267
@@ -985,13 +985,20 @@ allowed to specify the client ID as text, like this:
8e0267
 .B --dhcp-host=id:clientidastext,..... 
8e0267
 
8e0267
 A single
8e0267
-.B dhcp-host 
8e0267
-may contain an IPv4 address or an IPv6 address, or both. IPv6 addresses must be bracketed by square brackets thus:
8e0267
+.B --dhcp-host
8e0267
+may contain an IPv4 address or one or more IPv6 addresses, or both. IPv6 addresses must be bracketed by square brackets thus:
8e0267
 .B --dhcp-host=laptop,[1234::56]
8e0267
 IPv6 addresses may contain only the host-identifier part:
8e0267
 .B --dhcp-host=laptop,[::56]
8e0267
 in which case they act as wildcards in constructed dhcp ranges, with
8e0267
-the appropriate network part inserted. 
8e0267
+the appropriate network part inserted. For IPv6, an address may include a prefix length:
8e0267
+.B --dhcp-host=laptop,[1234:50/126]
8e0267
+which (in this case) specifies four addresses, 1234::50 to 1234::53. This (an the ability
8e0267
+to specify multiple addresses) is useful
8e0267
+when a host presents either a consistent name or hardware-ID, but varying DUIDs, since it allows
8e0267
+dnsmasq to honour the static address allocation but assign a different adddress for each DUID. This
8e0267
+typically occurs when chain netbooting, as each stage of the chain gets in turn allocates an address.
8e0267
+
8e0267
 Note that in IPv6 DHCP, the hardware address may not be
8e0267
 available, though it normally is for direct-connected clients, or
8e0267
 clients using DHCP relays which support RFC 6939.
8e0267
diff --git a/src/dhcp-common.c b/src/dhcp-common.c
8e0267
index d9719d1..5d437dd 100644
8e0267
--- a/src/dhcp-common.c
8e0267
+++ b/src/dhcp-common.c
8e0267
@@ -271,26 +271,35 @@ static int is_config_in_context(struct dhcp_context *context, struct dhcp_config
8e0267
 {
8e0267
   if (!context) /* called via find_config() from lease_update_from_configs() */
8e0267
     return 1; 
8e0267
-
8e0267
-  if (!(config->flags & (CONFIG_ADDR | CONFIG_ADDR6)))
8e0267
-    return 1;
8e0267
   
8e0267
 #ifdef HAVE_DHCP6
8e0267
-  if ((context->flags & CONTEXT_V6) && (config->flags & CONFIG_WILDCARD))
8e0267
-    return 1;
8e0267
-#endif
8e0267
+  if (context->flags & CONTEXT_V6)
8e0267
+    {
8e0267
+       struct addrlist *addr_list;
8e0267
 
8e0267
-  for (; context; context = context->current)
8e0267
-#ifdef HAVE_DHCP6
8e0267
-    if (context->flags & CONTEXT_V6) 
8e0267
-      {
8e0267
-	if ((config->flags & CONFIG_ADDR6) && is_same_net6(&config->addr6, &context->start6, context->prefix))
8e0267
-	  return 1;
8e0267
-      }
8e0267
-    else 
8e0267
+       if (!(config->flags & CONFIG_ADDR6))
8e0267
+	 return 1;
8e0267
+       
8e0267
+        for (; context; context = context->current)
8e0267
+	  for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
8e0267
+	    {
8e0267
+	      if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64)
8e0267
+		return 1;
8e0267
+	      
8e0267
+	      if (is_same_net6(&addr_list->addr.addr.addr6, &context->start6, context->prefix))
8e0267
+		return 1;
8e0267
+	    }
8e0267
+    }
8e0267
+  else
8e0267
 #endif
8e0267
-      if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask))
8e0267
+    {
8e0267
+      if (!(config->flags & CONFIG_ADDR))
8e0267
 	return 1;
8e0267
+      
8e0267
+      for (; context; context = context->current)
8e0267
+	if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask))
8e0267
+	  return 1;
8e0267
+    }
8e0267
 
8e0267
   return 0;
8e0267
 }
8e0267
@@ -418,10 +427,21 @@ void dhcp_update_configs(struct dhcp_config *configs)
8e0267
 
8e0267
 #ifdef HAVE_DHCP6
8e0267
 	    if (prot == AF_INET6 && 
8e0267
-		(!(conf_tmp = config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 128, 0)) || conf_tmp == config))
8e0267
+		(!(conf_tmp = config_find_by_address6(configs, NULL, 0, &crec->addr.addr.addr.addr6)) || conf_tmp == config))
8e0267
 	      {
8e0267
-		memcpy(&config->addr6, &crec->addr.addr.addr.addr6, IN6ADDRSZ);
8e0267
-		config->flags |= CONFIG_ADDR6 | CONFIG_ADDR_HOSTS;
8e0267
+		/* host must have exactly one address if comming from /etc/hosts. */
8e0267
+		if (!config->addr6 && (config->addr6 = whine_malloc(sizeof(struct addrlist))))
8e0267
+		  {
8e0267
+		    config->addr6->next = NULL;
8e0267
+		    config->addr6->flags = 0;
8e0267
+		  }
8e0267
+
8e0267
+		if (config->addr6 && !config->addr6->next && !(config->addr6->flags & (ADDRLIST_WILDCARD|ADDRLIST_PREFIX)))
8e0267
+		  {
8e0267
+		    memcpy(&config->addr6->addr.addr.addr6, &crec->addr.addr.addr.addr6, IN6ADDRSZ);
8e0267
+		    config->flags |= CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS;
8e0267
+		  }
8e0267
+	    
8e0267
 		continue;
8e0267
 	      }
8e0267
 #endif
8e0267
diff --git a/src/dhcp6.c b/src/dhcp6.c
8e0267
index 0853664..6f1f54e 100644
8e0267
--- a/src/dhcp6.c
8e0267
+++ b/src/dhcp6.c
8e0267
@@ -384,21 +384,26 @@ static int complete_context6(struct in6_addr *local,  int prefix,
8e0267
  return 1;
8e0267
 }
8e0267
 
8e0267
-struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
8e0267
+struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix,  struct in6_addr *addr)
8e0267
 {
8e0267
   struct dhcp_config *config;
8e0267
   
8e0267
   for (config = configs; config; config = config->next)
8e0267
-    if ((config->flags & CONFIG_ADDR6) &&
8e0267
-	is_same_net6(&config->addr6, net, prefix) &&
8e0267
-	(prefix == 128 || addr6part(&config->addr6) == addr))
8e0267
-      return config;
8e0267
+    if (config->flags & CONFIG_ADDR6)
8e0267
+      {
8e0267
+	struct addrlist *addr_list;
8e0267
+	
8e0267
+	for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
8e0267
+	  if ((!net || is_same_net6(&addr_list->addr.addr.addr6, net, prefix) || ((addr_list->flags & ADDRLIST_WILDCARD) && prefix == 64)) &&
8e0267
+	      is_same_net6(&addr_list->addr.addr.addr6, addr, (addr_list->flags & ADDRLIST_PREFIX) ? addr_list->prefixlen : 128))
8e0267
+	    return config;
8e0267
+      }
8e0267
   
8e0267
   return NULL;
8e0267
 }
8e0267
 
8e0267
 struct dhcp_context *address6_allocate(struct dhcp_context *context,  unsigned char *clid, int clid_len, int temp_addr,
8e0267
-				       int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)   
8e0267
+				       unsigned int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)   
8e0267
 {
8e0267
   /* Find a free address: exclude anything in use and anything allocated to
8e0267
      a particular hwaddr/clientid/hostname in our configuration.
8e0267
@@ -453,16 +458,15 @@ struct dhcp_context *address6_allocate(struct dhcp_context *context,  unsigned c
8e0267
 	    for (d = context; d; d = d->current)
8e0267
 	      if (addr == addr6part(&d->local6))
8e0267
 		break;
8e0267
+	    
8e0267
+	    *ans = c->start6;
8e0267
+	    setaddr6part (ans, addr);
8e0267
 
8e0267
 	    if (!d &&
8e0267
 		!lease6_find_by_addr(&c->start6, c->prefix, addr) && 
8e0267
-		!config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))
8e0267
-	      {
8e0267
-		*ans = c->start6;
8e0267
-		setaddr6part (ans, addr);
8e0267
-		return c;
8e0267
-	      }
8e0267
-	
8e0267
+		!config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, ans))
8e0267
+	      return c;
8e0267
+	    
8e0267
 	    addr++;
8e0267
 	    
8e0267
 	    if (addr  == addr6part(&c->end6) + 1)
8e0267
@@ -516,27 +520,6 @@ struct dhcp_context *address6_valid(struct dhcp_context *context,
8e0267
   return NULL;
8e0267
 }
8e0267
 
8e0267
-int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
8e0267
-{
8e0267
-  if (!config || !(config->flags & CONFIG_ADDR6))
8e0267
-    return 0;
8e0267
-
8e0267
-  if ((config->flags & CONFIG_WILDCARD) && context->prefix == 64)
8e0267
-    {
8e0267
-      *addr = context->start6;
8e0267
-      setaddr6part(addr, addr6part(&config->addr6));
8e0267
-      return 1;
8e0267
-    }
8e0267
-  
8e0267
-  if (is_same_net6(&context->start6, &config->addr6, context->prefix))
8e0267
-    {
8e0267
-      *addr = config->addr6;
8e0267
-      return 1;
8e0267
-    }
8e0267
-  
8e0267
-  return 0;
8e0267
-}
8e0267
-
8e0267
 void make_duid(time_t now)
8e0267
 {
8e0267
   (void)now;
8e0267
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
8e0267
index 6b18bb7..9437226 100644
8e0267
--- a/src/dnsmasq.h
8e0267
+++ b/src/dnsmasq.h
8e0267
@@ -343,9 +343,11 @@ struct ds_config {
8e0267
   struct ds_config *next;
8e0267
 };
8e0267
 
8e0267
-#define ADDRLIST_LITERAL 1
8e0267
-#define ADDRLIST_IPV6    2
8e0267
-#define ADDRLIST_REVONLY 4
8e0267
+#define ADDRLIST_LITERAL  1
8e0267
+#define ADDRLIST_IPV6     2
8e0267
+#define ADDRLIST_REVONLY  4
8e0267
+#define ADDRLIST_PREFIX   8
8e0267
+#define ADDRLIST_WILDCARD 16
8e0267
 
8e0267
 struct addrlist {
8e0267
   struct all_addr addr;
8e0267
@@ -748,7 +750,7 @@ struct dhcp_config {
8e0267
   char *hostname, *domain;
8e0267
   struct dhcp_netid_list *netid;
8e0267
 #ifdef HAVE_DHCP6
8e0267
-  struct in6_addr addr6;
8e0267
+  struct addrlist *addr6;
8e0267
 #endif
8e0267
   struct in_addr addr;
8e0267
   time_t decline_time;
8e0267
@@ -770,7 +772,7 @@ struct dhcp_config {
8e0267
 #define CONFIG_DECLINED       1024    /* address declined by client */
8e0267
 #define CONFIG_BANK           2048    /* from dhcp hosts file */
8e0267
 #define CONFIG_ADDR6          4096
8e0267
-#define CONFIG_WILDCARD       8192
8e0267
+#define CONFIG_ADDR6_HOSTS   16384    /* address added by from /etc/hosts */
8e0267
 
8e0267
 struct dhcp_opt {
8e0267
   int opt, len, flags;
8e0267
@@ -1463,8 +1465,7 @@ int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr,
8e0267
 void dhcp6_init(void);
8e0267
 void dhcp6_packet(time_t now);
8e0267
 struct dhcp_context *address6_allocate(struct dhcp_context *context,  unsigned char *clid, int clid_len, int temp_addr,
8e0267
-				       int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans);
8e0267
-int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr);
8e0267
+				       unsigned int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans);
8e0267
 struct dhcp_context *address6_available(struct dhcp_context *context, 
8e0267
 					struct in6_addr *taddr,
8e0267
 					struct dhcp_netid *netids,
8e0267
@@ -1474,7 +1475,7 @@ struct dhcp_context *address6_valid(struct dhcp_context *context,
8e0267
 				    struct dhcp_netid *netids,
8e0267
 				    int plain_range);
8e0267
 struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, 
8e0267
-					    int prefix, u64 addr);
8e0267
+					    int prefix, struct in6_addr *addr);
8e0267
 void make_duid(time_t now);
8e0267
 void dhcp_construct_contexts(time_t now);
8e0267
 void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, 
8e0267
diff --git a/src/option.c b/src/option.c
8e0267
index b12183b..ea70ee3 100644
8e0267
--- a/src/option.c
8e0267
+++ b/src/option.c
8e0267
@@ -1010,15 +1010,30 @@ static void dhcp_config_free(struct dhcp_config *config)
8e0267
   if (config)
8e0267
     {
8e0267
       struct hwaddr_config *hwaddr = config->hwaddr;
8e0267
+      
8e0267
       while (hwaddr)
8e0267
         {
8e0267
 	  struct hwaddr_config *tmp = hwaddr;
8e0267
           hwaddr = hwaddr->next;
8e0267
 	  free(tmp);
8e0267
         }
8e0267
+      
8e0267
       dhcp_netid_list_free(config->netid);
8e0267
+
8e0267
       if (config->flags & CONFIG_CLID)
8e0267
         free(config->clid);
8e0267
+
8e0267
+      if (config->flags & CONFIG_ADDR6)
8e0267
+	{
8e0267
+	  struct addrlist *addr, *tmp;
8e0267
+	  
8e0267
+	  for (addr = config->addr6; addr; addr = tmp)
8e0267
+	    {
8e0267
+	      tmp = addr->next;
8e0267
+	      free(addr);
8e0267
+	    }
8e0267
+	}
8e0267
+
8e0267
       free(config);
8e0267
     }
8e0267
 }
8e0267
@@ -3143,8 +3158,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
8e0267
     case LOPT_BANK:
8e0267
     case 'G':  /* --dhcp-host */
8e0267
       {
8e0267
-	int j, k = 0;
8e0267
-	char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
8e0267
 	struct dhcp_config *new;
8e0267
 	struct in_addr in;
8e0267
 	
8e0267
@@ -3155,197 +3168,222 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
8e0267
 	new->hwaddr = NULL;
8e0267
 	new->netid = NULL;
8e0267
 	new->clid = NULL;
8e0267
+	new->addr6 = NULL;
8e0267
 
8e0267
-	if ((a[0] = arg))
8e0267
-	  for (k = 1; k < 7; k++)
8e0267
-	    if (!(a[k] = split(a[k-1])))
8e0267
-	      break;
8e0267
-	
8e0267
-	for (j = 0; j < k; j++)
8e0267
-	  if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
8e0267
-	    {
8e0267
-	      char *arg = a[j];
8e0267
-	      
8e0267
-	      if ((arg[0] == 'i' || arg[0] == 'I') &&
8e0267
-		  (arg[1] == 'd' || arg[1] == 'D') &&
8e0267
-		  arg[2] == ':')
8e0267
-		{
8e0267
-		  if (arg[3] == '*')
8e0267
-		    new->flags |= CONFIG_NOCLID;
8e0267
-		  else
8e0267
-		    {
8e0267
-		      int len;
8e0267
-		      arg += 3; /* dump id: */
8e0267
-		      if (strchr(arg, ':'))
8e0267
-			len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
8e0267
-		      else
8e0267
-			{
8e0267
-			  unhide_metas(arg);
8e0267
-			  len = (int) strlen(arg);
8e0267
-			}
8e0267
-
8e0267
-		      if (len == -1)
8e0267
-			{
8e0267
-			  dhcp_config_free(new);
8e0267
-			  ret_err(_("bad hex constant"));
8e0267
-			}
8e0267
-		      else if ((new->clid = opt_malloc(len)))
8e0267
-			{
8e0267
-			  new->flags |= CONFIG_CLID;
8e0267
-			  new->clid_len = len;
8e0267
-			  memcpy(new->clid, arg, len);
8e0267
-			}
8e0267
-		    }
8e0267
-		}
8e0267
-	      /* dhcp-host has strange backwards-compat needs. */
8e0267
-	      else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
8e0267
-		{
8e0267
-		  struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
8e0267
-		  newlist->next = new->netid;
8e0267
-		  new->netid = newlist;
8e0267
-		  newlist->list = dhcp_netid_create(arg+4, NULL);
8e0267
-		}
8e0267
-	      else if (strstr(arg, "tag:") == arg)
8e0267
-		{
8e0267
-		  
8e0267
-		  dhcp_config_free(new);
8e0267
-		  ret_err(_("cannot match tags in --dhcp-host"));
8e0267
-		}
8e0267
+	while (arg)
8e0267
+	  {
8e0267
+	    comma = split(arg);
8e0267
+	    if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */
8e0267
+	      {
8e0267
+		if ((arg[0] == 'i' || arg[0] == 'I') &&
8e0267
+		    (arg[1] == 'd' || arg[1] == 'D') &&
8e0267
+		    arg[2] == ':')
8e0267
+		  {
8e0267
+		    if (arg[3] == '*')
8e0267
+		      new->flags |= CONFIG_NOCLID;
8e0267
+		    else
8e0267
+		      {
8e0267
+			int len;
8e0267
+			arg += 3; /* dump id: */
8e0267
+			if (strchr(arg, ':'))
8e0267
+			  len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
8e0267
+			else
8e0267
+			  {
8e0267
+			    unhide_metas(arg);
8e0267
+			    len = (int) strlen(arg);
8e0267
+			  }
8e0267
+			
8e0267
+			if (len == -1)
8e0267
+			  {
8e0267
+			    dhcp_config_free(new);
8e0267
+			    ret_err(_("bad hex constant"));
8e0267
+			  }
8e0267
+			else if ((new->clid = opt_malloc(len)))
8e0267
+			  {
8e0267
+			    new->flags |= CONFIG_CLID;
8e0267
+			    new->clid_len = len;
8e0267
+			    memcpy(new->clid, arg, len);
8e0267
+			  }
8e0267
+		      }
8e0267
+		  }
8e0267
+		/* dhcp-host has strange backwards-compat needs. */
8e0267
+		else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
8e0267
+		  {
8e0267
+		    struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
8e0267
+		    newlist->next = new->netid;
8e0267
+		    new->netid = newlist;
8e0267
+		    newlist->list = dhcp_netid_create(arg+4, NULL);
8e0267
+		  }
8e0267
+		else if (strstr(arg, "tag:") == arg)
8e0267
+		  {
8e0267
+		    
8e0267
+		    dhcp_config_free(new);
8e0267
+		    ret_err(_("cannot match tags in --dhcp-host"));
8e0267
+		  }
8e0267
 #ifdef HAVE_DHCP6
8e0267
-	      else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
8e0267
-		{
8e0267
-		  arg[strlen(arg)-1] = 0;
8e0267
-		  arg++;
8e0267
-		  
8e0267
-		  if (!inet_pton(AF_INET6, arg, &new->addr6))
8e0267
-		    {
8e0267
-		      dhcp_config_free(new);
8e0267
-		      ret_err(_("bad IPv6 address"));
8e0267
-		    }
8e0267
-
8e0267
-		  for (i= 0; i < 8; i++)
8e0267
-		    if (new->addr6.s6_addr[i] != 0)
8e0267
-		      break;
8e0267
+		else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
8e0267
+		  {
8e0267
+		    char *pref;
8e0267
+		    struct in6_addr in6;
8e0267
+		    struct addrlist *new_addr;
8e0267
+		    
8e0267
+		    arg[strlen(arg)-1] = 0;
8e0267
+		    arg++;
8e0267
+		    pref = split_chr(arg, '/');
8e0267
+		    
8e0267
+		    if (!inet_pton(AF_INET6, arg, &in6))
8e0267
+		      {
8e0267
+			dhcp_config_free(new);
8e0267
+			ret_err(_("bad IPv6 address"));
8e0267
+		      }
8e0267
 
8e0267
-		  /* set WILDCARD if network part all zeros */
8e0267
-		  if (i == 8)
8e0267
-		    new->flags |= CONFIG_WILDCARD;
8e0267
+		    new_addr = opt_malloc(sizeof(struct addrlist));
8e0267
+		    new_addr->next = new->addr6;
8e0267
+		    new_addr->flags = 0;
8e0267
+		    new_addr->addr.addr.addr6 = in6;
8e0267
+		    new->addr6 = new_addr;
8e0267
+		    
8e0267
+		    if (pref)
8e0267
+		      {
8e0267
+			u64 addrpart = addr6part(&in6;;
8e0267
+			
8e0267
+			if (!atoi_check(pref, &new_addr->prefixlen) ||
8e0267
+			    new_addr->prefixlen > 128 ||
8e0267
+			    (((1<<(128-new_addr->prefixlen))-1) & addrpart) != 0)
8e0267
+			  {
8e0267
+			    dhcp_config_free(new);
8e0267
+			    ret_err(_("bad IPv6 prefix"));
8e0267
+			  }
8e0267
+			
8e0267
+			new_addr->flags |= ADDRLIST_PREFIX;
8e0267
+		      }
8e0267
 		  
8e0267
-		  new->flags |= CONFIG_ADDR6;
8e0267
-		}
8e0267
+		    for (i= 0; i < 8; i++)
8e0267
+		      if (in6.s6_addr[i] != 0)
8e0267
+			break;
8e0267
+		    
8e0267
+		    /* set WILDCARD if network part all zeros */
8e0267
+		    if (i == 8)
8e0267
+		      new_addr->flags |= ADDRLIST_WILDCARD;
8e0267
+		    
8e0267
+		    new->flags |= CONFIG_ADDR6;
8e0267
+		  }
8e0267
 #endif
8e0267
-	      else
8e0267
-		{
8e0267
-		  struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
8e0267
-		  if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX, 
8e0267
-						     &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
8e0267
-		    {
8e0267
-		      free(newhw);
8e0267
-		      dhcp_config_free(new);
8e0267
-		      ret_err(_("bad hex constant"));
8e0267
-		    }
8e0267
-		  else
8e0267
-		    {
8e0267
-		      newhw->next = new->hwaddr;
8e0267
-		      new->hwaddr = newhw;
8e0267
-		    }		    
8e0267
-		}
8e0267
-	    }
8e0267
-	  else if (strchr(a[j], '.') && (inet_pton(AF_INET, a[j], &in) > 0))
8e0267
-	    {
8e0267
-	      struct dhcp_config *configs;
8e0267
-	      
8e0267
-	      new->addr = in;
8e0267
-	      new->flags |= CONFIG_ADDR;
8e0267
-
8e0267
-	      /* If the same IP appears in more than one host config, then DISCOVER
8e0267
-		 for one of the hosts will get the address, but REQUEST will be NAKed,
8e0267
-		 since the address is reserved by the other one -> protocol loop. */
8e0267
-	      for (configs = daemon->dhcp_conf; configs; configs = configs->next) 
8e0267
-		if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
8e0267
+		else
8e0267
 		  {
8e0267
-		    sprintf(errstr, _("duplicate dhcp-host IP address %s"),  inet_ntoa(in));
8e0267
-		    return 0;
8e0267
-		  }	      
8e0267
-	    }
8e0267
-	  else
8e0267
-	    {
8e0267
-	      char *cp, *lastp = NULL, last = 0;
8e0267
-	      int fac = 1, isdig = 0;
8e0267
-	      
8e0267
-	      if (strlen(a[j]) > 1)
8e0267
-		{
8e0267
-		  lastp = a[j] + strlen(a[j]) - 1;
8e0267
-		  last = *lastp;
8e0267
-		  switch (last)
8e0267
+		    struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
8e0267
+		    if ((newhw->hwaddr_len = parse_hex(arg, newhw->hwaddr, DHCP_CHADDR_MAX, 
8e0267
+						       &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
8e0267
+		      {
8e0267
+			free(newhw);
8e0267
+			dhcp_config_free(new);
8e0267
+			ret_err(_("bad hex constant"));
8e0267
+		      }
8e0267
+		    else
8e0267
+		      {
8e0267
+			newhw->next = new->hwaddr;
8e0267
+			new->hwaddr = newhw;
8e0267
+		      }		    
8e0267
+		  }
8e0267
+	      }
8e0267
+	    else if (strchr(arg, '.') && (inet_pton(AF_INET, arg, &in) > 0))
8e0267
+	      {
8e0267
+		struct dhcp_config *configs;
8e0267
+		
8e0267
+		new->addr = in;
8e0267
+		new->flags |= CONFIG_ADDR;
8e0267
+		
8e0267
+		/* If the same IP appears in more than one host config, then DISCOVER
8e0267
+		   for one of the hosts will get the address, but REQUEST will be NAKed,
8e0267
+		   since the address is reserved by the other one -> protocol loop. */
8e0267
+		for (configs = daemon->dhcp_conf; configs; configs = configs->next) 
8e0267
+		  if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
8e0267
 		    {
8e0267
-		    case 'w':
8e0267
-		    case 'W':
8e0267
-		      fac *= 7;
8e0267
-		      /* fall through */
8e0267
-		    case 'd':
8e0267
-		    case 'D':
8e0267
-		      fac *= 24;
8e0267
-		      /* fall through */
8e0267
-		    case 'h':
8e0267
-		    case 'H':
8e0267
-		      fac *= 60;
8e0267
-		      /* fall through */
8e0267
-		    case 'm':
8e0267
-		    case 'M':
8e0267
-		      fac *= 60;
8e0267
-		      /* fall through */
8e0267
-		    case 's':
8e0267
-		    case 'S':
8e0267
-		      *lastp = 0;
8e0267
-		    }
8e0267
-		}
8e0267
-	      
8e0267
-	      for (cp = a[j]; *cp; cp++)
8e0267
-		if (isdigit((unsigned char)*cp))
8e0267
-		  isdig = 1;
8e0267
-		else if (*cp != ' ')
8e0267
-		  break;
8e0267
+		      sprintf(errstr, _("duplicate dhcp-host IP address %s"),  inet_ntoa(in));
8e0267
+		      return 0;
8e0267
+		    }	      
8e0267
+	      }
8e0267
+	    else
8e0267
+	      {
8e0267
+		char *cp, *lastp = NULL, last = 0;
8e0267
+		int fac = 1, isdig = 0;
8e0267
+		
8e0267
+		if (strlen(arg) > 1)
8e0267
+		  {
8e0267
+		    lastp = arg + strlen(arg) - 1;
8e0267
+		    last = *lastp;
8e0267
+		    switch (last)
8e0267
+		      {
8e0267
+		      case 'w':
8e0267
+		      case 'W':
8e0267
+			fac *= 7;
8e0267
+			/* fall through */
8e0267
+		      case 'd':
8e0267
+		      case 'D':
8e0267
+			fac *= 24;
8e0267
+			/* fall through */
8e0267
+		      case 'h':
8e0267
+		      case 'H':
8e0267
+			fac *= 60;
8e0267
+			/* fall through */
8e0267
+		      case 'm':
8e0267
+		      case 'M':
8e0267
+			fac *= 60;
8e0267
+			/* fall through */
8e0267
+		      case 's':
8e0267
+		      case 'S':
8e0267
+			*lastp = 0;
8e0267
+		      }
8e0267
+		  }
8e0267
+		
8e0267
+		for (cp = arg; *cp; cp++)
8e0267
+		  if (isdigit((unsigned char)*cp))
8e0267
+		    isdig = 1;
8e0267
+		  else if (*cp != ' ')
8e0267
+		    break;
8e0267
+
8e0267
+		if (*cp)
8e0267
+		  {
8e0267
+		    if (lastp)
8e0267
+		      *lastp = last;
8e0267
+		    if (strcmp(arg, "infinite") == 0)
8e0267
+		      {
8e0267
+			new->lease_time = 0xffffffff;
8e0267
+			new->flags |= CONFIG_TIME;
8e0267
+		      }
8e0267
+		    else if (strcmp(arg, "ignore") == 0)
8e0267
+		      new->flags |= CONFIG_DISABLE;
8e0267
+		    else
8e0267
+		      {
8e0267
+			if (!(new->hostname = canonicalise_opt(arg)) ||
8e0267
+			    !legal_hostname(new->hostname))
8e0267
+			  {
8e0267
+			    dhcp_config_free(new);
8e0267
+			    ret_err(_("bad DHCP host name"));
8e0267
+			  }
8e0267
+			
8e0267
+			new->flags |= CONFIG_NAME;
8e0267
+			new->domain = strip_hostname(new->hostname);			
8e0267
+		      }
8e0267
+		  }
8e0267
+		else if (isdig)
8e0267
+		  {
8e0267
+		    new->lease_time = atoi(arg) * fac; 
8e0267
+		    /* Leases of a minute or less confuse
8e0267
+		       some clients, notably Apple's */
8e0267
+		    if (new->lease_time < 120)
8e0267
+		      new->lease_time = 120;
8e0267
+		    new->flags |= CONFIG_TIME;
8e0267
+		  }
8e0267
+	      }
8e0267
+
8e0267
+	    arg = comma;
8e0267
+	  }
8e0267
 
8e0267
-	      if (*cp)
8e0267
-		{
8e0267
-		  if (lastp)
8e0267
-		    *lastp = last;
8e0267
-		  if (strcmp(a[j], "infinite") == 0)
8e0267
-		    {
8e0267
-		      new->lease_time = 0xffffffff;
8e0267
-		      new->flags |= CONFIG_TIME;
8e0267
-		    }
8e0267
-		  else if (strcmp(a[j], "ignore") == 0)
8e0267
-		    new->flags |= CONFIG_DISABLE;
8e0267
-		  else
8e0267
-		    {
8e0267
-		      if (!(new->hostname = canonicalise_opt(a[j])) ||
8e0267
-			  !legal_hostname(new->hostname))
8e0267
-			{
8e0267
-			  dhcp_config_free(new);
8e0267
-			  ret_err(_("bad DHCP host name"));
8e0267
-			}
8e0267
-		     
8e0267
-		      new->flags |= CONFIG_NAME;
8e0267
-		      new->domain = strip_hostname(new->hostname);			
8e0267
-		    }
8e0267
-		}
8e0267
-	      else if (isdig)
8e0267
-		{
8e0267
-		  new->lease_time = atoi(a[j]) * fac; 
8e0267
-		  /* Leases of a minute or less confuse
8e0267
-		     some clients, notably Apple's */
8e0267
-		  if (new->lease_time < 120)
8e0267
-		    new->lease_time = 120;
8e0267
-		  new->flags |= CONFIG_TIME;
8e0267
-		}
8e0267
-	    }
8e0267
-	
8e0267
 	daemon->dhcp_conf = new;
8e0267
 	break;
8e0267
       }
8e0267
-
8e0267
+      
8e0267
     case LOPT_TAG_IF:  /* --tag-if */
8e0267
       {
8e0267
 	struct tag_if *new = opt_malloc(sizeof(struct tag_if));
8e0267
diff --git a/src/rfc3315.c b/src/rfc3315.c
8e0267
index ee1cf17..ee58b57 100644
8e0267
--- a/src/rfc3315.c
8e0267
+++ b/src/rfc3315.c
8e0267
@@ -55,6 +55,8 @@ static struct prefix_class *prefix_class_from_context(struct dhcp_context *conte
8e0267
 static void mark_context_used(struct state *state, struct in6_addr *addr);
8e0267
 static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr);
8e0267
 static int check_address(struct state *state, struct in6_addr *addr);
8e0267
+static int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state);
8e0267
+static int config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr);
8e0267
 static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option, 
8e0267
 			unsigned int *min_time, struct in6_addr *addr, time_t now);
8e0267
 static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now);
8e0267
@@ -717,7 +719,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
8e0267
 		    /* If the client asks for an address on the same network as a configured address, 
8e0267
 		       offer the configured address instead, to make moving to newly-configured
8e0267
 		       addresses automatic. */
8e0267
-		    if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr) && check_address(state, &addr))
8e0267
+		    if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr, state))
8e0267
 		      {
8e0267
 			req_addr = addr;
8e0267
 			mark_config_used(c, &addr);
8e0267
@@ -745,8 +747,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
8e0267
 	    for (c = state->context; c; c = c->current) 
8e0267
 	      if (!(c->flags & CONTEXT_CONF_USED) &&
8e0267
 		  match_netid(c->filter, solicit_tags, plain_range) &&
8e0267
-		  config_valid(config, c, &addr) && 
8e0267
-		  check_address(state, &addr))
8e0267
+		  config_valid(config, c, &addr, state))
8e0267
 		{
8e0267
 		  mark_config_used(state->context, &addr);
8e0267
 		  if (have_config(config, CONFIG_TIME))
8e0267
@@ -895,14 +896,13 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
8e0267
 		struct in6_addr req_addr;
8e0267
 		struct dhcp_context *dynamic, *c;
8e0267
 		unsigned int lease_time;
8e0267
-		struct in6_addr addr;
8e0267
 		int config_ok = 0;
8e0267
 
8e0267
 		/* align. */
8e0267
 		memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
8e0267
 		
8e0267
 		if ((c = address6_valid(state->context, &req_addr, tagif, 1)))
8e0267
-		  config_ok = config_valid(config, c, &addr) && IN6_ARE_ADDR_EQUAL(&addr, &req_addr);
8e0267
+		  config_ok = config_implies(config, c, &req_addr);
8e0267
 		
8e0267
 		if ((dynamic = address6_available(state->context, &req_addr, tagif, 1)) || c)
8e0267
 		  {
8e0267
@@ -1032,12 +1032,11 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
8e0267
 		if ((this_context = address6_available(state->context, &req_addr, tagif, 1)) ||
8e0267
 		    (this_context = address6_valid(state->context, &req_addr, tagif, 1)))
8e0267
 		  {
8e0267
-		    struct in6_addr addr;
8e0267
 		    unsigned int lease_time;
8e0267
 
8e0267
 		    get_context_tag(state, this_context);
8e0267
 		    
8e0267
-		    if (config_valid(config, this_context, &addr) && IN6_ARE_ADDR_EQUAL(&addr, &req_addr) && have_config(config, CONFIG_TIME))
8e0267
+		    if (config_implies(config, this_context, &req_addr) && have_config(config, CONFIG_TIME))
8e0267
 		      lease_time = config->lease_time;
8e0267
 		    else 
8e0267
 		      lease_time = this_context->lease_time;
8e0267
@@ -1760,6 +1759,76 @@ static int check_address(struct state *state, struct in6_addr *addr)
8e0267
 }
8e0267
 
8e0267
 
8e0267
+/* return true of *addr could have been generated from config. */
8e0267
+static int config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
8e0267
+{
8e0267
+  int prefix;
8e0267
+  struct in6_addr wild_addr;
8e0267
+  struct addrlist *addr_list;
8e0267
+  
8e0267
+  if (!config || !(config->flags & CONFIG_ADDR6))
8e0267
+    return 0;
8e0267
+  
8e0267
+  for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
8e0267
+    {
8e0267
+      prefix = (addr_list->flags & ADDRLIST_PREFIX) ? addr_list->prefixlen : 128;
8e0267
+      wild_addr = addr_list->addr.addr.addr6;
8e0267
+      
8e0267
+      if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64)
8e0267
+	{
8e0267
+	  wild_addr = context->start6;
8e0267
+	  setaddr6part(&wild_addr, addr6part(&addr_list->addr.addr.addr6));
8e0267
+	}
8e0267
+      else if (!is_same_net6(&context->start6, addr, context->prefix))
8e0267
+	continue;
8e0267
+      
8e0267
+      if (is_same_net6(&wild_addr, addr, prefix))
8e0267
+	return 1;
8e0267
+    }
8e0267
+  
8e0267
+  return 0;
8e0267
+}
8e0267
+
8e0267
+static int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state)
8e0267
+{
8e0267
+  u64 addrpart, i, addresses;
8e0267
+  struct addrlist *addr_list;
8e0267
+  
8e0267
+  if (!config || !(config->flags & CONFIG_ADDR6))
8e0267
+    return 0;
8e0267
+
8e0267
+  for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
8e0267
+    {
8e0267
+      addrpart = addr6part(&addr_list->addr.addr.addr6);
8e0267
+      addresses = 1;
8e0267
+
8e0267
+      if (addr_list->flags & ADDRLIST_PREFIX)
8e0267
+	addresses = 1<<(128-addr_list->prefixlen);
8e0267
+		
8e0267
+      if ((addr_list->flags & ADDRLIST_WILDCARD))
8e0267
+	{
8e0267
+	  if (context->prefix != 64)
8e0267
+	    continue;
8e0267
+      
8e0267
+	  *addr = context->start6;
8e0267
+	}
8e0267
+      else if (is_same_net6(&context->start6, &addr_list->addr.addr.addr6, context->prefix))
8e0267
+	*addr = addr_list->addr.addr.addr6;
8e0267
+      else
8e0267
+	continue;
8e0267
+      
8e0267
+      for (i = 0 ; i < addresses; i++)
8e0267
+	{
8e0267
+	  setaddr6part(addr, addrpart+i);
8e0267
+
8e0267
+	  if (check_address(state, addr))
8e0267
+	    return 1;
8e0267
+	}
8e0267
+    }
8e0267
+
8e0267
+  return 0;
8e0267
+}
8e0267
+
8e0267
 /* Calculate valid and preferred times to send in leases/renewals. 
8e0267
 
8e0267
    Inputs are:
8e0267
-- 
8e0267
1.8.3.1
8e0267