]> git.puffer.fish Git - matthieu/frr.git/commitdiff
zebra: support DNS configuration options in rtadv
authorLars Seipel <ls@slrz.net>
Sat, 26 Jan 2019 22:51:48 +0000 (23:51 +0100)
committerLars Seipel <ls@slrz.net>
Sat, 2 Feb 2019 18:10:19 +0000 (19:10 +0100)
Add support for the RDNSS and DNSSL router advertisement
options described in RFC 8106.

Signed-off-by: Lars Seipel <ls@slrz.net>
configure.ac
zebra/interface.c
zebra/interface.h
zebra/rtadv.c
zebra/rtadv.h
zebra/zebra_memory.c
zebra/zebra_memory.h

index 0c15b501f056e51cf10e381bfccdb399c55fb157..17227f2e0b40fce34e8c90f806dcd175c3d72627 100755 (executable)
@@ -1738,7 +1738,8 @@ AC_CHECK_TYPES([
        vifi_t, struct sioc_vif_req, struct igmpmsg,
        struct ifaliasreq, struct if6_aliasreq, struct in6_aliasreq,
        struct nd_opt_adv_interval, struct rt_addrinfo,
-       struct nd_opt_homeagent_info, struct nd_opt_adv_interval],
+       struct nd_opt_homeagent_info, struct nd_opt_adv_interval,
+       struct nd_opt_rdnss, struct nd_opt_dnssl],
        [], [], FRR_INCLUDES)
 
 AC_CHECK_MEMBERS([struct sockaddr.sa_len,
index 8bb5c6e8ef6a34e75a2db25c20c1d1d475830383..553435d2898da566cdf2378ccc5cbf4ede836216 100644 (file)
@@ -135,6 +135,8 @@ static int if_zebra_new_hook(struct interface *ifp)
                rtadv->DefaultPreference = RTADV_PREF_MEDIUM;
 
                rtadv->AdvPrefixList = list_new();
+               rtadv->AdvRDNSSList = list_new();
+               rtadv->AdvDNSSLList = list_new();
        }
 #endif /* HAVE_RTADV */
 
@@ -175,6 +177,8 @@ static int if_zebra_delete_hook(struct interface *ifp)
 
                rtadv = &zebra_if->rtadv;
                list_delete(&rtadv->AdvPrefixList);
+               list_delete(&rtadv->AdvRDNSSList);
+               list_delete(&rtadv->AdvDNSSLList);
 #endif /* HAVE_RTADV */
 
                THREAD_OFF(zebra_if->speed_update);
index 01dd6977729dc9f8c78d456ca68556b31e0b5797..1dbcf33fad84eadf3e7d2b478ea4ace70a738d8e 100644 (file)
@@ -168,6 +168,22 @@ struct rtadvconf {
        int DefaultPreference;
 #define RTADV_PREF_MEDIUM 0x0 /* Per RFC4191. */
 
+       /*
+        * List of recursive DNS servers to include in the RDNSS option.
+        * See [RFC8106 5.1]
+        *
+        * Default: empty list; do not emit RDNSS option
+        */
+       struct list *AdvRDNSSList;
+
+       /*
+        * List of DNS search domains to include in the DNSSL option.
+        * See [RFC8106 5.2]
+        *
+        * Default: empty list; do not emit DNSSL option
+        */
+       struct list *AdvDNSSLList;
+
        uint8_t inFastRexmit; /* True if we're rexmits faster than usual */
 
        /* Track if RA was configured by BGP or by the Operator or both */
@@ -182,6 +198,41 @@ struct rtadvconf {
 #define RTADV_NUM_FAST_REXMITS   4 /* Fast Rexmit RA 4 times on certain events */
 };
 
+struct rtadv_rdnss {
+       /* Address of recursive DNS server to advertise */
+       struct in6_addr addr;
+
+       /*
+        * Lifetime in seconds; all-ones means infinity, zero
+        * stop using it.
+        */
+       uint32_t lifetime;
+
+       /* If lifetime not set, use a default of 3*MaxRtrAdvInterval */
+       int lifetime_set;
+};
+
+/*
+ * [RFC1035 2.3.4] sets the maximum length of a domain name (a sequence of
+ * labels, each prefixed by a length octet) at 255 octets.
+ */
+#define RTADV_MAX_ENCODED_DOMAIN_NAME 255
+
+struct rtadv_dnssl {
+       /* Domain name without trailing root zone dot (NUL-terminated) */
+       char name[RTADV_MAX_ENCODED_DOMAIN_NAME - 1];
+
+       /* Name encoded as in [RFC1035 3.1] */
+       uint8_t encoded_name[RTADV_MAX_ENCODED_DOMAIN_NAME];
+
+       /* Actual length of encoded_name */
+       size_t encoded_len;
+
+       /* Lifetime as for RDNSS */
+       uint32_t lifetime;
+       int lifetime_set;
+};
+
 #endif /* HAVE_RTADV */
 
 /* Zebra interface type - ones of interest. */
index a22f6395c97ea719ea52a180a32a598b3b711ecb..c5d28ddfb3719040f6fd8b7877c3dfb3c3779803 100644 (file)
@@ -355,6 +355,53 @@ static void rtadv_send_packet(int sock, struct interface *ifp)
                len += sizeof(struct nd_opt_mtu);
        }
 
+       /* Recursive DNS servers */
+       struct rtadv_rdnss *rdnss;
+
+       for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvRDNSSList, node, rdnss)) {
+               struct nd_opt_rdnss *opt = (struct nd_opt_rdnss *)(buf + len);
+
+               opt->nd_opt_rdnss_type = ND_OPT_RDNSS;
+               opt->nd_opt_rdnss_len = 3;
+               opt->nd_opt_rdnss_reserved = 0;
+               opt->nd_opt_rdnss_lifetime = htonl(
+                       rdnss->lifetime_set
+                               ? rdnss->lifetime
+                               : MAX(1, 0.003 * zif->rtadv.MaxRtrAdvInterval));
+
+               len += sizeof(struct nd_opt_rdnss);
+               IPV6_ADDR_COPY(buf + len, &rdnss->addr);
+               len += sizeof(struct in6_addr);
+       }
+
+       /* DNS search list */
+       struct rtadv_dnssl *dnssl;
+
+       for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvDNSSLList, node, dnssl)) {
+               size_t names_start;
+               struct nd_opt_dnssl *opt = (struct nd_opt_dnssl *)(buf + len);
+
+               opt->nd_opt_dnssl_type = ND_OPT_DNSSL;
+               opt->nd_opt_dnssl_len = 1;
+               opt->nd_opt_dnssl_reserved = 0;
+               opt->nd_opt_dnssl_lifetime = htonl(
+                       dnssl->lifetime_set
+                               ? dnssl->lifetime
+                               : MAX(1, 0.003 * zif->rtadv.MaxRtrAdvInterval));
+
+               len += sizeof(struct nd_opt_dnssl);
+
+               names_start = len;
+               memcpy(buf + len, dnssl->encoded_name, dnssl->encoded_len);
+               len += dnssl->encoded_len;
+
+               /* Zero-pad to 8-octet boundary */
+               while (len % 8)
+                       buf[len++] = '\0';
+
+               opt->nd_opt_dnssl_len += (len - names_start) / 8;
+       }
+
        msg.msg_name = (void *)&addr;
        msg.msg_namelen = sizeof(struct sockaddr_in6);
        msg.msg_iov = &iov;
