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