From 479d3e1994a2e4ff7070dc2a0cb1615af7120b0c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 19 Feb 2024 10:35:48 +0900 Subject: [PATCH] network/ndisc: drop configurations when received RA with zero lifetime --- src/network/networkd-ndisc.c | 127 +++++++++++++++++++++++++++++------ 1 file changed, 108 insertions(+), 19 deletions(-) diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index f9fb7e207b1..bf67cb63bfd 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -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; }