diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index bc3174b3916..a84fc3d5c87 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -4017,14 +4017,29 @@ ServerAddress=192.168.0.1/24
+
+ ReachableTimeSec=
+
+
+ Configures the time, used in the Neighbor Unreachability Detection algorithm, for which
+ clients can assume a neighbor is reachable after having received a reachability confirmation. Takes
+ a time span in the range 0…4294967295 ms. When 0, clients will handle it as if the value wasn't
+ specified. Defaults to 0.
+
+
+
+
+
RetransmitSec=
- Takes a timespan. Configures the retransmit time, used by clients to retransmit Neighbor
- Solicitation messages on address resolution and the Neighbor Unreachability Detection algorithm.
- An integer, the default unit is seconds, in the range 0…4294967295 msec. Defaults to 0.
+
+ Configures the time, used in the Neighbor Unreachability Detection algorithm, for which
+ clients can use as retransmit time on address resolution and the Neighbor Unreachability Detection
+ algorithm. Takes a time span in the range 0…4294967295 ms. When 0, clients will handle it as if
+ the value wasn't specified. Defaults to 0.
-
+
diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h
index 378781c138d..0a535ddf4bd 100644
--- a/src/libsystemd-network/radv-internal.h
+++ b/src/libsystemd-network/radv-internal.h
@@ -43,9 +43,11 @@
#define RADV_MAX_ROUTER_LIFETIME_USEC (9000 * USEC_PER_SEC)
#define RADV_DEFAULT_ROUTER_LIFETIME_USEC (3 * RADV_DEFAULT_MAX_TIMEOUT_USEC)
/* RFC 4861 section 4.2.
- * Retrans Timer
+ * Reachable Time and Retrans Timer
* 32-bit unsigned integer. The time, in milliseconds. */
-#define RADV_MAX_RETRANSMIT_USEC (UINT32_MAX * USEC_PER_MSEC)
+#define RADV_MAX_UINT32_MSEC_USEC (UINT32_MAX * USEC_PER_MSEC)
+#define RADV_MAX_REACHABLE_TIME_USEC RADV_MAX_UINT32_MSEC_USEC
+#define RADV_MAX_RETRANSMIT_USEC RADV_MAX_UINT32_MSEC_USEC
/* draft-ietf-6man-slaac-renum-02 section 4.1.1.
* AdvPreferredLifetime: max(AdvDefaultLifetime, 3 * MaxRtrAdvInterval)
* AdvValidLifetime: 2 * AdvPreferredLifetime */
@@ -107,6 +109,7 @@ struct sd_radv {
uint8_t flags;
uint8_t preference;
uint32_t mtu;
+ usec_t reachable_usec;
usec_t retransmit_usec;
usec_t lifetime_usec; /* timespan */
diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c
index f6f30ce3197..a30a805271b 100644
--- a/src/libsystemd-network/sd-radv.c
+++ b/src/libsystemd-network/sd-radv.c
@@ -163,6 +163,7 @@ static int radv_send_router(sd_radv *ra, const struct in6_addr *dst) {
struct nd_router_advert adv = {
.nd_ra_type = ND_ROUTER_ADVERT,
.nd_ra_router_lifetime = usec_to_be16_sec(ra->lifetime_usec),
+ .nd_ra_reachable = usec_to_be32_msec(ra->reachable_usec),
.nd_ra_retransmit = usec_to_be32_msec(ra->retransmit_usec),
};
struct {
@@ -538,6 +539,13 @@ int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit) {
return 0;
}
+int sd_radv_set_reachable_time(sd_radv *ra, uint64_t usec) {
+ assert_return(ra, -EINVAL);
+
+ ra->reachable_usec = usec;
+ return 0;
+}
+
int sd_radv_set_retransmit(sd_radv *ra, uint64_t usec) {
assert_return(ra, -EINVAL);
diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c
index 78635ee6576..8be7351a098 100644
--- a/src/libsystemd-network/test-ndisc-ra.c
+++ b/src/libsystemd-network/test-ndisc-ra.c
@@ -199,6 +199,11 @@ TEST(radv) {
assert_se(sd_radv_set_other_information(ra, true) >= 0);
assert_se(sd_radv_set_other_information(ra, false) >= 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_reachable_time(NULL, 10 * USEC_PER_MSEC) < 0);
+ assert_se(sd_radv_set_reachable_time(ra, 10 * USEC_PER_MSEC) >= 0);
+ assert_se(sd_radv_set_reachable_time(ra, 0) >= 0);
+ assert_se(sd_radv_set_reachable_time(ra, USEC_INFINITY) >= 0);
+
ASSERT_RETURN_EXPECTED_SE(sd_radv_set_retransmit(NULL, 10 * USEC_PER_MSEC) < 0);
assert_se(sd_radv_set_retransmit(ra, 10 * USEC_PER_MSEC) >= 0);
assert_se(sd_radv_set_retransmit(ra, 0) >= 0);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 62f11c5c5c9..4124b3270fe 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -393,7 +393,8 @@ DHCPPrefixDelegation.RouteMetric, config_parse_uint32,
DHCPPrefixDelegation.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, dhcp_pd_netlabel)
DHCPPrefixDelegation.NFTSet, config_parse_nft_set, NFT_SET_PARSE_NETWORK, offsetof(Network, dhcp_pd_nft_set_context)
IPv6SendRA.RouterLifetimeSec, config_parse_router_lifetime, 0, offsetof(Network, router_lifetime_usec)
-IPv6SendRA.RetransmitSec, config_parse_router_retransmit, 0, offsetof(Network, router_retransmit_usec)
+IPv6SendRA.ReachableTimeSec, config_parse_router_uint32_msec_usec, 0, offsetof(Network, router_reachable_usec)
+IPv6SendRA.RetransmitSec, config_parse_router_uint32_msec_usec, 0, offsetof(Network, router_retransmit_usec)
IPv6SendRA.Managed, config_parse_bool, 0, offsetof(Network, router_managed)
IPv6SendRA.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information)
IPv6SendRA.RouterPreference, config_parse_router_preference, 0, 0
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index c8573e09734..92d367eccf8 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -246,6 +246,7 @@ struct Network {
RADVPrefixDelegation router_prefix_delegation;
usec_t router_lifetime_usec;
uint8_t router_preference;
+ usec_t router_reachable_usec;
usec_t router_retransmit_usec;
uint8_t router_hop_limit;
bool router_managed;
diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c
index fa5884a6c35..0f0f3a34ce9 100644
--- a/src/network/networkd-radv.c
+++ b/src/network/networkd-radv.c
@@ -577,6 +577,10 @@ static int radv_configure(Link *link) {
return r;
}
+ r = sd_radv_set_reachable_time(link->radv, link->network->router_reachable_usec);
+ if (r < 0)
+ return r;
+
if (link->network->router_retransmit_usec > 0) {
r = sd_radv_set_retransmit(link->radv, link->network->router_retransmit_usec);
if (r < 0)
@@ -1496,7 +1500,7 @@ int config_parse_router_lifetime(
return 0;
}
-int config_parse_router_retransmit(
+int config_parse_router_uint32_msec_usec(
const char *unit,
const char *filename,
unsigned line,
@@ -1508,7 +1512,7 @@ int config_parse_router_retransmit(
void *data,
void *userdata) {
- usec_t usec, *router_retransmit_usec = ASSERT_PTR(data);
+ usec_t usec, *router_usec = ASSERT_PTR(data);
int r;
assert(filename);
@@ -1517,7 +1521,7 @@ int config_parse_router_retransmit(
assert(rvalue);
if (isempty(rvalue)) {
- *router_retransmit_usec = 0;
+ *router_usec = 0;
return 0;
}
@@ -1529,13 +1533,13 @@ int config_parse_router_retransmit(
}
if (usec != USEC_INFINITY &&
- usec > RADV_MAX_RETRANSMIT_USEC) {
+ usec > RADV_MAX_UINT32_MSEC_USEC) {
log_syntax(unit, LOG_WARNING, filename, line, 0,
"Invalid [%s] %s=, ignoring assignment: %s", section, lvalue, rvalue);
return 0;
}
- *router_retransmit_usec = usec;
+ *router_usec = usec;
return 0;
}
diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h
index 94834e77a8e..dcb8cfb304e 100644
--- a/src/network/networkd-radv.h
+++ b/src/network/networkd-radv.h
@@ -86,7 +86,7 @@ RADVPrefixDelegation radv_prefix_delegation_from_string(const char *s) _pure_;
CONFIG_PARSER_PROTOTYPE(config_parse_router_prefix_delegation);
CONFIG_PARSER_PROTOTYPE(config_parse_router_lifetime);
-CONFIG_PARSER_PROTOTYPE(config_parse_router_retransmit);
+CONFIG_PARSER_PROTOTYPE(config_parse_router_uint32_msec_usec);
CONFIG_PARSER_PROTOTYPE(config_parse_router_preference);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_boolean);
diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h
index f25622fceb5..3120dd7f00a 100644
--- a/src/systemd/sd-radv.h
+++ b/src/systemd/sd-radv.h
@@ -57,6 +57,7 @@ int sd_radv_set_link_local_address(sd_radv *ra, const struct in6_addr *addr);
int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr);
int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu);
int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit);
+int sd_radv_set_reachable_time(sd_radv *ra, uint64_t usec);
int sd_radv_set_retransmit(sd_radv *ra, uint64_t usec);
int sd_radv_set_router_lifetime(sd_radv *ra, uint64_t usec);
int sd_radv_set_managed_information(sd_radv *ra, int b);
diff --git a/test/test-network/conf/25-ipv6-prefix.network b/test/test-network/conf/25-ipv6-prefix.network
index 9cf8f257535..e801581a898 100644
--- a/test/test-network/conf/25-ipv6-prefix.network
+++ b/test/test-network/conf/25-ipv6-prefix.network
@@ -10,6 +10,8 @@ IPv6SendRA=yes
DNS=_link_local 2002:da8:1:0::1
DNSLifetimeSec=1min
Domains=hogehoge.test
+ReachableTimeSec=42
+RetransmitSec=500ms
[IPv6Prefix]
Prefix=2002:da8:1:0::/64
diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py
index 17b45a43391..569563175bf 100755
--- a/test/test-network/systemd-networkd-tests.py
+++ b/test/test-network/systemd-networkd-tests.py
@@ -5514,6 +5514,9 @@ class NetworkdRATests(unittest.TestCase, Utilities):
print(output)
self.assertRegex(output, '2002:da8:1:0')
+ self.check_ipv6_neigh_sysctl_attr('veth99', 'base_reachable_time_ms', '42000')
+ self.check_ipv6_neigh_sysctl_attr('veth99', 'retrans_time_ms', '500')
+
self.check_netlabel('veth99', '2002:da8:1::/64')
self.check_netlabel('veth99', '2002:da8:2::/64')