]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network/ndisc: drop configurations when received RA with zero lifetime
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 19 Feb 2024 01:35:48 +0000 (10:35 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 20 Feb 2024 06:31:39 +0000 (15:31 +0900)
src/network/networkd-ndisc.c

index f9fb7e207b151bd4896d5422442f9c793ffb54c1..bf67cb63bfd25c8262bc46a44d20c0787e253461 100644 (file)
@@ -273,6 +273,39 @@ static int ndisc_request_route(Route *route, Link *link, sd_ndisc_router *rt) {
         return 0;
 }
 
+static int ndisc_remove_route(Route *route, Link *link) {
+        int r;
+
+        assert(route);
+        assert(link);
+        assert(link->manager);
+
+        ndisc_set_route_priority(link, route);
+
+        if (!route->table_set)
+                route->table = link_get_ipv6_accept_ra_route_table(link);
+
+        r = route_adjust_nexthops(route, link);
+        if (r < 0)
+                return r;
+
+        if (route->pref_set) {
+                ndisc_set_route_priority(link, route);
+                return route_remove_and_cancel(route, link->manager);
+        }
+
+        uint8_t pref;
+        FOREACH_ARGUMENT(pref, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH) {
+                route->pref = pref;
+                ndisc_set_route_priority(link, route);
+                r = route_remove_and_cancel(route, link->manager);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) {
         int r;
 
@@ -340,6 +373,55 @@ static int ndisc_request_address(Address *address, Link *link, sd_ndisc_router *
         return 0;
 }
 
+static int ndisc_router_drop_default(Link *link, sd_ndisc_router *rt) {
+        _cleanup_(route_unrefp) Route *route = NULL;
+        struct in6_addr gateway;
+        int r;
+
+        assert(link);
+        assert(link->network);
+        assert(rt);
+
+        r = sd_ndisc_router_get_address(rt, &gateway);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
+
+        r = route_new(&route);
+        if (r < 0)
+                return log_oom();
+
+        route->family = AF_INET6;
+        route->nexthop.family = AF_INET6;
+        route->nexthop.gw.in6 = gateway;
+
+        r = ndisc_remove_route(route, link);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to remove the default gateway configured by RA: %m");
+
+        Route *route_gw;
+        HASHMAP_FOREACH(route_gw, link->network->routes_by_section) {
+                _cleanup_(route_unrefp) Route *tmp = NULL;
+
+                if (!route_gw->gateway_from_dhcp_or_ra)
+                        continue;
+
+                if (route_gw->nexthop.family != AF_INET6)
+                        continue;
+
+                r = route_dup(route_gw, NULL, &tmp);
+                if (r < 0)
+                        return r;
+
+                tmp->nexthop.gw.in6 = gateway;
+
+                r = ndisc_remove_route(tmp, link);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Could not remove semi-static gateway: %m");
+        }
+
+        return 0;
+}
+
 static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         usec_t lifetime_usec;
         struct in6_addr gateway;
@@ -350,6 +432,13 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         assert(link->network);
         assert(rt);
 
+        /* If the router lifetime is zero, the router should not be used as the default gateway. */
+        r = sd_ndisc_router_get_lifetime(rt, NULL);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return ndisc_router_drop_default(link, rt);
+
         if (!link->network->ipv6_accept_ra_use_gateway &&
             hashmap_isempty(link->network->routes_by_section))
                 return 0;
@@ -578,9 +667,22 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
                 address->lifetime_valid_usec = lifetime_valid_usec;
                 address->lifetime_preferred_usec = lifetime_preferred_usec;
 
-                r = ndisc_request_address(address, link, rt);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "Could not request SLAAC address: %m");
+                /* draft-ietf-6man-slaac-renum-07 section 4.2
+                 * https://datatracker.ietf.org/doc/html/draft-ietf-6man-slaac-renum-07#section-4.2
+                 *
+                 * If the advertised prefix is equal to the prefix of an address configured by stateless
+                 * autoconfiguration in the list, the valid lifetime and the preferred lifetime of the
+                 * address should be updated by processing the Valid Lifetime and the Preferred Lifetime
+                 * (respectively) in the received advertisement. */
+                if (lifetime_valid_usec == 0) {
+                        r = address_remove_and_cancel(address, link);
+                        if (r < 0)
+                                return log_link_warning_errno(link, r, "Could not remove SLAAC address: %m");
+                } else {
+                        r = ndisc_request_address(address, link, rt);
+                        if (r < 0)
+                                return log_link_warning_errno(link, r, "Could not request SLAAC address: %m");
+                }
         }
 
         return 0;
@@ -636,7 +738,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
 
 static int ndisc_router_drop_onlink_prefix(Link *link, sd_ndisc_router *rt) {
         _cleanup_(route_unrefp) Route *route = NULL;
-        unsigned prefixlen, preference;
+        unsigned prefixlen;
         struct in6_addr prefix;
         usec_t lifetime_usec;
         int r;
@@ -669,11 +771,6 @@ static int ndisc_router_drop_onlink_prefix(Link *link, sd_ndisc_router *rt) {
         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();
@@ -681,16 +778,8 @@ static int ndisc_router_drop_onlink_prefix(Link *link, sd_ndisc_router *rt) {
         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);
+        r = ndisc_remove_route(route, link);
         if (r < 0)
                 return log_link_warning_errno(link, r, "Could not remove prefix route: %m");
 
@@ -715,7 +804,7 @@ static int ndisc_router_process_prefix(Link *link, sd_ndisc_router *rt) {
          * A router SHOULD NOT send a prefix option for the link-local prefix and a host SHOULD ignore such
          * a prefix option. */
         if (in6_addr_is_link_local(&a)) {
-                log_link_debug(link, "Received link-local prefix, ignoring autonomous prefix.");
+                log_link_debug(link, "Received link-local prefix, ignoring prefix.");
                 return 0;
         }