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

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