[Dnsmasq-discuss] [PATCH] DHCPv6 - List or Range reservation for single host

Harald Jensås hjensas at redhat.com
Thu Jan 16 18:09:51 GMT 2020


Hi,

Changing the topic and dropping the history as this is a full re-write
of the patch based on the previous feedback and discussion. Instead of
multiple dhcp-host entries, a single dhcp-host entry can be defined
with either  a list: or range: of addresses, this should eliminate the
issue with ordering of entries in the configuration file.



--
Harald



>From cfd8881d57ba9e0e26c183318f0118a5ca65c705 Mon Sep 17 00:00:00 2001
From: Harald Jensås <hjensas at redhat.com>
Date: Mon, 13 Jan 2020 19:44:43 +0100
Subject: [PATCH] DHCPv6 - List or Range reservation for single host

Add the possibility to provide either a list  or a range
of ipv6 addresses for a dhcp-host reservation. When a
request matching the clid or mac address is recieved the
server will iterate over the available 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.
---
 man/dnsmasq.8 | 11 +++++++++
 src/dnsmasq.h | 13 +++++++++-
 src/option.c  | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/rfc3315.c | 60 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 149 insertions(+), 2 deletions(-)

diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index cb5cc73..454fca3 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -1079,6 +1079,17 @@ work reliably if only one of the hardware addresses is active at any
 time and there is no way for dnsmasq to enforce this. It is, for instance,
 useful to allocate a stable IP address to a laptop which
 has both wired and wireless interfaces.
+
+For DHCPv6 it is possible to provide a list or a range of IPv6 addresses.
+\fB--dhcp-host=52:54:00:3f:5c:c0,list:[fd12:3456::aa02][fd12:3456::aa04],host1\fP
+will make the two addresses \fBfd12:3456::aa02\fP and \fBfd12:3456::aa04\fP
+available to the host with hardware address 52:54:00:3f:5c:c0.
+\fB--dhcp-host=52:54:00:3f:5c:c0,range:fd12:3456::aa01-fd12:3456::aa63,host1\fP
+will make the range of addresses between the start address (fd12:3456::aa01) and
+the end address (fd12:3456::aa63) available to the host with hardware address
+52:54:00:3f:5c:c0. Providing a range or list of addresses is useful for network
+booting where individual boot stages will request addresses with different IAID's.
+
 .TP
 .B --dhcp-hostsfile=<path>
 Read DHCP host information from the specified file. If a directory
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 7fb440c..a77955b 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -759,14 +759,23 @@ struct hwaddr_config {
   struct hwaddr_config *next;
 };
 
+#ifdef HAVE_DHCP6
+struct in6_addr_list {
+  struct in6_addr addr6;
+  struct in6_addr_list *next;
+};
+#endif
+
 struct dhcp_config {
-  unsigned int flags;
+  unsigned long flags;
   int clid_len;          /* length of client identifier */
   unsigned char *clid;   /* clientid */
   char *hostname, *domain;
   struct dhcp_netid_list *netid;
 #ifdef HAVE_DHCP6
   struct in6_addr addr6;
+  struct in6_addr start6, end6; /* range of addresses */
+  struct in6_addr_list *addr6_list;
 #endif
   struct in_addr addr;
   time_t decline_time;
@@ -790,6 +799,8 @@ struct dhcp_config {
 #define CONFIG_ADDR6          4096
 #define CONFIG_WILDCARD       8192
 #define CONFIG_ADDR6_HOSTS   16384    /* address added by from /etc/hosts */
+#define CONFIG_ADDR6_RANGE   32768
+#define CONFIG_ADDR6_LIST    65536
 
 struct dhcp_opt {
   int opt, len, flags;
diff --git a/src/option.c b/src/option.c
index f110b75..2a4a9e2 100644
--- a/src/option.c
+++ b/src/option.c
@@ -1015,6 +1015,16 @@ static void dhcp_netid_list_free(struct dhcp_netid_list *netid)
     }
 }
 
+static void dhcp_addr6_list_free(struct in6_addr_list *addr6_list)
+{
+  while (addr6_list)
+    {
+      struct in6_addr_list *tmp = addr6_list;
+      addr6_list = addr6_list->next;
+      free(tmp);
+    }
+}
+
 static void dhcp_config_free(struct dhcp_config *config)
 {
   if (config)
@@ -1027,6 +1037,7 @@ static void dhcp_config_free(struct dhcp_config *config)
 	  free(tmp);
         }
       dhcp_netid_list_free(config->netid);
+      dhcp_addr6_list_free(config->addr6_list);
       if (config->flags & CONFIG_CLID)
         free(config->clid);
       free(config);
@@ -3262,8 +3273,62 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
 		  ret_err(_("cannot match tags in --dhcp-host"));
 		}
 #ifdef HAVE_DHCP6
