diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 38a0173cf09..fcf37a19b82 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -3076,6 +3076,15 @@ Token=prefixstable:2002:da8:1::
+
+ 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 of seconds, in the range 0…4294967295 msec. Defaults to 0.
+
+
+
RouterPreference=
diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h
index 1a268757ad2..4bb32f21283 100644
--- a/src/libsystemd-network/radv-internal.h
+++ b/src/libsystemd-network/radv-internal.h
@@ -87,6 +87,7 @@ struct sd_radv {
uint8_t hop_limit;
uint8_t flags;
uint32_t mtu;
+ uint32_t retransmit_msec;
usec_t lifetime_usec; /* timespan */
int fd;
diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c
index 839c9a9ee6f..5d4dfdef028 100644
--- a/src/libsystemd-network/sd-radv.c
+++ b/src/libsystemd-network/sd-radv.c
@@ -179,6 +179,7 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, usec_t lifetime_us
adv.nd_ra_type = ND_ROUTER_ADVERT;
adv.nd_ra_curhoplimit = ra->hop_limit;
+ adv.nd_ra_retransmit = htobe32(ra->retransmit_msec);
adv.nd_ra_flags_reserved = ra->flags;
assert_cc(RADV_MAX_ROUTER_LIFETIME_USEC <= UINT16_MAX * USEC_PER_SEC);
adv.nd_ra_router_lifetime = htobe16(DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC));
@@ -494,6 +495,17 @@ int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit) {
return 0;
}
+int sd_radv_set_retransmit(sd_radv *ra, uint32_t retransmit_msec) {
+ assert_return(ra, -EINVAL);
+
+ if (ra->state != RADV_STATE_IDLE)
+ return -EBUSY;
+
+ ra->retransmit_msec = retransmit_msec;
+
+ return 0;
+}
+
int sd_radv_set_router_lifetime(sd_radv *ra, uint64_t lifetime_usec) {
assert_return(ra, -EINVAL);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 2e062ce3197..a364abf2b47 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -368,6 +368,7 @@ DHCPPrefixDelegation.Token, config_parse_address_generation_typ
DHCPPrefixDelegation.RouteMetric, config_parse_uint32, 0, offsetof(Network, dhcp_pd_route_metric)
DHCPPrefixDelegation.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, dhcp_pd_netlabel)
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.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 f3b3271811e..75f1be442aa 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -227,6 +227,7 @@ struct Network {
RADVPrefixDelegation router_prefix_delegation;
usec_t router_lifetime_usec;
uint8_t router_preference;
+ usec_t router_retransmit_usec;
bool router_managed;
bool router_other_information;
bool router_emit_dns;
diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c
index 0f67ff09732..e6ef3974f28 100644
--- a/src/network/networkd-radv.c
+++ b/src/network/networkd-radv.c
@@ -486,6 +486,12 @@ static int radv_configure(Link *link) {
return r;
}
+ if (link->network->router_retransmit_usec > 0) {
+ r = sd_radv_set_retransmit(link->radv, DIV_ROUND_UP(link->network->router_retransmit_usec, USEC_PER_MSEC));
+ if (r < 0)
+ return r;
+ }
+
HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
r = radv_set_prefix(link, p);
if (r < 0 && r != -EEXIST)
@@ -1305,6 +1311,49 @@ int config_parse_router_lifetime(
return 0;
}
+int config_parse_router_retransmit(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ usec_t usec, *router_retransmit_usec = ASSERT_PTR(data);
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ *router_retransmit_usec = 0;
+ return 0;
+ }
+
+ r = parse_sec(rvalue, &usec);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ if (usec != USEC_INFINITY &&
+ DIV_ROUND_UP(usec, USEC_PER_MSEC) > UINT32_MAX) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Invalid %s= must be in the range 0...%"PRIu32"Sec, ignoring: %s", lvalue, UINT32_MAX, rvalue);
+ return 0;
+ }
+
+ *router_retransmit_usec = usec;
+ return 0;
+}
+
int config_parse_router_preference(
const char *unit,
const char *filename,
diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h
index ebebb3942b8..e1c22d054f2 100644
--- a/src/network/networkd-radv.h
+++ b/src/network/networkd-radv.h
@@ -74,6 +74,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_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 5e8d06f18f4..98fda297a72 100644
--- a/src/systemd/sd-radv.h
+++ b/src/systemd/sd-radv.h
@@ -53,6 +53,7 @@ int sd_radv_get_ifname(sd_radv *ra, const char **ret);
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_retransmit(sd_radv *ra, uint32_t retransmit_msec);
int sd_radv_set_router_lifetime(sd_radv *ra, uint64_t lifetime_usec);
int sd_radv_set_managed_information(sd_radv *ra, int managed);
int sd_radv_set_other_information(sd_radv *ra, int other);