From d469cea3bde53bc39317c8b433c825bb4790cbe5 Mon Sep 17 00:00:00 2001 From: Michael Marley Date: Sun, 5 Jul 2020 06:46:27 -0400 Subject: [PATCH] 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. --- src/libsystemd-network/sd-radv.c | 18 +++++++++++------- src/libsystemd-network/test-ndisc-ra.c | 2 +- src/network/networkd-dhcp6.c | 2 +- src/network/networkd-link.c | 2 +- src/systemd/sd-radv.h | 3 ++- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index cc5c0223b5..ee7c0ee53f 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 d759ec03a8..7c59418ca7 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 ecf8a32691..d9df46a23e 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 053aa3b4ce..dcbf197ff4 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 011e40d8a5..763d5ddd87 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);