Blob Blame History Raw
From 03936d309782da038a2982b08fce7be85d884f0e Mon Sep 17 00:00:00 2001
From: Vladislav Grishenko <themiron@mail.ru>
Date: Sun, 8 Mar 2020 15:34:34 +0000
Subject: [PATCH] Add DHCPv6 ntp-server (56) option handling.

There was discussion in the past regarding DHCPv6 NTP server option
which needs special subclassing per RFC5908.

Patch adds support for unicast, multicast IPv6 address and for FQDN string,
preserving possibly used (as suggested earlier) hex value.

Unfortunately it's still not fully free from limitations - only address list or
only fqdn value list is possible, not mixed due current
state option parsing & flagging.

(cherry picked from commit dded78b2338147daf69064d6d48c16b12744e441)
---
 src/dhcp-common.c    |  2 +-
 src/dhcp6-protocol.h |  4 ++++
 src/option.c         | 19 ++++++++++++++++---
 src/rfc3315.c        | 24 ++++++++++++++++++++----
 4 files changed, 41 insertions(+), 8 deletions(-)

diff --git a/src/dhcp-common.c b/src/dhcp-common.c
index 368d686..242a5a1 100644
--- a/src/dhcp-common.c
+++ b/src/dhcp-common.c
@@ -642,7 +642,7 @@ static const struct opttab_t opttab6[] = {
   { "sntp-server", 31,  OT_ADDR_LIST },
   { "information-refresh-time", 32, OT_TIME },
   { "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME },
-  { "ntp-server", 56,  0 },
+  { "ntp-server", 56, 0 /* OT_ADDR_LIST | OT_RFC1035_NAME */ },
   { "bootfile-url", 59, OT_NAME },
   { "bootfile-param", 60, OT_CSTRING },
   { "client-arch", 61, 2 | OT_DEC }, /* RFC 5970 */
diff --git a/src/dhcp6-protocol.h b/src/dhcp6-protocol.h
index fee5d28..05560e8 100644
--- a/src/dhcp6-protocol.h
+++ b/src/dhcp6-protocol.h
@@ -59,12 +59,16 @@
 #define OPTION6_REMOTE_ID       37
 #define OPTION6_SUBSCRIBER_ID   38
 #define OPTION6_FQDN            39
+#define OPTION6_NTP_SERVER      56
 #define OPTION6_CLIENT_MAC      79
 
 /* replace this with the real number when allocated.
    defining this also enables the relevant code. */ 
 /* #define OPTION6_PREFIX_CLASS    99 */
 
+#define NTP_SUBOPTION_SRV_ADDR  1
+#define NTP_SUBOPTION_MC_ADDR   2
+#define NTP_SUBOPTION_SRV_FQDN  3
 
 #define DHCP6SUCCESS     0
 #define DHCP6UNSPEC      1
diff --git a/src/option.c b/src/option.c
index 6fa7bbd..1382c55 100644
--- a/src/option.c
+++ b/src/option.c
@@ -1245,6 +1245,12 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
       if (!found_dig)
 	is_dec = is_addr = 0;
      
+#ifdef HAVE_DHCP6
+      /* NTP server option takes hex, addresses or FQDN */
+      if (is6 && new->opt == OPTION6_NTP_SERVER && !is_hex)
+	opt_len |= is_addr6 ? OT_ADDR_LIST : OT_RFC1035_NAME;
+#endif
+     
       /* We know that some options take addresses */
       if (opt_len & OT_ADDR_LIST)
 	{
@@ -1505,8 +1511,9 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
 	    }
 	  else if (comma && (opt_len & OT_RFC1035_NAME))
 	    {
-	      unsigned char *p = NULL, *newp, *end;
+	      unsigned char *p = NULL, *q, *newp, *end;
 	      int len = 0;
+	      int header_size = (is6 && new->opt == OPTION6_NTP_SERVER) ? 4 : 0;
 	      arg = comma;
 	      comma = split(arg);
 	      
@@ -1516,7 +1523,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
 		  if (!dom)
 		    goto_err(_("bad domain in dhcp-option"));
 		    		  
-		  newp = opt_malloc(len + strlen(dom) + 2);
+		  newp = opt_malloc(len + header_size + strlen(dom) + 2);
 		  
 		  if (p)
 		    {
@@ -1525,8 +1532,14 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
 		    }
 		  
 		  p = newp;
-		  end = do_rfc1035_name(p + len, dom, NULL);
+		  q = p + len;
+		  end = do_rfc1035_name(q + header_size, dom, NULL);
 		  *end++ = 0;
+		  if (is6 && new->opt == OPTION6_NTP_SERVER)
+		    {
+		      PUTSHORT(NTP_SUBOPTION_SRV_FQDN, q);
+		      PUTSHORT(end - q - 2, q);
+		    }
 		  len = end - p;
 		  free(dom);
 
diff --git a/src/rfc3315.c b/src/rfc3315.c
index 1f1aad8..0fa12ad 100644
--- a/src/rfc3315.c
+++ b/src/rfc3315.c
@@ -1380,23 +1380,39 @@ static struct dhcp_netid *add_options(struct state *state, int do_refresh)
 	      	  
 	      for (a = (struct in6_addr *)opt_cfg->val, j = 0; j < opt_cfg->len; j+=IN6ADDRSZ, a++)
 		{
+		  struct in6_addr *p = NULL;
+
 		  if (IN6_IS_ADDR_UNSPECIFIED(a))
 		    {
 		      if (!add_local_addrs(state->context))
-			put_opt6(state->fallback, IN6ADDRSZ);
+			p = state->fallback;
 		    }
 		  else if (IN6_IS_ADDR_ULA_ZERO(a))
 		    {
 		      if (!IN6_IS_ADDR_UNSPECIFIED(state->ula_addr))
-			put_opt6(state->ula_addr, IN6ADDRSZ);
+			p = state->ula_addr;
 		    }
 		  else if (IN6_IS_ADDR_LINK_LOCAL_ZERO(a))
 		    {
 		      if (!IN6_IS_ADDR_UNSPECIFIED(state->ll_addr))
-			put_opt6(state->ll_addr, IN6ADDRSZ);
+			p = state->ll_addr;
+		    }
+		  else
+		    p = a;
+
+		  if (!p)
+		    continue;
+		  else if (opt_cfg->opt == OPTION6_NTP_SERVER)
+		    {
+		      if (IN6_IS_ADDR_MULTICAST(p))
+			o1 = new_opt6(NTP_SUBOPTION_MC_ADDR);
+		      else
+			o1 = new_opt6(NTP_SUBOPTION_SRV_ADDR);
+		      put_opt6(p, IN6ADDRSZ);
+		      end_opt6(o1);
 		    }
 		  else
-		    put_opt6(a, IN6ADDRSZ);
+		    put_opt6(p, IN6ADDRSZ);
 		}
 
 	      end_opt6(o);
-- 
2.36.1