]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network/ndisc: remember the latest RA from the default router
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 23 Feb 2024 02:42:33 +0000 (11:42 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 4 Apr 2024 20:55:06 +0000 (05:55 +0900)
The remembered RA will be used later. Preparation for later commits.

src/network/networkd-link.h
src/network/networkd-ndisc.c

index b3e8bb001fede8e05b50aa35cb8673f1796435b0..01931f6773e923ede2406901d22d953fbdf86cbe 100644 (file)
@@ -160,6 +160,7 @@ typedef struct Link {
         sd_dhcp_server *dhcp_server;
 
         sd_ndisc *ndisc;
+        sd_ndisc_router *ndisc_default_router;
         sd_event_source *ndisc_expire;
         Set *ndisc_rdnss;
         Set *ndisc_dnssl;
index 71e3c30727e88a834732f11fba53d4f8116bdece..6be0c0f9d9b1309ad6ab9ea7d6a2593bfa5826c0 100644 (file)
@@ -505,6 +505,126 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         return 0;
 }
 
+static int update_default_router_address(Link *link, const struct in6_addr *original_address, const struct in6_addr *current_address) {
+        struct in6_addr a;
+        int r;
+
+        assert(link);
+        assert(original_address);
+        assert(current_address);
+
+        if (!link->ndisc_default_router)
+                return 0;
+
+        r = sd_ndisc_router_get_sender_address(link->ndisc_default_router, &a);
+        if (r < 0)
+                return r;
+
+        if (!in6_addr_equal(&a, original_address))
+                return 0;
+
+        return sd_ndisc_router_set_sender_address(link->ndisc_default_router, current_address);
+}
+
+static int drop_default_router(Link *link, const struct in6_addr *router, usec_t timestamp_usec) {
+        usec_t lifetime_usec;
+        int r;
+
+        assert(link);
+
+        if (!link->ndisc_default_router)
+                return 0;
+
+        if (router) {
+                struct in6_addr a;
+
+                r = sd_ndisc_router_get_sender_address(link->ndisc_default_router, &a);
+                if (r < 0)
+                        return r;
+
+                if (!in6_addr_equal(&a, router))
+                        return 0;
+        }
+
+        r = sd_ndisc_router_get_lifetime_timestamp(link->ndisc_default_router, CLOCK_BOOTTIME, &lifetime_usec);
+        if (r < 0)
+                return r;
+
+        if (lifetime_usec > timestamp_usec)
+                return 0;
+
+        link->ndisc_default_router = sd_ndisc_router_unref(link->ndisc_default_router);
+        return 0;
+}
+
+static int accept_default_router(sd_ndisc_router *new_router, sd_ndisc_router *existing_router) {
+        usec_t lifetime_usec;
+        struct in6_addr a, b;
+        uint8_t p, q;
+        int r;
+
+        assert(new_router);
+
+        r = sd_ndisc_router_get_lifetime(new_router, &lifetime_usec);
+        if (r < 0)
+                return r;
+
+        if (lifetime_usec == 0)
+                return false; /* Received a new RA about revoking the router, ignoring. */
+
+        if (!existing_router)
+                return true;
+
+        /* lifetime of the existing router is already checked in ndisc_drop_outdated(). */
+
+        r = sd_ndisc_router_get_sender_address(new_router, &a);
+        if (r < 0)
+                return r;
+
+        r = sd_ndisc_router_get_sender_address(existing_router, &b);
+        if (r < 0)
+                return r;
+
+        if (in6_addr_equal(&a, &b))
+                return true; /* Received a new RA from the remembered router. Replace the remembered RA. */
+
+        r = sd_ndisc_router_get_preference(new_router, &p);
+        if (r < 0)
+                return r;
+
+        r = sd_ndisc_router_get_preference(existing_router, &q);
+        if (r < 0)
+                return r;
+
+        if (p == q)
+                return true;
+
+        if (p == SD_NDISC_PREFERENCE_HIGH)
+                return true;
+
+        if (p == SD_NDISC_PREFERENCE_MEDIUM && q == SD_NDISC_PREFERENCE_LOW)
+                return true;
+
+        return false;
+}
+
+static int ndisc_remember_default_router(Link *link, sd_ndisc_router *rt) {
+        int r;
+
+        assert(link);
+        assert(rt);
+
+        r = accept_default_router(rt, link->ndisc_default_router);
+        if (r <= 0)
+                return r;
+
+        sd_ndisc_router_ref(rt);
+        sd_ndisc_router_unref(link->ndisc_default_router);
+        link->ndisc_default_router = rt;
+
+        return 1; /* The received router advertisement is from the default router. */
+}
+
 static int ndisc_router_process_reachable_time(Link *link, sd_ndisc_router *rt) {
         usec_t reachable_time, msec;
         int r;
@@ -1410,6 +1530,10 @@ static int ndisc_drop_outdated(Link *link, const struct in6_addr *router, usec_t
          * valid lifetimes to improve the reaction of SLAAC to renumbering events.
          * See draft-ietf-6man-slaac-renum-02, section 4.2. */
 
+        r = drop_default_router(link, router, timestamp_usec);
+        if (r < 0)
+                RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to drop outdated default router, ignoring: %m"));
+
         SET_FOREACH(route, link->manager->routes) {
                 if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
                         continue;
@@ -1657,6 +1781,10 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return r;
 
+        r = ndisc_remember_default_router(link, rt);
+        if (r < 0)
+                return r;
+
         r = ndisc_start_dhcp6_client(link, rt);
         if (r < 0)
                 return r;
@@ -1743,6 +1871,10 @@ static int ndisc_neighbor_handle_router_message(Link *link, sd_ndisc_neighbor *n
         if (in6_addr_equal(&current_address, &original_address))
                 return 0; /* the router address is not changed */
 
+        r = update_default_router_address(link, &original_address, &current_address);
+        if (r < 0)
+                return r;
+
         Route *route;
         SET_FOREACH(route, link->manager->routes) {
                 if (route->source != NETWORK_CONFIG_SOURCE_NDISC)