-	      else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
+	      /* dhcp-host have a range of addresses */
+	      else if (strstr(arg, "range:") == arg)
 		{
+		  char *dash;
+		  arg += 6; /* dump range: */
+	          dash = split_chr(arg, '-');
+
+		  if (!inet_pton(AF_INET6, arg, &new->start6))
+		    {
+		      dhcp_config_free(new);
+		      ret_err(_("bad IPv6 start address"));
+		    }
+
+		  if (!inet_pton(AF_INET6, dash, &new->end6))
+		    {
+		      dhcp_config_free(new);
+		      ret_err(_("bad IPv6 end address"));
+		    }
+
+		  if (addr6part(&new->start6) > addr6part(&new->end6))
+	            {
+		      struct in6_addr tmp = new->start6;
+		      new->start6 = new->end6;
+		      new->end6 = tmp;
+	            }
+
+		  /* set CONFIG_ADDR6_RANGE flag */
+		  new->flags |= CONFIG_ADDR6_RANGE;
+
+		}
+	      /* handle list of ipv6 addresses, [addr#1][addr#2][addr#3] */
+	      else if (strstr(arg, "list:") == arg)
+	        {
+		  char *closing_bracket;
+		  arg += 5; /* dump list */
+		  while (strlen(arg))
+	            {
+		      closing_bracket = split_chr(arg, ']'); /* split and dump closing bracket */
+
+		      struct in6_addr_list *newaddr6 = opt_malloc(sizeof(struct in6_addr_list));
+		      newaddr6->next = new->addr6_list;
+		      new->addr6_list = newaddr6;
+
+		      arg++; /* dump open bracket */
+		      if (!inet_pton(AF_INET6, arg, &new->addr6_list->addr6))
+		        {
+		          dhcp_config_free(new);
+		          ret_err(_("bad IPv6 address"));
+		        }
+		      strcpy(arg, closing_bracket);
+		    }
+		    new->flags |= CONFIG_ADDR6_LIST;
+		}
+	      /* handle single address */
+	      else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
+	        {
 		  arg[strlen(arg)-1] = 0;
 		  arg++;
 		  
diff --git a/src/rfc3315.c b/src/rfc3315.c
index 9471f5c..7096ccb 100644
--- a/src/rfc3315.c
+++ b/src/rfc3315.c
@@ -664,6 +664,66 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 	    if (address_assigned)
 		address_assigned = 2;
 
+            if (have_config(config, CONFIG_ADDR6_RANGE))
+	      {
+		int octet;
+		struct in6_addr addr6;
+		addr6 = config->start6;
+
+		/* unset CONFIG_ADDR6 and config->addr6 in case this is a another iteration */
+	        if (have_config(config, CONFIG_ADDR6))
+		  {
+		    config->flags ^= CONFIG_ADDR6;
+		    config->addr6 = addr6;
+		  }
+
+		while (addr6part(&addr6) <= addr6part(&config->end6))
+		  {
+		    /* Check if address not leased to another CLID/IAID */
+		    if (check_address(state, &addr6))
+		      {
+			memcpy(&config->addr6, &addr6, sizeof(struct in6_addr));
+			config->flags |= CONFIG_ADDR6;
+			break;
+		      }
+		    else
+		      {
+			/* Increment the address */
+			for (octet = 15; octet >= 0; --octet)
+			  if (addr6.s6_addr[octet] < 255)
+			    {
+			      addr6.s6_addr[octet]++;
+			      break;
+			    }
+			  else
+			    addr6.s6_addr[octet] = 0;
+		      }
+		  }
+	      }
+	    else if (have_config(config, CONFIG_ADDR6_LIST))
+	      {
+		struct in6_addr_list *list;
+		struct in6_addr addr6;
+
+		/* unset CONFIG_ADDR6 and config->addr6 in case this is a another iteration */
+	        if (have_config(config, CONFIG_ADDR6))
+		  {
+		    config->flags ^= CONFIG_ADDR6;
+		    config->addr6 = addr6;
+		  }
+
+		for (list = config->addr6_list; list; list = list->next)
+		  {
+		    addr6 = list->addr6;
+		    if (check_address(state, &addr6))
+		      {
+		        memcpy(&config->addr6, &addr6, sizeof(struct in6_addr));
+		        config->flags |= CONFIG_ADDR6;
+		        break;
+		      }
+		  }
+	      }
+
 	    for (ia_counter = 0; ia_option; ia_counter++, ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
 	      {
 		/* worry about alignment here. */
-- 
2.24.1





More information about the Dnsmasq-discuss mailing list