From: Michael Marley Date: Sun, 5 Jul 2020 10:46:27 +0000 (-0400) Subject: network: Don't send RA with zero router lifetime when restarting radv X-Git-Tag: v246-rc1~29 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d469cea3bde53bc39317c8b433c825bb4790cbe5;p=thirdparty%2Fsystemd.git network: Don't send RA with zero router lifetime when restarting radv While investigating https://github.com/systemd/systemd/issues/16356, I discovered that networkd stops the radv service before adding or updating prefixes and then starts it again. This causes networkd to send an RA with a router lifetime of zero, causing the routes to flap on systems receiving the RA for a fraction of a second before radv is started again and proper RAs are sent. That has the potential to cause issues with latency-sensitive traffic like gaming or VoIP. This patch adds a boolean argument to the sd_radv_stop() function to control this behavior. The zero lifetime RA is still sent whenever radv is actually being stopped, but when it is being restarted for a prefix update (from networkd-dhcp6.c), the final RA is no longer sent to avoid the route flapping. --- diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index cc5c0223b5c..ee7c0ee53fc 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -339,12 +339,12 @@ static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) { return 0; fail: - sd_radv_stop(ra); + sd_radv_stop(ra, true); return 0; } -_public_ int sd_radv_stop(sd_radv *ra) { +_public_ int sd_radv_stop(sd_radv *ra, bool zero_router_lifetime) { int r; assert_return(ra, -EINVAL); @@ -354,11 +354,15 @@ _public_ int sd_radv_stop(sd_radv *ra) { log_radv("Stopping IPv6 Router Advertisement daemon"); - /* RFC 4861, Section 6.2.5, send at least one Router Advertisement - with zero lifetime */ - r = radv_send(ra, NULL, 0); - if (r < 0) - log_radv_errno(r, "Unable to send last Router Advertisement with router lifetime set to zero: %m"); + if (zero_router_lifetime) { + /* RFC 4861, Section 6.2.5, send at least one Router Advertisement + with zero lifetime */ + r = radv_send(ra, NULL, 0); + if (r < 0) + log_radv_errno(r, "Unable to send last Router Advertisement with router lifetime set to zero: %m"); + else + log_radv("Sent last Router Advertisement with router lifetime set to zero"); + } radv_reset(ra); ra->fd = safe_close(ra->fd); diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c index d759ec03a8a..7c59418ca7d 100644 --- a/src/libsystemd-network/test-ndisc-ra.c +++ b/src/libsystemd-network/test-ndisc-ra.c @@ -284,7 +284,7 @@ static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdat return 0; } - assert_se(sd_radv_stop(ra) >= 0); + assert_se(sd_radv_stop(ra, true) >= 0); test_stopped = true; return 0; diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index ecf8a32691d..d9df46a23ee 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -188,7 +188,7 @@ static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix, if (r < 0) return r; - r = sd_radv_stop(radv); + r = sd_radv_stop(radv, false); if (r < 0) return r; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 053aa3b4cee..dcbf197ff46 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -840,7 +840,7 @@ int link_stop_clients(Link *link, bool may_keep_dhcp) { } if (link->radv) { - k = sd_radv_stop(link->radv); + k = sd_radv_stop(link->radv, true); if (k < 0) r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Advertisement: %m"); } diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h index 011e40d8a5c..763d5ddd87d 100644 --- a/src/systemd/sd-radv.h +++ b/src/systemd/sd-radv.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "_sd-common.h" @@ -49,7 +50,7 @@ int sd_radv_detach_event(sd_radv *nd); sd_event *sd_radv_get_event(sd_radv *ra); int sd_radv_start(sd_radv *ra); -int sd_radv_stop(sd_radv *ra); +int sd_radv_stop(sd_radv *ra, bool zero_router_lifetime); int sd_radv_set_ifindex(sd_radv *ra, int interface_index); int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr);