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

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