@@ -1533,6 +1580,308 @@ DEFUN (no_ipv6_nd_mtu,
        return CMD_SUCCESS;
 }
 
+static struct rtadv_rdnss *rtadv_rdnss_new(void)
+{
+       return XCALLOC(MTYPE_RTADV_RDNSS, sizeof(struct rtadv_rdnss));
+}
+
+static void rtadv_rdnss_free(struct rtadv_rdnss *rdnss)
+{
+       XFREE(MTYPE_RTADV_RDNSS, rdnss);
+}
+
+static struct rtadv_rdnss *rtadv_rdnss_lookup(struct list *list,
+                                             struct rtadv_rdnss *rdnss)
+{
+       struct listnode *node;
+       struct rtadv_rdnss *p;
+
+       for (ALL_LIST_ELEMENTS_RO(list, node, p))
+               if (IPV6_ADDR_SAME(&p->addr, &rdnss->addr))
+                       return p;
+       return NULL;
+}
+
+static struct rtadv_rdnss *rtadv_rdnss_get(struct list *list,
+                                          struct rtadv_rdnss *rdnss)
+{
+       struct rtadv_rdnss *p;
+
+       p = rtadv_rdnss_lookup(list, rdnss);
+       if (p)
+               return p;
+
+       p = rtadv_rdnss_new();
+       memcpy(p, rdnss, sizeof(struct rtadv_rdnss));
+       listnode_add(list, p);
+
+       return p;
+}
+
+static void rtadv_rdnss_set(struct zebra_if *zif, struct rtadv_rdnss *rdnss)
+{
+       struct rtadv_rdnss *p;
+
+       p = rtadv_rdnss_get(zif->rtadv.AdvRDNSSList, rdnss);
+       p->lifetime = rdnss->lifetime;
+       p->lifetime_set = rdnss->lifetime_set;
+}
+
+static int rtadv_rdnss_reset(struct zebra_if *zif, struct rtadv_rdnss *rdnss)
+{
+       struct rtadv_rdnss *p;
+
+       p = rtadv_rdnss_lookup(zif->rtadv.AdvRDNSSList, rdnss);
+       if (p) {
+               listnode_delete(zif->rtadv.AdvRDNSSList, p);
+               rtadv_rdnss_free(p);
+               return 1;
+       }
+
+       return 0;
+}
+
+static struct rtadv_dnssl *rtadv_dnssl_new(void)
+{
+       return XCALLOC(MTYPE_RTADV_DNSSL, sizeof(struct rtadv_dnssl));
+}
+
+static void rtadv_dnssl_free(struct rtadv_dnssl *dnssl)
+{
+       XFREE(MTYPE_RTADV_DNSSL, dnssl);
+}
+
+static struct rtadv_dnssl *rtadv_dnssl_lookup(struct list *list,
+                                             struct rtadv_dnssl *dnssl)
+{
+       struct listnode *node;
+       struct rtadv_dnssl *p;
+
+       for (ALL_LIST_ELEMENTS_RO(list, node, p))
+               if (!strcasecmp(p->name, dnssl->name))
+                       return p;
+       return NULL;
+}
+
+static struct rtadv_dnssl *rtadv_dnssl_get(struct list *list,
+                                          struct rtadv_dnssl *dnssl)
+{
+       struct rtadv_dnssl *p;
+
+       p = rtadv_dnssl_lookup(list, dnssl);
+       if (p)
+               return p;
+
+       p = rtadv_dnssl_new();
+       memcpy(p, dnssl, sizeof(struct rtadv_dnssl));
+       listnode_add(list, p);
+
+       return p;
+}
+
+static void rtadv_dnssl_set(struct zebra_if *zif, struct rtadv_dnssl *dnssl)
+{
+       struct rtadv_dnssl *p;
+
+       p = rtadv_dnssl_get(zif->rtadv.AdvDNSSLList, dnssl);
+       memcpy(p, dnssl, sizeof(struct rtadv_dnssl));
+}
+
+static int rtadv_dnssl_reset(struct zebra_if *zif, struct rtadv_dnssl *dnssl)
+{
+       struct rtadv_dnssl *p;
+
+       p = rtadv_dnssl_lookup(zif->rtadv.AdvDNSSLList, dnssl);
+       if (p) {
+               listnode_delete(zif->rtadv.AdvDNSSLList, p);
+               rtadv_dnssl_free(p);
+               return 1;
+       }
+
+       return 0;
+}
+
+/*
+ * Convert dotted domain name (with or without trailing root zone dot) to
+ * sequence of length-prefixed labels, as described in [RFC1035 3.1]. Write up
+ * to strlen(in) + 2 octets to out.
+ *
+ * Returns the number of octets written to out or -1 if in does not constitute
+ * a valid domain name.
+ */
+static int rtadv_dnssl_encode(uint8_t *out, const char *in)
+{
+       const char *label_start, *label_end;
+       size_t outp;
+
+       outp = 0;
+       label_start = in;
+
+       while (*label_start) {
+               size_t label_len;
+
+               label_end = strchr(label_start, '.');
+               if (label_end == NULL)
+                       label_end = label_start + strlen(label_start);
+
+               label_len = label_end - label_start;
+               if (label_len >= 64)
+                       return -1; /* labels must be 63 octets or less */
+
+               out[outp++] = (uint8_t)label_len;
+               memcpy(out + outp, label_start, label_len);
+               outp += label_len;
+               label_start += label_len;
+               if (*label_start == '.')
+                       label_start++;
+       }
+
+       out[outp++] = '\0';
+       return outp;
+}
+
+DEFUN(ipv6_nd_rdnss,
+      ipv6_nd_rdnss_cmd,
+      "ipv6 nd rdnss X:X::X:X [<(0-4294967295)|infinite>]",
+      "Interface IPv6 config commands\n"
+      "Neighbor discovery\n"
+      "Recursive DNS server information\n"
+      "IPv6 address\n"
+      "Valid lifetime in seconds\n"
+      "Infinite valid lifetime\n")
+{
+       VTY_DECLVAR_CONTEXT(interface, ifp);
+       struct zebra_if *zif = ifp->info;
+       struct rtadv_rdnss rdnss = {0};
+
+       if (inet_pton(AF_INET6, argv[3]->arg, &rdnss.addr) != 1) {
+               vty_out(vty, "Malformed IPv6 address\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+       if (argc > 4) {
+               char *lifetime = argv[4]->type == RANGE_TKN ? argv[4]->arg
+                                                           : argv[4]->text;
+               rdnss.lifetime = strmatch(lifetime, "infinite")
+                                        ? UINT32_MAX
+                                        : strtoll(lifetime, NULL, 10);
+               rdnss.lifetime_set = 1;
+       }
+
+       rtadv_rdnss_set(zif, &rdnss);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_ipv6_nd_rdnss,
+      no_ipv6_nd_rdnss_cmd,
+      "no ipv6 nd rdnss X:X::X:X [<(0-4294967295)|infinite>]",
+      NO_STR
+      "Interface IPv6 config commands\n"
+      "Neighbor discovery\n"
+      "Recursive DNS server information\n"
+      "IPv6 address\n"
+      "Valid lifetime in seconds\n"
+      "Infinite valid lifetime\n")
+{
+       VTY_DECLVAR_CONTEXT(interface, ifp);
+       struct zebra_if *zif = ifp->info;
+       struct rtadv_rdnss rdnss = {0};
+
+       if (inet_pton(AF_INET6, argv[4]->arg, &rdnss.addr) != 1) {
+               vty_out(vty, "Malformed IPv6 address\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+       if (rtadv_rdnss_reset(zif, &rdnss) != 1) {
+               vty_out(vty, "Non-existant RDNSS address\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(ipv6_nd_dnssl,
+      ipv6_nd_dnssl_cmd,
+      "ipv6 nd dnssl SUFFIX [<(0-4294967295)|infinite>]",
+      "Interface IPv6 config commands\n"
+      "Neighbor discovery\n"
+      "DNS search list information\n"
+      "Domain name suffix\n"
+      "Valid lifetime in seconds\n"
+      "Infinite valid lifetime\n")
+{
+       VTY_DECLVAR_CONTEXT(interface, ifp);
+       struct zebra_if *zif = ifp->info;
+       struct rtadv_dnssl dnssl = {0};
+       size_t len;
+       int ret;
+
+       len = strlcpy(dnssl.name, argv[3]->arg, sizeof(dnssl.name));
+       if (len == 0 || len >= sizeof(dnssl.name)) {
+               vty_out(vty, "Malformed DNS search domain\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+       if (dnssl.name[len - 1] == '.') {
+               /*
+                * Allow, but don't require, a trailing dot signifying the root
+                * zone. Canonicalize by cutting it off if present.
+                */
+               dnssl.name[len - 1] = '\0';
+               len--;
+       }
+       if (argc > 4) {
+               char *lifetime = argv[4]->type == RANGE_TKN ? argv[4]->arg
+                                                           : argv[4]->text;
+               dnssl.lifetime = strmatch(lifetime, "infinite")
+                                        ? UINT32_MAX
+                                        : strtoll(lifetime, NULL, 10);
+               dnssl.lifetime_set = 1;
+       }
+
+       ret = rtadv_dnssl_encode(dnssl.encoded_name, dnssl.name);
+       if (ret < 0) {
+               vty_out(vty, "Malformed DNS search domain\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+       dnssl.encoded_len = ret;
+       rtadv_dnssl_set(zif, &dnssl);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_ipv6_nd_dnssl,
+      no_ipv6_nd_dnssl_cmd,
+      "no ipv6 nd dnssl SUFFIX [<(0-4294967295)|infinite>]",
+      NO_STR
+      "Interface IPv6 config commands\n"
+      "Neighbor discovery\n"
+      "DNS search list information\n"
+      "Domain name suffix\n"
+      "Valid lifetime in seconds\n"
+      "Infinite valid lifetime\n")
+{
+       VTY_DECLVAR_CONTEXT(interface, ifp);
+       struct zebra_if *zif = ifp->info;
+       struct rtadv_dnssl dnssl = {0};
+       size_t len;
+
+       len = strlcpy(dnssl.name, argv[4]->arg, sizeof(dnssl.name));
+       if (len == 0 || len >= sizeof(dnssl.name)) {
+               vty_out(vty, "Malformed DNS search domain\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+       if (dnssl.name[len - 1] == '.') {
+               dnssl.name[len - 1] = '\0';
+               len--;
+       }
+       if (rtadv_dnssl_reset(zif, &dnssl) != 1) {
+               vty_out(vty, "Non-existant DNS search domain\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       return CMD_SUCCESS;
+}
+
+
 /* Dump interface ND information to vty. */
 static int nd_dump_vty(struct vty *vty, struct interface *ifp)
 {
@@ -1607,6 +1956,8 @@ static int rtadv_config_write(struct vty *vty, struct interface *ifp)
        struct zebra_if *zif;
        struct listnode *node;
        struct rtadv_prefix *rprefix;
+       struct rtadv_rdnss *rdnss;
+       struct rtadv_dnssl *dnssl;
        char buf[PREFIX_STRLEN];
        int interval;
 
@@ -1688,6 +2039,29 @@ static int rtadv_config_write(struct vty *vty, struct interface *ifp)
                        vty_out(vty, " router-address");
                vty_out(vty, "\n");
        }
+       for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvRDNSSList, node, rdnss)) {
+               char buf[INET6_ADDRSTRLEN];
+
+               vty_out(vty, " ipv6 nd rdnss %s",
+                       inet_ntop(AF_INET6, &rdnss->addr, buf, sizeof(buf)));
+               if (rdnss->lifetime_set) {
+                       if (rdnss->lifetime == UINT32_MAX)
+                               vty_out(vty, " infinite");
+                       else
+                               vty_out(vty, " %u", rdnss->lifetime);
+               }
+               vty_out(vty, "\n");
+       }
+       for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvDNSSLList, node, dnssl)) {
+               vty_out(vty, " ipv6 nd dnssl %s", dnssl->name);
+               if (dnssl->lifetime_set) {
+                       if (dnssl->lifetime == UINT32_MAX)
+                               vty_out(vty, " infinite");
+                       else
+                               vty_out(vty, " %u", dnssl->lifetime);
+               }
+               vty_out(vty, "\n");
+       }
        return 0;
 }
 
@@ -1782,6 +2156,10 @@ void rtadv_cmd_init(void)
        install_element(INTERFACE_NODE, &no_ipv6_nd_router_preference_cmd);
        install_element(INTERFACE_NODE, &ipv6_nd_mtu_cmd);
        install_element(INTERFACE_NODE, &no_ipv6_nd_mtu_cmd);
+       install_element(INTERFACE_NODE, &ipv6_nd_rdnss_cmd);
+       install_element(INTERFACE_NODE, &no_ipv6_nd_rdnss_cmd);
+       install_element(INTERFACE_NODE, &ipv6_nd_dnssl_cmd);
+       install_element(INTERFACE_NODE, &no_ipv6_nd_dnssl_cmd);
 }
 
 static int if_join_all_router(int sock, struct interface *ifp)
index 03db13fd69bb1ccd1f77752e0289a841816365a7..f7c27ebcb3a0c9c5de776e3ba49823ea1e396db0 100644 (file)
@@ -91,6 +91,37 @@ struct nd_opt_homeagent_info { /* Home Agent info */
 } __attribute__((__packed__));
 #endif
 
+#ifndef ND_OPT_RDNSS
+#define ND_OPT_RDNSS 25
+#endif
+#ifndef ND_OPT_DNSSL
+#define ND_OPT_DNSSL 31
+#endif
+
+#ifndef HAVE_STRUCT_ND_OPT_RDNSS
+struct nd_opt_rdnss { /* Recursive DNS server option [RFC8106 5.1] */
+       uint8_t nd_opt_rdnss_type;
+       uint8_t nd_opt_rdnss_len;
+       uint16_t nd_opt_rdnss_reserved;
+       uint32_t nd_opt_rdnss_lifetime;
+       /* Followed by one or more IPv6 addresses */
+} __attribute__((__packed__));
+#endif
+
+#ifndef HAVE_STRUCT_ND_OPT_DNSSL
+struct nd_opt_dnssl { /* DNS search list option [RFC8106 5.2] */
+       uint8_t nd_opt_dnssl_type;
+       uint8_t nd_opt_dnssl_len;
+       uint16_t nd_opt_dnssl_reserved;
+       uint32_t nd_opt_dnssl_lifetime;
+       /*
+        * Followed by one or more domain names encoded as in [RFC1035 3.1].
+        * Multiple domain names are concatenated after encoding. In any case,
+        * the result is zero-padded to a multiple of 8 octets.
+        */
+} __attribute__((__packed__));
+#endif
+
 extern const char *rtadv_pref_strs[];
 
 #endif /* HAVE_RTADV */
index ee041b1c3dbd95009cb1a79b9a80083aaae9875c..4eaabd237d9c711230a432151bab5eacee12d224 100644 (file)
@@ -27,6 +27,8 @@
 
 DEFINE_MGROUP(ZEBRA, "zebra")
 DEFINE_MTYPE(ZEBRA, RTADV_PREFIX, "Router Advertisement Prefix")
+DEFINE_MTYPE(ZEBRA, RTADV_RDNSS, "Router Advertisement RDNSS")
+DEFINE_MTYPE(ZEBRA, RTADV_DNSSL, "Router Advertisement DNSSL")
 DEFINE_MTYPE(ZEBRA, ZEBRA_VRF, "ZEBRA VRF")
 DEFINE_MTYPE(ZEBRA, RE, "Route Entry")
 DEFINE_MTYPE(ZEBRA, RIB_QUEUE, "RIB process work queue")
index de55478de2fc841d59b810063e0a83d8f31de1d4..1f3015186f5bb9c3b69fcc5603a4d666da0dc5d0 100644 (file)
@@ -26,6 +26,8 @@
 
 DECLARE_MGROUP(ZEBRA)
 DECLARE_MTYPE(RTADV_PREFIX)
+DECLARE_MTYPE(RTADV_RDNSS)
+DECLARE_MTYPE(RTADV_DNSSL)
 DECLARE_MTYPE(ZEBRA_NS)
 DECLARE_MTYPE(ZEBRA_VRF)
 DECLARE_MTYPE(RE)