[Dnsmasq-discuss] [PATCH] DHCPv6 - Multiple reservations for single host

Pali Rohár pali.rohar at gmail.com
Tue Jan 7 09:51:02 GMT 2020


Hi Harald! What are differences between your patch and mine which adds
support for it too (plus honor assignment based on MAC address)?
http://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/2019q4/013545.html

On Tuesday 07 January 2020 10:01:59 Harald Jensås wrote:
> Reposting this, as it seems my e-mail client mangled the patch by
> inserting line-breaks etc.
> 
> On Mon, 2019-12-23 at 12:24 +0100, Harald Jensas wrote:
> > Hi,
> > 
> > The patch below is a slight alteration to a possible solution
> > discussed in 
> > http://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/2017q1/011289.html
> > .
> > 
> > My approach here does not require making dhcp-host conditional on a
> > tag. However, making dhcp-host conditional on a tag would be a nice
> > addition that could be introduced as a follow up to this to have a
> > match on the tag of the final OS to keep the provisioned system
> > consistently configured with a specific address can be very handy.
> > For
> > the Openstack use-case I am working in, this however is'nt necessary.
> > 
> > I have confirmed that the patch below together with a small change in
> > Openstack Ironic (see: https://review.opendev.org/700002) solved the
> > long standing issue when doing network booting and node provisioning
> > in combination with static only dhcp configuration.
> > 
> > We are looking forward to comments and feedback regarding this
> > approach.
> > 
> > Thank you!
> > 
> > Regards
> > Harald Jensås
> > 
> 
> From 8b238dcf99dcf3332ec1c76fbb5af283db65a637 Mon Sep 17 00:00:00 2001
> From: Harald Jensås <hjensas at redhat.com>
> Date: Wed, 18 Dec 2019 23:59:11 +0100
> Subject: [PATCH] DHCPv6 - Multiple reservations for single host
> 
> This change adds support for multiple dhcpv6 host
> reservations. The same clid or hwaddr can be used in
> multiple --dhcp-host entries.
> 
> When receiving a request and a config containing an ip
> address is found, a test is done to see if the address is
> already leased to a different CLID/IAID. In case the ip
> address in the config was already used, skip_entry is
> incremented and find_config() is re-executed. find_config()
> will now skip the first config it finds, and continue
> looking for another config entry to return. This repeats
> until all possible config entries has been exhausted.
> 
> 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.
> 
> RFC 8415 which updates RFC 3315 describes a single client
> request multiple IA's of any kind. These clients do this,
> using a new SOLICIT to request each IA. The clients could
> pack all IA's in one SOLICIT, but doing it individually as
> the above mentioned implementations do should not be a
> problem.
> ---
>  src/dhcp-common.c | 19 ++++++++++++++++---
>  src/dnsmasq.h     |  3 ++-
>  src/lease.c       |  2 +-
>  src/rfc2131.c     |  6 +++---
>  src/rfc3315.c     | 29 +++++++++++++++++++++++------
>  5 files changed, 45 insertions(+), 14 deletions(-)
> 
> diff --git a/src/dhcp-common.c b/src/dhcp-common.c
> index 602873e..5e770de 100644
> --- a/src/dhcp-common.c
> +++ b/src/dhcp-common.c
> @@ -299,7 +299,8 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
>  				struct dhcp_context *context,
>  				unsigned char *clid, int clid_len,
>  				unsigned char *hwaddr, int hw_len, 
> -				int hw_type, char *hostname)
> +				int hw_type, char *hostname,
> +				int skip_entries)
>  {
>    int count, new;
>    struct dhcp_config *config, *candidate; 
> @@ -312,15 +313,23 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
>  	  if (config->clid_len == clid_len && 
>  	      memcmp(config->clid, clid, clid_len) == 0 &&
>  	      is_config_in_context(context, config))
> +	  {
> +	    if (--skip_entries > 0)
> +	      continue;
>  	    return config;
> -	  
> +	  }
> +
>  	  /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
>  	     cope with that here. This is IPv4 only. context==NULL implies IPv4, 
>  	     see lease_update_from_configs() */
>  	  if ((!context || !(context->flags & CONTEXT_V6)) && *clid == 0 && config->clid_len == clid_len-1  &&
>  	      memcmp(config->clid, clid+1, clid_len-1) == 0 &&
>  	      is_config_in_context(context, config))
> +	  {
> +	    if (--skip_entries > 0)
> +	      continue;
>  	    return config;
> +	  }
>  	}
>    
>  
> @@ -328,7 +337,11 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
>      for (config = configs; config; config = config->next)
>        if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
>  	  is_config_in_context(context, config))
> -	return config;
> +      {
> +        if (--skip_entries > 0)
> +          continue;
> +        return config;
> +      }
>    
>    if (hostname && context)
>      for (config = configs; config; config = config->next)
> diff --git a/src/dnsmasq.h b/src/dnsmasq.h
> index 8e047fc..8760517 100644
> --- a/src/dnsmasq.h
> +++ b/src/dnsmasq.h
> @@ -1563,7 +1563,8 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
>  				struct dhcp_context *context,
>  				unsigned char *clid, int clid_len,
>  				unsigned char *hwaddr, int hw_len, 
> -				int hw_type, char *hostname);
> +				int hw_type, char *hostname,
> +				int skip_entries);
>  int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type);
>  #ifdef HAVE_LINUX_NETWORK
>  char *whichdevice(void);
> diff --git a/src/lease.c b/src/lease.c
> index 081d90e..c34b90a 100644
> --- a/src/lease.c
> +++ b/src/lease.c
> @@ -230,7 +230,7 @@ void lease_update_from_configs(void)
>      if (lease->flags & (LEASE_TA | LEASE_NA))
>        continue;
>      else if ((config = find_config(daemon->dhcp_conf, NULL, lease->clid, lease->clid_len, 
> -				   lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) && 
> +				   lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL, 0)) &&
>  	     (config->flags & CONFIG_NAME) &&
>  	     (!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr))
>        lease_set_hostname(lease, config->hostname, 1, get_domain(lease->addr), NULL);
> diff --git a/src/rfc2131.c b/src/rfc2131.c
> index 033c5db..a7179c3 100644
> --- a/src/rfc2131.c
> +++ b/src/rfc2131.c
> @@ -504,7 +504,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
>    mess->op = BOOTREPLY;
>    
>    config = find_config(daemon->dhcp_conf, context, clid, clid_len, 
> -		       mess->chaddr, mess->hlen, mess->htype, NULL);
> +		       mess->chaddr, mess->hlen, mess->htype, NULL, 0);
>  
>    /* set "known" tag for known hosts */
>    if (config)
> @@ -514,7 +514,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
>        netid = &known_id;
>      }
>    else if (find_config(daemon->dhcp_conf, NULL, clid, clid_len, 
> -		       mess->chaddr, mess->hlen, mess->htype, NULL))
> +		       mess->chaddr, mess->hlen, mess->htype, NULL, 0))
>      {
>        known_id.net = "known-othernet";
>        known_id.next = netid;
> @@ -781,7 +781,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
>  		 to avoid impersonation by name. */
>  	      struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
>  						    mess->chaddr, mess->hlen, 
> -						    mess->htype, hostname);
> +						    mess->htype, hostname, 0);
>  	      if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
>  		{
>  		  config = new;
> diff --git a/src/rfc3315.c b/src/rfc3315.c
> index 2ef9073..97672b9 100644
> --- a/src/rfc3315.c
> +++ b/src/rfc3315.c
> @@ -535,10 +535,27 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
>  	     }
>  	 }
>      }	 
> -  
> -  if (state->clid &&
> -      (config = find_config(daemon->dhcp_conf, state->context, state->clid, state->clid_len, state->mac, state->mac_len, state->mac_type, NULL)) &&
> -      have_config(config, CONFIG_NAME))
> +
> +  if (state->clid)
> +  {
> +      /* Loop to find config that is not already used*/
> +      int skip_entries = 0;
> +      do {
> +        config = find_config(daemon->dhcp_conf, state->context, state->clid,
> +                             state->clid_len, state->mac, state->mac_len,
> +                             state->mac_type, NULL, skip_entries);
> +        /* Always use config with no address */
> +        if (config && !&config->addr6)
> +          break;
> +        /* Check if address not leased to another CLID/IAID */
> +        if (config && check_address(state, &config->addr6))
> +          break;
> +        /* Skip one more entry in the next find_config pass */
> +        skip_entries++;
> +      } while (config != NULL);
> +  }
> +
> +  if (state->clid && config && have_config(config, CONFIG_NAME))
>      {
>        state->hostname = config->hostname;
>        state->domain = config->domain;
> @@ -557,7 +574,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
>  	      /* Search again now we have a hostname. 
>  		 Only accept configs without CLID here, (it won't match)
>  		 to avoid impersonation by name. */
> -	      struct dhcp_config *new = find_config(daemon->dhcp_conf, state->context, NULL, 0, NULL, 0, 0, state->hostname);
> +	      struct dhcp_config *new = find_config(daemon->dhcp_conf, state->context, NULL, 0, NULL, 0, 0, state->hostname, 0);
>  	      if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
>  		config = new;
>  	    }
> @@ -583,7 +600,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
>  	ignore = 1;
>      }
>    else if (state->clid &&
> -	   find_config(daemon->dhcp_conf, NULL, state->clid, state->clid_len, state->mac, state->mac_len, state->mac_type, NULL))
> +	   find_config(daemon->dhcp_conf, NULL, state->clid, state->clid_len, state->mac, state->mac_len, state->mac_type, NULL, 0))
>      {
>        known_id.net = "known-othernet";
>        known_id.next = state->tags;

-- 
Pali Rohár
pali.rohar at gmail.com



More information about the Dnsmasq-discuss mailing list