Skip to content

Commit 01420b2

Browse files
yuwatabluca
authored andcommitted
network/ndisc: use router lifetime as one for redirect route
Previously, we did not set lifetime for redirect route, and redirect routes were removed only when received a RA from the target address. Thus, routes that redirect on-link addresses were never removed. RFCs mention nothing about the lifetime of redirection. But the previous implementation does not pass the IPv6 Core Conformance Tests. This makes - remember all received RAs and manage them by the sender address (previously, remembered only one with the highest preference), - then use the router lifetime as one for redirect route, - remove redirect route also when the router corresponds to the sender address is dropped (previously, considered only target address). Note, even if we recieve a new RA, we do not update existing redirect routes. The lifetime of the redirect route is updated only when a new Redirect message is received. Closes #32527.
1 parent 02f35b1 commit 01420b2

File tree

3 files changed

+126
-99
lines changed

3 files changed

+126
-99
lines changed

src/network/networkd-link.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@ typedef struct Link {
160160
sd_dhcp_server *dhcp_server;
161161

162162
sd_ndisc *ndisc;
163-
sd_ndisc_router *ndisc_default_router;
164163
sd_event_source *ndisc_expire;
164+
Hashmap *ndisc_routers_by_sender;
165165
Set *ndisc_rdnss;
166166
Set *ndisc_dnssl;
167167
Set *ndisc_captive_portals;

src/network/networkd-ndisc.c

Lines changed: 121 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "event-util.h"
1414
#include "missing_network.h"
15+
#include "ndisc-router-internal.h"
1516
#include "networkd-address-generation.h"
1617
#include "networkd-address.h"
1718
#include "networkd-dhcp6.h"
@@ -35,6 +36,8 @@
3536
* Not sure if the threshold is high enough. Let's adjust later if not. */
3637
#define NDISC_PREF64_MAX 64U
3738

39+
static int ndisc_drop_outdated(Link *link, const struct in6_addr *router, usec_t timestamp_usec);
40+
3841
bool link_ndisc_enabled(Link *link) {
3942
assert(link);
4043

@@ -516,8 +519,6 @@ static int ndisc_redirect_drop_conflict(Link *link, sd_ndisc_redirect *rd) {
516519
}
517520

518521
static int ndisc_redirect_verify_sender(Link *link, sd_ndisc_redirect *rd) {
519-
sd_ndisc_redirect *existing;
520-
struct in6_addr router, sender;
521522
int r;
522523

523524
assert(link);
@@ -527,11 +528,18 @@ static int ndisc_redirect_verify_sender(Link *link, sd_ndisc_redirect *rd) {
527528
* The IP source address of the Redirect is the same as the current first-hop router for the specified
528529
* ICMP Destination Address. */
529530

531+
struct in6_addr sender;
530532
r = sd_ndisc_redirect_get_sender_address(rd, &sender);
531533
if (r < 0)
532534
return r;
533535

534-
existing = set_get(link->ndisc_redirects, rd);
536+
/* We will reuse the sender's router lifetime as the lifetime of the redirect route. Hence, if we
537+
* have not remembered an RA from the sender, refuse the Redirect message. */
538+
sd_ndisc_router *router = hashmap_get(link->ndisc_routers_by_sender, &sender);
539+
if (!router)
540+
return false;
541+
542+
sd_ndisc_redirect *existing = set_get(link->ndisc_redirects, rd);
535543
if (existing) {
536544
struct in6_addr target, dest;
537545

@@ -555,15 +563,30 @@ static int ndisc_redirect_verify_sender(Link *link, sd_ndisc_redirect *rd) {
555563
return false;
556564
}
557565

558-
if (!link->ndisc_default_router)
559-
return false;
560-
561-
r = sd_ndisc_router_get_sender_address(link->ndisc_default_router, &router);
566+
/* Check if the sender is one of the known router with highest priority. */
567+
uint8_t preference;
568+
r = sd_ndisc_router_get_preference(router, &preference);
562569
if (r < 0)
563570
return r;
564571

565-
/* The sender must be the default router. */
566-
return in6_addr_equal(&sender, &router);
572+
if (preference == SD_NDISC_PREFERENCE_HIGH)
573+
return true;
574+
575+
sd_ndisc_router *rt;
576+
HASHMAP_FOREACH(rt, link->ndisc_routers_by_sender) {
577+
if (rt == router)
578+
continue;
579+
580+
uint8_t pref;
581+
if (sd_ndisc_router_get_preference(rt, &pref) < 0)
582+
continue;
583+
584+
if (pref == SD_NDISC_PREFERENCE_HIGH ||
585+
(pref == SD_NDISC_PREFERENCE_MEDIUM && preference == SD_NDISC_PREFERENCE_LOW))
586+
return false;
587+
}
588+
589+
return true;
567590
}
568591

569592
static int ndisc_redirect_handler(Link *link, sd_ndisc_redirect *rd) {
@@ -576,6 +599,15 @@ static int ndisc_redirect_handler(Link *link, sd_ndisc_redirect *rd) {
576599
if (!link->network->ndisc_use_redirect)
577600
return 0;
578601

602+
usec_t now_usec;
603+
r = sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec);
604+
if (r < 0)
605+
return r;
606+
607+
r = ndisc_drop_outdated(link, /* router = */ NULL, now_usec);
608+
if (r < 0)
609+
return r;
610+
579611
r = ndisc_redirect_verify_sender(link, rd);
580612
if (r <= 0)
581613
return r;
@@ -598,6 +630,14 @@ static int ndisc_redirect_handler(Link *link, sd_ndisc_redirect *rd) {
598630
if (r < 0)
599631
return r;
600632

633+
sd_ndisc_router *rt = hashmap_get(link->ndisc_routers_by_sender, &route->provider.in6);
634+
if (!rt)
635+
return -EADDRNOTAVAIL;
636+
637+
r = sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &route->lifetime_usec);
638+
if (r < 0)
639+
return r;
640+
601641
return ndisc_request_route(route, link);
602642
}
603643

@@ -609,13 +649,10 @@ static int ndisc_drop_redirect(Link *link, const struct in6_addr *router) {
609649
sd_ndisc_redirect *rd;
610650
SET_FOREACH(rd, link->ndisc_redirects) {
611651
if (router) {
612-
struct in6_addr target;
613-
614-
r = sd_ndisc_redirect_get_target_address(rd, &target);
615-
if (r < 0)
616-
return r;
652+
struct in6_addr a;
617653

618-
if (!in6_addr_equal(&target, router))
654+
if (!(sd_ndisc_redirect_get_sender_address(rd, &a) >= 0 && in6_addr_equal(&a, router)) &&
655+
!(sd_ndisc_redirect_get_target_address(rd, &a) >= 0 && in6_addr_equal(&a, router)))
619656
continue;
620657
}
621658

@@ -782,124 +819,105 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
782819
return 0;
783820
}
784821

785-
static int update_default_router_address(Link *link, const struct in6_addr *original_address, const struct in6_addr *current_address) {
786-
struct in6_addr a;
822+
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
823+
ndisc_router_hash_ops,
824+
struct in6_addr,
825+
in6_addr_hash_func,
826+
in6_addr_compare_func,
827+
sd_ndisc_router,
828+
sd_ndisc_router_unref);
829+
830+
static int ndisc_update_router_address(Link *link, const struct in6_addr *original_address, const struct in6_addr *current_address) {
831+
_cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
787832
int r;
788833

789834
assert(link);
790835
assert(original_address);
791836
assert(current_address);
792837

793-
if (!link->ndisc_default_router)
838+
rt = hashmap_remove(link->ndisc_routers_by_sender, original_address);
839+
if (!rt)
794840
return 0;
795841

796-
r = sd_ndisc_router_get_sender_address(link->ndisc_default_router, &a);
797-
if (r < 0)
798-
return r;
799-
800-
if (!in6_addr_equal(&a, original_address))
842+
/* If we already received an RA from the new address, then forget the RA from the old address. */
843+
if (hashmap_contains(link->ndisc_routers_by_sender, current_address))
801844
return 0;
802845

803-
return sd_ndisc_router_set_sender_address(link->ndisc_default_router, current_address);
804-
}
805-
806-
static int drop_default_router(Link *link, const struct in6_addr *router, usec_t timestamp_usec) {
807-
usec_t lifetime_usec;
808-
int r;
809-
810-
assert(link);
811-
812-
if (!link->ndisc_default_router)
813-
return 0;
814-
815-
if (router) {
816-
struct in6_addr a;
817-
818-
r = sd_ndisc_router_get_sender_address(link->ndisc_default_router, &a);
819-
if (r < 0)
820-
return r;
821-
822-
if (!in6_addr_equal(&a, router))
823-
return 0;
824-
}
825-
826-
r = sd_ndisc_router_get_lifetime_timestamp(link->ndisc_default_router, CLOCK_BOOTTIME, &lifetime_usec);
846+
/* Otherwise, update the sender address of the previously received RA. */
847+
r = sd_ndisc_router_set_sender_address(rt, current_address);
827848
if (r < 0)
828849
return r;
829850

830-
if (lifetime_usec > timestamp_usec)
831-
return 0;
851+
r = hashmap_put(link->ndisc_routers_by_sender, &rt->packet->sender_address, rt);
852+
if (r < 0)
853+
return r;
832854

833-
link->ndisc_default_router = sd_ndisc_router_unref(link->ndisc_default_router);
855+
TAKE_PTR(rt);
834856
return 0;
835857
}
836858

837-
static int accept_default_router(sd_ndisc_router *new_router, sd_ndisc_router *existing_router) {
859+
static int ndisc_drop_router_one(Link *link, sd_ndisc_router *rt, usec_t timestamp_usec) {
838860
usec_t lifetime_usec;
839-
struct in6_addr a, b;
840-
uint8_t p, q;
841861
int r;
842862

843-
assert(new_router);
863+
assert(link);
864+
assert(rt);
865+
assert(rt->packet);
844866

845-
r = sd_ndisc_router_get_lifetime(new_router, &lifetime_usec);
867+
r = sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
846868
if (r < 0)
847869
return r;
848870

849-
if (lifetime_usec == 0)
850-
return false; /* Received a new RA about revoking the router, ignoring. */
851-
852-
if (!existing_router)
853-
return true;
871+
if (lifetime_usec > timestamp_usec)
872+
return 0;
854873

855-
/* lifetime of the existing router is already checked in ndisc_drop_outdated(). */
874+
r = ndisc_drop_redirect(link, &rt->packet->sender_address);
856875

857-
r = sd_ndisc_router_get_sender_address(new_router, &a);
858-
if (r < 0)
859-
return r;
876+
sd_ndisc_router_unref(hashmap_remove(link->ndisc_routers_by_sender, &rt->packet->sender_address));
860877

861-
r = sd_ndisc_router_get_sender_address(existing_router, &b);
862-
if (r < 0)
863-
return r;
864-
865-
if (in6_addr_equal(&a, &b))
866-
return true; /* Received a new RA from the remembered router. Replace the remembered RA. */
878+
return r;
879+
}
867880

868-
r = sd_ndisc_router_get_preference(new_router, &p);
869-
if (r < 0)
870-
return r;
881+
static int ndisc_drop_routers(Link *link, const struct in6_addr *router, usec_t timestamp_usec) {
882+
sd_ndisc_router *rt;
883+
int ret = 0;
871884

872-
r = sd_ndisc_router_get_preference(existing_router, &q);
873-
if (r < 0)
874-
return r;
885+
assert(link);
875886

876-
if (p == q)
877-
return true;
887+
if (router) {
888+
rt = hashmap_get(link->ndisc_routers_by_sender, router);
889+
if (!rt)
890+
return 0;
878891

879-
if (p == SD_NDISC_PREFERENCE_HIGH)
880-
return true;
892+
return ndisc_drop_router_one(link, rt, timestamp_usec);
893+
}
881894

882-
if (p == SD_NDISC_PREFERENCE_MEDIUM && q == SD_NDISC_PREFERENCE_LOW)
883-
return true;
895+
HASHMAP_FOREACH_KEY(rt, router, link->ndisc_routers_by_sender)
896+
RET_GATHER(ret, ndisc_drop_router_one(link, rt, timestamp_usec));
884897

885-
return false;
898+
return ret;
886899
}
887900

888-
static int ndisc_remember_default_router(Link *link, sd_ndisc_router *rt) {
901+
static int ndisc_remember_router(Link *link, sd_ndisc_router *rt) {
889902
int r;
890903

891904
assert(link);
892905
assert(rt);
906+
assert(rt->packet);
907+
908+
sd_ndisc_router_unref(hashmap_remove(link->ndisc_routers_by_sender, &rt->packet->sender_address));
893909

894-
r = accept_default_router(rt, link->ndisc_default_router);
910+
/* Remember RAs with non-zero lifetime. */
911+
r = sd_ndisc_router_get_lifetime(rt, NULL);
895912
if (r <= 0)
896913
return r;
897914

898-
sd_ndisc_router_ref(rt);
899-
sd_ndisc_router_unref(link->ndisc_default_router);
900-
link->ndisc_default_router = rt;
915+
r = hashmap_ensure_put(&link->ndisc_routers_by_sender, &ndisc_router_hash_ops, &rt->packet->sender_address, rt);
916+
if (r < 0)
917+
return r;
901918

902-
return 1; /* The received router advertisement is from the default router. */
919+
sd_ndisc_router_ref(rt);
920+
return 0;
903921
}
904922

905923
static int ndisc_router_process_reachable_time(Link *link, sd_ndisc_router *rt) {
@@ -1842,7 +1860,7 @@ static int ndisc_drop_outdated(Link *link, const struct in6_addr *router, usec_t
18421860
* valid lifetimes to improve the reaction of SLAAC to renumbering events.
18431861
* See draft-ietf-6man-slaac-renum-02, section 4.2. */
18441862

1845-
r = drop_default_router(link, router, timestamp_usec);
1863+
r = ndisc_drop_routers(link, router, timestamp_usec);
18461864
if (r < 0)
18471865
RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to drop outdated default router, ignoring: %m"));
18481866

@@ -1961,6 +1979,16 @@ static int ndisc_setup_expire(Link *link) {
19611979
assert(link);
19621980
assert(link->manager);
19631981

1982+
sd_ndisc_router *rt;
1983+
HASHMAP_FOREACH(rt, link->ndisc_routers_by_sender) {
1984+
usec_t t;
1985+
1986+
if (sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &t) < 0)
1987+
continue;
1988+
1989+
lifetime_usec = MIN(lifetime_usec, t);
1990+
}
1991+
19641992
SET_FOREACH(route, link->manager->routes) {
19651993
if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
19661994
continue;
@@ -2096,7 +2124,7 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
20962124
if (r < 0)
20972125
return r;
20982126

2099-
r = ndisc_remember_default_router(link, rt);
2127+
r = ndisc_remember_router(link, rt);
21002128
if (r < 0)
21012129
return r;
21022130

@@ -2195,7 +2223,7 @@ static int ndisc_neighbor_handle_router_message(Link *link, sd_ndisc_neighbor *n
21952223
if (in6_addr_equal(&current_address, &original_address))
21962224
return 0; /* the router address is not changed */
21972225

2198-
r = update_default_router_address(link, &original_address, &current_address);
2226+
r = ndisc_update_router_address(link, &original_address, &current_address);
21992227
if (r < 0)
22002228
return r;
22012229

@@ -2447,6 +2475,7 @@ void ndisc_flush(Link *link) {
24472475
(void) ndisc_drop_outdated(link, /* router = */ NULL, /* timestamp_usec = */ USEC_INFINITY);
24482476
(void) ndisc_drop_redirect(link, /* router = */ NULL);
24492477

2478+
link->ndisc_routers_by_sender = hashmap_free(link->ndisc_routers_by_sender);
24502479
link->ndisc_rdnss = set_free(link->ndisc_rdnss);
24512480
link->ndisc_dnssl = set_free(link->ndisc_dnssl);
24522481
link->ndisc_captive_portals = set_free(link->ndisc_captive_portals);

0 commit comments

Comments
 (0)