On Sun, 2020-01-26 at 18:34 +0000, Simon Kelley wrote:
> /62 is crazy, I don't know why I even said that. Harald, if you could
> tweak your patch work with 128-based prefixes, I think we have
> reached a
> successful conclusion.

Sure, since 128-bit int's might not be available on many platforms
where dnsmasq run I opted to support a max prefix of /64. So a prefix
between /64 - /128 are valid in config. If the user tries to use a
prefix < /64 an error is raised.

Updated patch below:




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

Add the possibility to provide list's of individual
addresses as well as prefixed ranges of ipv6 addresses
for a dhcp-host reservation.

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.
---
 man/dnsmasq.8 | 16 +++++++++++
 src/dnsmasq.h | 15 +++++++++--
 src/option.c  | 75 ++++++++++++++++++++++++++++++++++++++++-----------
 src/rfc3315.c | 66 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 155 insertions(+), 17 deletions(-)

diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index cb5cc73..46daf98 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -1079,6 +1079,22 @@ 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 multiple IPv6 addresses for a single
+dhcp-host. It is also possible to use a prefix in the configuration to
+set up a range of addresses. For example:
+\fB--dhcp-host=52:54:00:3f:5c:c0,[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,[fd12:3456::aa00/126],host1\fP
+will make the range of 4 addresses between the base address (fd12:3456::aa00)
+and the end address (fd12:3456::aa03) available to the host with hardware
+address 52:54:00:3f:5c:c0. Multiple non-prefixed, prefixed or prefixed
+wildcard addresses with only the host-identifier part can be mixed, eg:
+\fB--dhcp-host=52:54:00:3f:5c:c0,[::aa03][::aa04/127][::aa08/126],host1\fP.
+Providing multiple 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..5c771b6 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -759,14 +759,24 @@ struct hwaddr_config {
   struct hwaddr_config *next;
 };
 
+#ifdef HAVE_DHCP6
+struct in6_addr_list {
+  struct in6_addr addr6;
+  int prefix;
+  unsigned long flags;
+  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 addr6; /* internal only, user opts in addr6_list */
+  struct in6_addr_list *addr6_list;
 #endif
   struct in_addr addr;
   time_t decline_time;
@@ -790,6 +800,7 @@ 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_CAND    32768    /* IPv6 address candidate(s) available */
 
 struct dhcp_opt {
   int opt, len, flags;
diff --git a/src/option.c b/src/option.c
index f110b75..9f19ca8 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,9 @@ static void dhcp_config_free(struct dhcp_config *config)
          free(tmp);
         }
       dhcp_netid_list_free(config->netid);
+#ifdef HAVE_DHCP6
+      dhcp_addr6_list_free(config->addr6_list);
+#endif
       if (config->flags & CONFIG_CLID)
         free(config->clid);
       free(config);
@@ -3264,24 +3277,56 @@ static int one_opt(int option, char *arg, char *errstr, 
char *gen_err, int comma
 #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))
+                 char *closing_bracket;
+                 char *prefix;
+                 while (strlen(arg))
                    {
-                     dhcp_config_free(new);
-                     ret_err(_("bad IPv6 address"));
-                   }
+                     struct in6_addr_list *newaddr6 = malloc(sizeof(struct 
in6_addr_list));
+                     newaddr6->next = new->addr6_list;
+                     new->addr6_list = newaddr6;
+                     new->addr6_list->flags = (option == LOPT_BANK) ? 
CONFIG_BANK : 0;
 
-                 for (i= 0; i < 8; i++)
-                   if (new->addr6.s6_addr[i] != 0)
-                     break;
+                     closing_bracket = split_chr(arg, ']'); /* split and dump 
closing bracket */
+                     arg++; /* dump open bracket */
 
-                 /* set WILDCARD if network part all zeros */
-                 if (i == 8)
-                   new->flags |= CONFIG_WILDCARD;
-                 
-                 new->flags |= CONFIG_ADDR6;
+                     prefix = split_chr(arg, '/');
+                     if (prefix == NULL)
+                       prefix = "128";
+
+                     new->addr6_list->prefix = atoi(prefix);
+
+                     if (new->addr6_list->prefix < 64)
+                       {
+                         dhcp_config_free(new);
+                         ret_err(_("bad IPv6 dhcp-host prefix, minimum prefix 
lenght is 64"));
+                       }
+                     else
+                       new->addr6_list->prefix -= 64;
+
+                     if (!inet_pton(AF_INET6, arg, &new->addr6_list->addr6))
+                       {
+                         dhcp_config_free(new);
+                         ret_err(_("bad IPv6 address"));
+                       }
+
+                     if ((new->addr6_list->prefix <= 63) &&
+                         (addr6part(&new->addr6_list->addr6) << 
new->addr6_list->prefix) != 0)
+                       {
+                         dhcp_config_free(new);
+                         ret_err(_("bad IPv6 base address with prefix"));
+                       }
+
+                     for (i = 0; i < 8; i++)
+                       if (new->addr6_list->addr6.s6_addr[i] != 0)
+                         break;
+
+                     /* set WILDCARD if network part all zeros */
+                     if (i == 8)
+                         new->addr6_list->flags |= CONFIG_WILDCARD;
+
+                     strcpy(arg, closing_bracket);
+                   }
+                 new->flags |= CONFIG_ADDR6_CAND;
                }
 #endif
              else
diff --git a/src/rfc3315.c b/src/rfc3315.c
index 9471f5c..2462efe 100644
--- a/src/rfc3315.c
+++ b/src/rfc3315.c
@@ -664,6 +664,72 @@ 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_CAND))
+             {
+               struct in6_addr_list *list;
+               struct in6_addr addr6;
+               int hextet;
+               u64 pbit;
+
+               /* unset CONFIG_ADDR6, config->addr6 */
+               if (have_config(config, CONFIG_ADDR6))
+                 {
+                   config->flags ^= CONFIG_ADDR6;
+                   config->addr6 = addr6;
+                 }
+
+               for (list = config->addr6_list; list; list = list->next)
+                 {
+                   if ((list->flags & CONFIG_WILDCARD) && 
(state->context->prefix == 64))
+                     {
+                       addr6 = state->context->start6;
+                       setaddr6part(&addr6, addr6part(&list->addr6));
+                     }
+                   else
+                       addr6 = list->addr6;
+
+                   if ((list->prefix > 63) && check_address(state, &addr6))
+                     {
+                       /* unleased address found, set config->addr6 + flags */
+                       memcpy(&config->addr6, &addr6, sizeof(struct in6_addr));
+                       config->flags |= CONFIG_ADDR6;
+                       break;
+                     }
+                   else if (list->prefix <= 63)
+                     {
+                       /* get prefix bits and invert with XOR */
+                       u64 prefixbits = (0xFFFFFFFFFFFFFFFF << (64 - 
list->prefix)) ^ 0xFFFFFFFFFFFFFFFF;
+
+                       for (pbit = 0; pbit <= prefixbits; pbit++)
+                         {
+                           if (check_address(state, &addr6))
+                             {
+                               /* unleased address found, set config->addr6 + 
flags */
+                               memcpy(&config->addr6, &addr6, sizeof(struct 
in6_addr));
+                               config->flags |= CONFIG_ADDR6;
+                               break;
+                             }
+
+                           /* increment address */
+                           for (hextet = 15; hextet >= 0; --hextet)
+                             {
+                               if (addr6.s6_addr[hextet] < 255)
+                                 {
+                                   addr6.s6_addr[hextet]++;
+                                   break;
+                                 }
+                                 else
+                                   addr6.s6_addr[hextet] = 0;
+                             }
+
+                         }
+
+                       if (have_config(config, 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



_______________________________________________
Dnsmasq-discuss mailing list
Dnsmasq-discuss@lists.thekelleys.org.uk
http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss

Reply via email to