]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network/ndisc: drop onlink prefix route when on-link flag is zero 31177/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 2 Feb 2024 07:08:23 +0000 (16:08 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 8 Feb 2024 02:30:19 +0000 (11:30 +0900)
Fixes #28435.

src/network/networkd-ndisc.c

index dfcd400a52496ddb22dc1172a8e949a3a5cc93d8..808a9f2baef56eb9592f9cbad45e93927bbb3e66 100644 (file)
@@ -545,6 +545,69 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
         return 0;
 }
 
+static int ndisc_router_drop_onlink_prefix(Link *link, sd_ndisc_router *rt) {
+        _cleanup_(route_unrefp) Route *route = NULL;
+        unsigned prefixlen, preference;
+        struct in6_addr prefix;
+        usec_t lifetime_usec;
+        int r;
+
+        assert(link);
+        assert(link->network);
+        assert(rt);
+
+        /* RFC 4861 section 6.3.4.
+         * Note, however, that a Prefix Information option with the on-link flag set to zero conveys no
+         * information concerning on-link determination and MUST NOT be interpreted to mean that addresses
+         * covered by the prefix are off-link. The only way to cancel a previous on-link indication is to
+         * advertise that prefix with the L-bit set and the Lifetime set to zero. */
+
+        if (!link->network->ipv6_accept_ra_use_onlink_prefix)
+                return 0;
+
+        r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_usec);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to get prefix lifetime: %m");
+
+        if (lifetime_usec != 0)
+                return 0;
+
+        r = sd_ndisc_router_prefix_get_address(rt, &prefix);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to get prefix address: %m");
+
+        r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to get prefix length: %m");
+
+        /* Prefix Information option does not have preference, hence we use the 'main' preference here */
+        r = sd_ndisc_router_get_preference(rt, &preference);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to get router preference from RA: %m");
+
+        r = route_new(&route);
+        if (r < 0)
+                return log_oom();
+
+        route->family = AF_INET6;
+        route->dst.in6 = prefix;
+        route->dst_prefixlen = prefixlen;
+        route->table = link_get_ipv6_accept_ra_route_table(link);
+        route->pref = preference;
+        ndisc_set_route_priority(link, route);
+        route->protocol = RTPROT_RA;
+
+        r = route_adjust_nexthops(route, link);
+        if (r < 0)
+                return r;
+
+        r = route_remove_and_cancel(route, link->manager);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Could not remove prefix route: %m");
+
+        return 0;
+}
+
 static int ndisc_router_process_prefix(Link *link, sd_ndisc_router *rt) {
         unsigned prefixlen;
         struct in6_addr a;
@@ -584,11 +647,12 @@ static int ndisc_router_process_prefix(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m");
 
-        if (FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK)) {
+        if (FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK))
                 r = ndisc_router_process_onlink_prefix(link, rt);
-                if (r < 0)
-                        return r;
-        }
+        else
+                r = ndisc_router_drop_onlink_prefix(link, rt);
+        if (r < 0)
+                return r;
 
         if (FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO)) {
                 r = ndisc_router_process_autonomous_prefix(link, rt);