From 167c7ae511b5c320100405841fd8b93d743fa4ce Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 3 Oct 2022 12:28:41 +0900 Subject: [PATCH 1/9] network: ndisc: read prefix earlier No functional changes. --- src/network/networkd-ndisc.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 2250235d4eb..b1201eb00be 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -408,6 +408,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { _cleanup_(route_freep) Route *route = NULL; usec_t timestamp_usec; uint32_t lifetime_sec; + struct in6_addr prefix; unsigned prefixlen; int r; @@ -429,6 +430,10 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); + r = sd_ndisc_router_prefix_get_address(rt, &prefix); + if (r < 0) + return log_link_error_errno(link, r, "Failed to get prefix address: %m"); + r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen); if (r < 0) return log_link_error_errno(link, r, "Failed to get prefix length: %m"); @@ -439,13 +444,10 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { route->family = AF_INET6; route->flags = RTM_F_PREFIX; + route->dst.in6 = prefix; route->dst_prefixlen = prefixlen; route->lifetime_usec = sec_to_usec(lifetime_sec, timestamp_usec); - r = sd_ndisc_router_prefix_get_address(rt, &route->dst.in6); - if (r < 0) - return log_link_error_errno(link, r, "Failed to get prefix address: %m"); - r = ndisc_request_route(TAKE_PTR(route), link, rt); if (r < 0) return log_link_error_errno(link, r, "Could not request prefix route: %m"); From a115c60e0de034c81d10f9ecad3045aa663884b7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 3 Oct 2022 10:12:23 +0900 Subject: [PATCH 2/9] network: ndisc: ignore prefix option with link-local prefix See https://www.rfc-editor.org/rfc/rfc4861#section-4.6.2. --- src/network/networkd-ndisc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index b1201eb00be..e992ebd1a9f 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -469,6 +469,14 @@ static int ndisc_router_process_prefix(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get prefix address: %m"); + /* RFC 4861 Section 4.6.2: + * 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."); + return 0; + } + r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen); if (r < 0) return log_link_error_errno(link, r, "Failed to get prefix length: %m"); From d4b7631468e0a28da1366d7f2d6f616b35acce70 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 3 Oct 2022 12:39:56 +0900 Subject: [PATCH 3/9] network: introduce {address,route}_remove_and_drop() Preparation for later commits. --- src/network/networkd-address.c | 12 ++++++++++++ src/network/networkd-address.h | 1 + src/network/networkd-route.c | 15 +++++++++++++++ src/network/networkd-route.h | 1 + 4 files changed, 29 insertions(+) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index bb1eedc6f1d..60d3c6286ed 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -765,6 +765,18 @@ int address_remove(Address *address) { return 0; } +int address_remove_and_drop(Address *address) { + if (!address) + return 0; + + address_cancel_request(address); + + if (address_exists(address)) + return address_remove(address); + + return address_drop(address); +} + bool link_address_is_dynamic(const Link *link, const Address *address) { Route *route; diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index ef29caf4e78..7a1e44632d5 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -78,6 +78,7 @@ Address* address_free(Address *address); int address_get(Link *link, const Address *in, Address **ret); int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg); int address_remove(Address *address); +int address_remove_and_drop(Address *address); int address_dup(const Address *src, Address **ret); bool address_is_ready(const Address *a); void address_set_broadcast(Address *a, Link *link); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index f1fba4ff425..2b17959ca2e 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -781,6 +781,21 @@ int route_remove(Route *route) { return 0; } +int route_remove_and_drop(Route *route) { + if (!route) + return 0; + + route_cancel_request(route, NULL); + + if (route_exists(route)) + return route_remove(route); + + if (route->state == 0) + route_free(route); + + return 0; +} + static void manager_mark_routes(Manager *manager, bool foreign, const Link *except) { Route *route; Link *link; diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 071803cefbd..5fc76b17f85 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -85,6 +85,7 @@ int route_dup(const Route *src, Route **ret); int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg); int route_remove(Route *route); +int route_remove_and_drop(Route *route); int route_get(Manager *manager, Link *link, const Route *in, Route **ret); From 8d1babc51d9f40da28bc08c9d0ff401bd00b8e1f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 3 Oct 2022 23:57:48 +0900 Subject: [PATCH 4/9] network: ndisc: address_get() returns 0 on success After the commit 3b6a3bdebfb555754fdc6ee507e3f6964de7b61c, address_get() does not return 1. --- src/network/networkd-ndisc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index e992ebd1a9f..5b772adc033 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -389,7 +389,7 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r * honoring all valid lifetimes to improve the reaction of SLAAC to renumbering events. * See draft-ietf-6man-slaac-renum-02, section 4.2. */ r = address_get(link, address, &e); - if (r > 0) { + if (r >= 0) { /* If the address is already assigned, but not valid anymore, then refuse to * update the address, and it will be removed. */ if (e->lifetime_valid_usec < timestamp_usec) From 94e6d37c2be87f8a9e7d563aa0c487946a6e5cc8 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 3 Oct 2022 12:18:49 +0900 Subject: [PATCH 5/9] network: ndisc: drop outdated settings before processing RA message Otherwise, e.g. if a router is replaced, then the previously received settings may never dropped. Follow-up for 2ccada8dc4a3571468a335808fd6fe49b8c6c6dd. --- src/network/networkd-ndisc.c | 83 ++++++++++++++++++++++++++++++------ 1 file changed, 71 insertions(+), 12 deletions(-) diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 5b772adc033..6452010d2d4 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -372,7 +372,6 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r SET_FOREACH(a, addresses) { _cleanup_(address_freep) Address *address = NULL; - Address *e; r = address_new(&address); if (r < 0) @@ -385,17 +384,6 @@ 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; - /* See RFC4862, section 5.5.3.e. But the following logic is deviated from RFC4862 by - * honoring all valid lifetimes to improve the reaction of SLAAC to renumbering events. - * See draft-ietf-6man-slaac-renum-02, section 4.2. */ - r = address_get(link, address, &e); - if (r >= 0) { - /* If the address is already assigned, but not valid anymore, then refuse to - * update the address, and it will be removed. */ - if (e->lifetime_valid_usec < timestamp_usec) - continue; - } - r = ndisc_request_address(TAKE_PTR(address), link, rt); if (r < 0) return log_link_error_errno(link, r, "Could not request SLAAC address: %m"); @@ -824,6 +812,68 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { } } +static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) { + bool updated = false; + NDiscDNSSL *dnssl; + NDiscRDNSS *rdnss; + Address *address; + Route *route; + int r = 0, k; + + assert(link); + + /* If an address or friends is already assigned, but not valid anymore, then refuse to update it, + * and let's immediately remove it. + * See RFC4862, section 5.5.3.e. But the following logic is deviated from RFC4862 by honoring all + * valid lifetimes to improve the reaction of SLAAC to renumbering events. + * See draft-ietf-6man-slaac-renum-02, section 4.2. */ + + SET_FOREACH(route, link->routes) { + if (route->source != NETWORK_CONFIG_SOURCE_NDISC) + continue; + + if (route->lifetime_usec >= timestamp_usec) + continue; /* the route is still valid */ + + k = route_remove_and_drop(route); + if (k < 0) + r = log_link_warning_errno(link, k, "Failed to remove outdated SLAAC route, ignoring: %m"); + } + + SET_FOREACH(address, link->addresses) { + if (address->source != NETWORK_CONFIG_SOURCE_NDISC) + continue; + + if (address->lifetime_valid_usec >= timestamp_usec) + continue; /* the address is still valid */ + + k = address_remove_and_drop(address); + if (k < 0) + r = log_link_warning_errno(link, k, "Failed to remove outdated SLAAC address, ignoring: %m"); + } + + SET_FOREACH(rdnss, link->ndisc_rdnss) { + if (rdnss->lifetime_usec >= timestamp_usec) + continue; /* the DNS server is still valid */ + + free(set_remove(link->ndisc_rdnss, rdnss)); + updated = true; + } + + SET_FOREACH(dnssl, link->ndisc_dnssl) { + if (dnssl->lifetime_usec >= timestamp_usec) + continue; /* the DNS domain is still valid */ + + free(set_remove(link->ndisc_dnssl, dnssl)); + updated = true; + } + + if (updated) + link_dirty(link); + + return r; +} + static int ndisc_start_dhcp6_client(Link *link, sd_ndisc_router *rt) { int r; @@ -869,6 +919,7 @@ static int ndisc_start_dhcp6_client(Link *link, sd_ndisc_router *rt) { static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { struct in6_addr router; + usec_t timestamp_usec; int r; assert(link); @@ -890,6 +941,14 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { return 0; } + r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec); + if (r < 0) + return r; + + r = ndisc_drop_outdated(link, timestamp_usec); + if (r < 0) + return r; + r = ndisc_start_dhcp6_client(link, rt); if (r < 0) return r; From 773024685b37170395a11716f8e4ad99d3580455 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 4 Oct 2022 00:19:13 +0900 Subject: [PATCH 6/9] network: ndisc: also introduce timer event source to drop outdated settings Otherwise, settings based on previously received RA messages will never removed without receiving a new RA message. --- src/network/networkd-link.c | 2 +- src/network/networkd-link.h | 1 + src/network/networkd-ndisc.c | 77 ++++++++++++++++++++++++++++++++++++ src/network/networkd-ndisc.h | 1 + 4 files changed, 80 insertions(+), 1 deletion(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 57335216115..69d5eb9d1b9 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -332,7 +332,7 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) { if (k < 0) r = log_link_warning_errno(link, k, "Could not remove DHCPv6 PD addresses and routes: %m"); - k = sd_ndisc_stop(link->ndisc); + k = ndisc_stop(link); if (k < 0) r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m"); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 362f439940a..9f1cdca3127 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -147,6 +147,7 @@ typedef struct Link { sd_dhcp_server *dhcp_server; sd_ndisc *ndisc; + sd_event_source *ndisc_expire; Set *ndisc_rdnss; Set *ndisc_dnssl; unsigned ndisc_messages; diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 6452010d2d4..124c7771053 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -10,6 +10,7 @@ #include "sd-ndisc.h" +#include "event-util.h" #include "missing_network.h" #include "networkd-address-generation.h" #include "networkd-address.h" @@ -874,6 +875,69 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) { return r; } +static int ndisc_setup_expire(Link *link); + +static int ndisc_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) { + Link *link = ASSERT_PTR(userdata); + usec_t now_usec; + + assert(link->manager); + + assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0); + + (void) ndisc_drop_outdated(link, now_usec); + (void) ndisc_setup_expire(link); + return 0; +} + +static int ndisc_setup_expire(Link *link) { + usec_t lifetime_usec = USEC_INFINITY; + NDiscDNSSL *dnssl; + NDiscRDNSS *rdnss; + Address *address; + Route *route; + int r; + + assert(link); + assert(link->manager); + + SET_FOREACH(route, link->routes) { + if (route->source != NETWORK_CONFIG_SOURCE_NDISC) + continue; + + if (!route_exists(route)) + continue; + + lifetime_usec = MIN(lifetime_usec, route->lifetime_usec); + } + + SET_FOREACH(address, link->addresses) { + if (address->source != NETWORK_CONFIG_SOURCE_NDISC) + continue; + + if (!address_exists(address)) + continue; + + lifetime_usec = MIN(lifetime_usec, address->lifetime_valid_usec); + } + + SET_FOREACH(rdnss, link->ndisc_rdnss) + lifetime_usec = MIN(lifetime_usec, rdnss->lifetime_usec); + + SET_FOREACH(dnssl, link->ndisc_dnssl) + lifetime_usec = MIN(lifetime_usec, dnssl->lifetime_usec); + + if (lifetime_usec == USEC_INFINITY) + return 0; + + r = event_reset_time(link->manager->event, &link->ndisc_expire, CLOCK_BOOTTIME, + lifetime_usec, 0, ndisc_expire_handler, link, 0, "ndisc-expiration", true); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to update expiration timer for ndisc: %m"); + + return 0; +} + static int ndisc_start_dhcp6_client(Link *link, sd_ndisc_router *rt) { int r; @@ -961,6 +1025,10 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { if (r < 0) return r; + r = ndisc_setup_expire(link); + if (r < 0) + return r; + if (link->ndisc_messages == 0) link->ndisc_configured = true; else @@ -1101,6 +1169,15 @@ int link_request_ndisc(Link *link) { return 0; } +int ndisc_stop(Link *link) { + assert(link); + + link->ndisc_expire = sd_event_source_disable_unref(link->ndisc_expire); + + return sd_ndisc_stop(link->ndisc); +} + + void ndisc_vacuum(Link *link) { NDiscRDNSS *r; NDiscDNSSL *d; diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h index bdbe2f41f1d..b696db9df13 100644 --- a/src/network/networkd-ndisc.h +++ b/src/network/networkd-ndisc.h @@ -40,6 +40,7 @@ bool link_ipv6_accept_ra_enabled(Link *link); void network_adjust_ipv6_accept_ra(Network *network); int ndisc_start(Link *link); +int ndisc_stop(Link *link); void ndisc_vacuum(Link *link); void ndisc_flush(Link *link); From 0cf1fe88885bd5ae1944c331ee1a5ec80b0ddfbc Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 3 Oct 2022 12:42:40 +0900 Subject: [PATCH 7/9] network: make sec_to_usec() map 0sec -> 0usec Zero lifetime in RA is special, and we should not assign possibly very short lifetime addresses or friends. This should not change anything at least now, preparation for later commits. Note, DHCPv4 and v6 code also uses it, but sd-dhcp-client and sd-dhcp6-client already filtered messages with zero lifetime. Hence, the change should not affect DHCP code. --- src/network/networkd-util.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h index 84fdc99958f..e8c390196e1 100644 --- a/src/network/networkd-util.h +++ b/src/network/networkd-util.h @@ -36,12 +36,15 @@ typedef enum NetworkConfigState { NETWORK_CONFIG_STATE_REMOVING = 1 << 4, /* e.g. address_remove() is called, but no response is received yet */ } NetworkConfigState; -static inline usec_t sec16_to_usec(uint16_t sec, usec_t timestamp_usec) { - return sec == UINT16_MAX ? USEC_INFINITY : usec_add(timestamp_usec, sec * USEC_PER_SEC); +static inline usec_t sec_to_usec(uint32_t sec, usec_t timestamp_usec) { + return + sec == 0 ? 0 : + sec == UINT32_MAX ? USEC_INFINITY : + usec_add(timestamp_usec, sec * USEC_PER_SEC); } -static inline usec_t sec_to_usec(uint32_t sec, usec_t timestamp_usec) { - return sec == UINT32_MAX ? USEC_INFINITY : usec_add(timestamp_usec, sec * USEC_PER_SEC); +static inline usec_t sec16_to_usec(uint16_t sec, usec_t timestamp_usec) { + return sec_to_usec(sec == UINT16_MAX ? UINT32_MAX : (uint32_t) sec, timestamp_usec); } static inline uint32_t usec_to_sec(usec_t usec, usec_t now_usec) { From af2aea8bb64b0dc42ecbe5549216eb567681a803 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 3 Oct 2022 11:54:20 +0900 Subject: [PATCH 8/9] network: ndisc: drop addresses and friends when RA with zero lifetime is received Routers may send options with zero lifetime if previously announced information is outdated. Hence, if we receive such messages, then we need to drop relevant addresses or friends. See e.g. https://www.rfc-editor.org/rfc/rfc4861#section-12. Follow-up for 2ccada8dc4a3571468a335808fd6fe49b8c6c6dd. --- src/network/networkd-address.c | 10 ++++++++-- src/network/networkd-ndisc.c | 34 ++++++++++++++-------------------- src/network/networkd-route.c | 10 ++++++++-- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 60d3c6286ed..259cd312c99 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -1154,7 +1154,7 @@ int link_request_address( address_netlink_handler_t netlink_handler, Request **ret) { - Address *acquired, *existing; + Address *acquired, *existing = NULL; int r; assert(link); @@ -1187,7 +1187,13 @@ int link_request_address( address_set_broadcast(address, link); } - if (address_get(link, address, &existing) < 0) { + (void) address_get(link, address, &existing); + + if (address->lifetime_valid_usec == 0) + /* The requested address is outdated. Let's remove it. */ + return address_remove_and_drop(existing); + + if (!existing) { _cleanup_(address_freep) Address *tmp = NULL; if (consume_object) diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 124c7771053..f2057a2d980 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -235,9 +235,6 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get gateway lifetime from RA: %m"); - if (lifetime_sec == 0) /* not a default router */ - return 0; - r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec); if (r < 0) return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); @@ -351,11 +348,6 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r if (r < 0) return log_link_error_errno(link, r, "Failed to get prefix valid lifetime: %m"); - if (lifetime_valid_sec == 0) { - log_link_debug(link, "Ignoring prefix as its valid lifetime is zero."); - return 0; - } - r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred_sec); if (r < 0) return log_link_error_errno(link, r, "Failed to get prefix preferred lifetime: %m"); @@ -412,9 +404,6 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get prefix lifetime: %m"); - if (lifetime_sec == 0) - return 0; - r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec); if (r < 0) return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); @@ -515,9 +504,6 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get route lifetime from RA: %m"); - if (lifetime_sec == 0) - return 0; - r = sd_ndisc_router_route_get_address(rt, &dst); if (r < 0) return log_link_error_errno(link, r, "Failed to get route destination address: %m"); @@ -623,9 +609,6 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get RDNSS lifetime: %m"); - if (lifetime_sec == 0) - return 0; - lifetime_usec = sec_to_usec(lifetime_sec, timestamp_usec); n = sd_ndisc_router_rdnss_get_addresses(rt, &a); @@ -643,6 +626,13 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { .address = a[j], }; + if (lifetime_usec == 0) { + /* The entry is outdated. */ + free(set_remove(link->ndisc_rdnss, &d)); + updated = true; + continue; + } + rdnss = set_get(link->ndisc_rdnss, &d); if (rdnss) { rdnss->router = router; @@ -716,9 +706,6 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get DNSSL lifetime: %m"); - if (lifetime_sec == 0) - return 0; - lifetime_usec = sec_to_usec(lifetime_sec, timestamp_usec); r = sd_ndisc_router_dnssl_get_domains(rt, &l); @@ -741,6 +728,13 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { strcpy(NDISC_DNSSL_DOMAIN(s), *j); + if (lifetime_usec == 0) { + /* The entry is outdated. */ + free(set_remove(link->ndisc_dnssl, s)); + updated = true; + continue; + } + dnssl = set_get(link->ndisc_dnssl, s); if (dnssl) { dnssl->router = router; diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 2b17959ca2e..514845f7c5c 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -1426,7 +1426,7 @@ int link_request_route( route_netlink_handler_t netlink_handler, Request **ret) { - Route *existing; + Route *existing = NULL; int r; assert(link); @@ -1435,7 +1435,13 @@ int link_request_route( assert(route->source != NETWORK_CONFIG_SOURCE_FOREIGN); assert(!route_needs_convert(route)); - if (route_get(link->manager, link, route, &existing) < 0) { + (void) route_get(link->manager, link, route, &existing); + + if (route->lifetime_usec == 0) + /* The requested route is outdated. Let's remove it. */ + return route_remove_and_drop(existing); + + if (!existing) { _cleanup_(route_freep) Route *tmp = NULL; if (consume_object) From 8aba7b839b98eb97dd0e5d69a97ff4d69268e366 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 4 Oct 2022 00:06:46 +0900 Subject: [PATCH 9/9] network: ndisc: do not accept too many DNS servers or domains If there exists multiple routers, then the previous logic may introduce too many DNS servers or domains. --- src/network/networkd-ndisc.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index f2057a2d980..95722e24ef9 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -587,7 +587,7 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { uint32_t lifetime_sec; const struct in6_addr *a; struct in6_addr router; - bool updated = false; + bool updated = false, logged_about_too_many = false; int n, r; assert(link); @@ -615,11 +615,6 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { if (n < 0) return log_link_error_errno(link, n, "Failed to get RDNSS addresses: %m"); - if (n >= (int) NDISC_RDNSS_MAX) { - log_link_warning(link, "Too many RDNSS records per link. Only first %u records will be used.", NDISC_RDNSS_MAX); - n = NDISC_RDNSS_MAX; - } - for (int j = 0; j < n; j++) { _cleanup_free_ NDiscRDNSS *x = NULL; NDiscRDNSS *rdnss, d = { @@ -640,6 +635,13 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { continue; } + if (set_size(link->ndisc_rdnss) >= NDISC_RDNSS_MAX) { + if (!logged_about_too_many) + log_link_warning(link, "Too many RDNSS records per link. Only first %u records will be used.", NDISC_RDNSS_MAX); + logged_about_too_many = true; + continue; + } + x = new(NDiscRDNSS, 1); if (!x) return log_oom(); @@ -684,7 +686,7 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { usec_t lifetime_usec, timestamp_usec; struct in6_addr router; uint32_t lifetime_sec; - bool updated = false; + bool updated = false, logged_about_too_many = false; int r; assert(link); @@ -712,12 +714,6 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get DNSSL addresses: %m"); - if (strv_length(l) >= NDISC_DNSSL_MAX) { - log_link_warning(link, "Too many DNSSL records per link. Only first %u records will be used.", NDISC_DNSSL_MAX); - STRV_FOREACH(j, l + NDISC_DNSSL_MAX) - *j = mfree(*j); - } - STRV_FOREACH(j, l) { _cleanup_free_ NDiscDNSSL *s = NULL; NDiscDNSSL *dnssl; @@ -742,6 +738,13 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { continue; } + if (set_size(link->ndisc_dnssl) >= NDISC_DNSSL_MAX) { + if (!logged_about_too_many) + log_link_warning(link, "Too many DNSSL records per link. Only first %u records will be used.", NDISC_DNSSL_MAX); + logged_about_too_many = true; + continue; + } + s->router = router; s->lifetime_usec = lifetime_usec;