diff --git a/man/systemd.network.xml b/man/systemd.network.xml index f6f91fb458..433ec4bb0d 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -991,6 +991,17 @@ DuplicateAddressDetection=none + + IPv4DuplicateAddressDetectionTimeoutSec= + + Configures the maximum timeout for IPv4 Duplicate Address Detection (RFC 5227). Must be a + value between 1 millisecond and 60 seconds. If set, Duplicate Address Detection takes a randomized + time between 57% (4/7) and 100% of the given value. If unset, defaults to 7 seconds. + + + + + IPv4ReversePathFilter= diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c index 423e043253..82d5fa7568 100644 --- a/src/libsystemd-network/sd-ipv4acd.c +++ b/src/libsystemd-network/sd-ipv4acd.c @@ -25,18 +25,26 @@ #include "string-util.h" #include "time-util.h" -/* Constants from the RFC */ -#define PROBE_WAIT_USEC (1U * USEC_PER_SEC) -#define PROBE_NUM 3U -#define PROBE_MIN_USEC (1U * USEC_PER_SEC) -#define PROBE_MAX_USEC (2U * USEC_PER_SEC) -#define ANNOUNCE_WAIT_USEC (2U * USEC_PER_SEC) -#define ANNOUNCE_NUM 2U +/* Intervals from the RFC in seconds, need to be multiplied by the time unit */ +#define PROBE_WAIT 1U +#define PROBE_MIN 1U +#define PROBE_MAX 2U +#define ANNOUNCE_WAIT 2U +#define TOTAL_TIME_UNITS 7U + +/* Intervals from the RFC not adjusted to the time unit */ #define ANNOUNCE_INTERVAL_USEC (2U * USEC_PER_SEC) -#define MAX_CONFLICTS 10U #define RATE_LIMIT_INTERVAL_USEC (60U * USEC_PER_SEC) #define DEFEND_INTERVAL_USEC (10U * USEC_PER_SEC) +/* Other constants from the RFC */ +#define PROBE_NUM 3U +#define ANNOUNCE_NUM 2U +#define MAX_CONFLICTS 10U + +/* Default timeout from the RFC */ +#define DEFAULT_ACD_TIMEOUT_USEC (7 * USEC_PER_SEC) + typedef enum IPv4ACDState { IPV4ACD_STATE_INIT, IPV4ACD_STATE_STARTED, @@ -60,6 +68,10 @@ struct sd_ipv4acd { unsigned n_iteration; unsigned n_conflict; + /* Indicates the duration of a "time unit", i.e. one second in the RFC but scaled to the + * chosen total duration. Represents 1/7 of the total conflict detection timeout. */ + usec_t time_unit_usec; + sd_event_source *receive_message_event_source; sd_event_source *timer_event_source; @@ -150,6 +162,7 @@ int sd_ipv4acd_new(sd_ipv4acd **ret) { *acd = (sd_ipv4acd) { .n_ref = 1, .state = IPV4ACD_STATE_INIT, + .time_unit_usec = DEFAULT_ACD_TIMEOUT_USEC / TOTAL_TIME_UNITS, .ifindex = -1, .fd = -EBADF, }; @@ -218,14 +231,20 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) case IPV4ACD_STATE_STARTED: acd->defend_window = 0; + log_ipv4acd(acd, + "Started on address " IPV4_ADDRESS_FMT_STR " with a max timeout of %s", + IPV4_ADDRESS_FMT_VAL(acd->address), + FORMAT_TIMESPAN(TOTAL_TIME_UNITS * acd->time_unit_usec, USEC_PER_MSEC)); + ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_PROBE, true); if (acd->n_conflict >= MAX_CONFLICTS) { log_ipv4acd(acd, "Max conflicts reached, delaying by %s", FORMAT_TIMESPAN(RATE_LIMIT_INTERVAL_USEC, 0)); - r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC); + r = ipv4acd_set_next_wakeup( + acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT * acd->time_unit_usec); } else - r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC); + r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT * acd->time_unit_usec); if (r < 0) goto fail; @@ -245,13 +264,16 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) if (acd->n_iteration < PROBE_NUM - 2) { ipv4acd_set_state(acd, IPV4ACD_STATE_PROBING, false); - r = ipv4acd_set_next_wakeup(acd, PROBE_MIN_USEC, (PROBE_MAX_USEC-PROBE_MIN_USEC)); + r = ipv4acd_set_next_wakeup( + acd, + PROBE_MIN * acd->time_unit_usec, + (PROBE_MAX - PROBE_MIN) * acd->time_unit_usec); if (r < 0) goto fail; } else { ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_ANNOUNCE, true); - r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_WAIT_USEC, 0); + r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_WAIT * acd->time_unit_usec, 0); if (r < 0) goto fail; } @@ -442,6 +464,19 @@ int sd_ipv4acd_set_ifname(sd_ipv4acd *acd, const char *ifname) { return free_and_strdup(&acd->ifname, ifname); } +int sd_ipv4acd_set_timeout(sd_ipv4acd *acd, uint64_t usec) { + assert_return(acd, -EINVAL); + + if (usec == 0) + usec = DEFAULT_ACD_TIMEOUT_USEC; + + /* Clamp the total duration to a value between 1ms and 1 minute */ + acd->time_unit_usec = DIV_ROUND_UP( + CLAMP(usec, 1U * USEC_PER_MSEC, 1U * USEC_PER_MINUTE), TOTAL_TIME_UNITS); + + return 0; +} + int sd_ipv4acd_get_ifname(sd_ipv4acd *acd, const char **ret) { int r; diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c index 5bf98332a9..557c145509 100644 --- a/src/libsystemd-network/sd-ipv4ll.c +++ b/src/libsystemd-network/sd-ipv4ll.c @@ -153,6 +153,12 @@ int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) { return 0; } +int sd_ipv4ll_set_timeout(sd_ipv4ll *ll, uint64_t usec) { + assert_return(ll, -EINVAL); + + return sd_ipv4acd_set_timeout(ll->acd, usec); +} + int sd_ipv4ll_detach_event(sd_ipv4ll *ll) { assert_return(ll, -EINVAL); diff --git a/src/network/networkd-ipv4acd.c b/src/network/networkd-ipv4acd.c index 0ecdee0893..3c938546bf 100644 --- a/src/network/networkd-ipv4acd.c +++ b/src/network/networkd-ipv4acd.c @@ -224,6 +224,7 @@ int ipv4acd_configure(Link *link, const Address *address) { assert(link); assert(link->manager); + assert(link->network); assert(address); if (address->family != AF_INET) @@ -268,6 +269,10 @@ int ipv4acd_configure(Link *link, const Address *address) { if (r < 0) return r; + r = sd_ipv4acd_set_timeout(acd, link->network->ipv4_dad_timeout_usec); + if (r < 0) + return r; + r = sd_ipv4acd_set_callback(acd, on_acd, link); if (r < 0) return r; diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 7522bca0aa..9dbaba0fa0 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -223,6 +223,7 @@ int ipv4ll_configure(Link *link) { int r; assert(link); + assert(link->network); if (!link_ipv4ll_enabled(link)) return 0; @@ -253,6 +254,10 @@ int ipv4ll_configure(Link *link) { if (r < 0) return r; + r = sd_ipv4ll_set_timeout(link->ipv4ll, link->network->ipv4_dad_timeout_usec); + if (r < 0) + return r; + r = sd_ipv4ll_set_ifindex(link->ipv4ll, link->ifindex); if (r < 0) return r; diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 7dd793bf5d..2b61f3b744 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -159,6 +159,7 @@ Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extension Network.IPv6AcceptRA, config_parse_tristate, 0, offsetof(Network, ndisc) Network.IPv6AcceptRouterAdvertisements, config_parse_tristate, 0, offsetof(Network, ndisc) Network.IPv6DuplicateAddressDetection, config_parse_int, 0, offsetof(Network, ipv6_dad_transmits) +Network.IPv4DuplicateAddressDetectionTimeoutSec, config_parse_sec, 0, offsetof(Network, ipv4_dad_timeout_usec) Network.IPv6HopLimit, config_parse_uint8, 0, offsetof(Network, ipv6_hop_limit) Network.IPv6RetransmissionTimeSec, config_parse_sec, 0, offsetof(Network, ipv6_retransmission_time) Network.IPv6ProxyNDP, config_parse_tristate, 0, offsetof(Network, ipv6_proxy_ndp) diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index c83281441e..c783da5f47 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -112,6 +112,7 @@ struct Network { char **bind_carrier; bool default_route_on_device; AddressFamily ip_masquerade; + usec_t ipv4_dad_timeout_usec; /* Protocol independent settings */ UseDomains use_domains; diff --git a/src/systemd/sd-ipv4acd.h b/src/systemd/sd-ipv4acd.h index 6be5770f13..65a53f9f2b 100644 --- a/src/systemd/sd-ipv4acd.h +++ b/src/systemd/sd-ipv4acd.h @@ -48,6 +48,7 @@ int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int interface_index); int sd_ipv4acd_get_ifindex(sd_ipv4acd *acd); int sd_ipv4acd_set_ifname(sd_ipv4acd *acd, const char *interface_name); int sd_ipv4acd_get_ifname(sd_ipv4acd *acd, const char **ret); +int sd_ipv4acd_set_timeout(sd_ipv4acd *acd, uint64_t usec); int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address); int sd_ipv4acd_is_running(sd_ipv4acd *acd); int sd_ipv4acd_is_bound(sd_ipv4acd *acd); diff --git a/src/systemd/sd-ipv4ll.h b/src/systemd/sd-ipv4ll.h index 35e4679a0d..1e7922cf67 100644 --- a/src/systemd/sd-ipv4ll.h +++ b/src/systemd/sd-ipv4ll.h @@ -44,6 +44,7 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address); int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata); int sd_ipv4ll_set_check_mac_callback(sd_ipv4ll *ll, sd_ipv4ll_check_mac_callback_t cb, void *userdata); int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr); +int sd_ipv4ll_set_timeout(sd_ipv4ll *ll, uint64_t usec); int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int interface_index); int sd_ipv4ll_get_ifindex(sd_ipv4ll *ll); int sd_ipv4ll_set_ifname(sd_ipv4ll *ll, const char *interface_name);