]> git.puffer.fish Git - matthieu/frr.git/commitdiff
zebra: avoid exceeding link MTU through RA options
authorLars Seipel <ls@slrz.net>
Mon, 28 Jan 2019 12:42:01 +0000 (13:42 +0100)
committerLars Seipel <ls@slrz.net>
Sat, 2 Feb 2019 18:10:19 +0000 (19:10 +0100)
Signed-off-by: Lars Seipel <ls@slrz.net>
zebra/rtadv.c

index c5d28ddfb3719040f6fd8b7877c3dfb3c3779803..33225c97c6adf2f9e83783e3316bc8a0fb46b66d 100644 (file)
@@ -355,14 +355,31 @@ static void rtadv_send_packet(int sock, struct interface *ifp)
                len += sizeof(struct nd_opt_mtu);
        }
 
+       /*
+        * There is no limit on the number of configurable recursive DNS
+        * servers or search list entries. We don't want the RA message
+        * to exceed the link's MTU (risking fragmentation) or even
+        * blow the stack buffer allocated for it.
+        */
+       size_t max_len = MIN(ifp->mtu6 - 40, sizeof(buf));
+
        /* Recursive DNS servers */
        struct rtadv_rdnss *rdnss;
 
        for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvRDNSSList, node, rdnss)) {
+               size_t opt_len =
+                       sizeof(struct nd_opt_rdnss) + sizeof(struct in6_addr);
+
+               if (len + opt_len > max_len) {
+                       zlog_warn(
+                               "%s(%u): Tx RA: RDNSS option would exceed MTU, omitting it",
+                               ifp->name, ifp->ifindex);
+                       goto no_more_opts;
+               }
                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_len = opt_len / 8;
                opt->nd_opt_rdnss_reserved = 0;
                opt->nd_opt_rdnss_lifetime = htonl(
                        rdnss->lifetime_set
@@ -370,6 +387,7 @@ static void rtadv_send_packet(int sock, struct interface *ifp)
                                : 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);
        }
@@ -378,11 +396,19 @@ static void rtadv_send_packet(int sock, struct interface *ifp)
        struct rtadv_dnssl *dnssl;
 
        for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvDNSSLList, node, dnssl)) {
-               size_t names_start;
+               size_t opt_len = sizeof(struct nd_opt_dnssl)
+                                + ((dnssl->encoded_len + 7) & ~7);
+
+               if (len + opt_len > max_len) {
+                       zlog_warn(
+                               "%s(%u): Tx RA: DNSSL option would exceed MTU, omitting it",
+                               ifp->name, ifp->ifindex);
+                       goto no_more_opts;
+               }
                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_len = opt_len / 8;
                opt->nd_opt_dnssl_reserved = 0;
                opt->nd_opt_dnssl_lifetime = htonl(
                        dnssl->lifetime_set
@@ -391,17 +417,16 @@ static void rtadv_send_packet(int sock, struct interface *ifp)
 
                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;
        }
 
+no_more_opts:
+
        msg.msg_name = (void *)&addr;
        msg.msg_namelen = sizeof(struct sockaddr_in6);
        msg.msg_iov = &iov;