diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2019-12-07 14:11:44 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-12-07 14:11:44 +0900 |
commit | 43a20059a5b3e263f3b00c9732578fc535a4f059 (patch) | |
tree | 58a10d340b51ae9c1c060e9bdb74dafb0ad6f626 | |
parent | 65ca546f0697f7a17bd435abd0d77bec26dbf28e (diff) | |
parent | dc7d3c5fd4cfba5f467457c248bc722faad2789d (diff) | |
download | systemd-43a20059a5b3e263f3b00c9732578fc535a4f059.tar.gz |
Merge pull request #14102 from ssahani/acd-duplicate-ip
network: introduce DAD for static IPV4 address
-rw-r--r-- | man/systemd.network.xml | 10 | ||||
-rw-r--r-- | src/network/networkd-address.c | 152 | ||||
-rw-r--r-- | src/network/networkd-address.h | 16 | ||||
-rw-r--r-- | src/network/networkd-link.c | 31 | ||||
-rw-r--r-- | src/network/networkd-network-gperf.gperf | 2 | ||||
-rw-r--r-- | src/network/networkd-util.c | 8 | ||||
-rw-r--r-- | src/network/networkd-util.h | 3 | ||||
-rw-r--r-- | test/test-network/conf/25-address-dad-veth-peer.network | 9 | ||||
-rw-r--r-- | test/test-network/conf/25-address-dad-veth99.network | 8 | ||||
-rwxr-xr-x | test/test-network/systemd-networkd-tests.py | 16 |
10 files changed, 241 insertions, 14 deletions
diff --git a/man/systemd.network.xml b/man/systemd.network.xml index a2ac24059a..63dfb517b6 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -986,9 +986,13 @@ <varlistentry> <term><varname>DuplicateAddressDetection=</varname></term> <listitem> - <para>Takes a boolean. Do not perform Duplicate Address Detection - <ulink url="https://tools.ietf.org/html/rfc4862">RFC 4862</ulink> when adding this address. - Supported only on IPv6. Defaults to false.</para> + <para>Takes one of <literal>ipv4</literal>, <literal>ipv6</literal>, + <literal>both</literal>, <literal>none</literal>. When <literal>ipv4</literal>, + performs IPv4 Duplicate Address Detection. See + <ulink url="https://tools.ietf.org/html/rfc5227">RFC 5224</ulink>. + When <literal>ipv6</literal>, performs IPv6 Duplicate Address Detection. See + <ulink url="https://tools.ietf.org/html/rfc4862">RFC 4862</ulink>. + Defaults to <literal>ipv6</literal>.</para> </listitem> </varlistentry> <varlistentry> diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 2810a6420e..23a970a13f 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -32,6 +32,7 @@ int address_new(Address **ret) { .scope = RT_SCOPE_UNIVERSE, .cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME, .cinfo.ifa_valid = CACHE_INFO_INFINITY_LIFE_TIME, + .duplicate_address_detection = ADDRESS_FAMILY_IPV6, }; *ret = TAKE_PTR(address); @@ -102,7 +103,7 @@ void address_free(Address *address) { hashmap_remove(address->network->addresses_by_section, address->section); } - if (address->link) { + if (address->link && !address->acd) { set_remove(address->link->addresses, address); set_remove(address->link->addresses_foreign, address); @@ -110,6 +111,8 @@ void address_free(Address *address) { memzero(&address->link->ipv6ll_address, sizeof(struct in6_addr)); } + sd_ipv4acd_unref(address->acd); + network_config_section_free(address->section); free(address->label); free(address); @@ -587,7 +590,7 @@ int address_configure( if (address->home_address) address->flags |= IFA_F_HOMEADDRESS; - if (address->duplicate_address_detection) + if (!FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV6)) address->flags |= IFA_F_NODAD; if (address->manage_temporary_address) @@ -658,9 +661,101 @@ int address_configure( return log_link_error_errno(link, r, "Could not add address: %m"); } + if (address->acd) { + assert(address->family == AF_INET); + if (DEBUG_LOGGING) { + _cleanup_free_ char *pretty = NULL; + + (void) in_addr_to_string(address->family, &address->in_addr, &pretty); + log_debug("Starting IPv4ACD client. Probing address %s", strna(pretty)); + } + + r = sd_ipv4acd_start(address->acd); + if (r < 0) + log_link_warning_errno(link, r, "Failed to start IPv4ACD client, ignoring: %m"); + } + return 1; } +static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { + _cleanup_free_ char *pretty = NULL; + Address *address; + Link *link; + int r; + + assert(acd); + assert(userdata); + + address = (Address *) userdata; + link = address->link; + + (void) in_addr_to_string(address->family, &address->in_addr, &pretty); + switch (event) { + case SD_IPV4ACD_EVENT_STOP: + log_link_debug(link, "Stopping ACD client..."); + return; + + case SD_IPV4ACD_EVENT_BIND: + log_link_debug(link, "Successfully claimed address %s", strna(pretty)); + link_check_ready(link); + break; + + case SD_IPV4ACD_EVENT_CONFLICT: + log_link_warning(link, "DAD conflict. Dropping address %s", strna(pretty)); + r = address_remove(address, link, NULL); + if (r < 0) + log_link_error_errno(link, r, "Failed to drop DAD conflicted address %s", strna(pretty));; + + link_check_ready(link); + break; + + default: + assert_not_reached("Invalid IPv4ACD event."); + } + + sd_ipv4acd_stop(acd); + + return; +} + +int configure_ipv4_duplicate_address_detection(Link *link, Address *address) { + int r; + + assert(link); + assert(address); + assert(address->family == AF_INET); + assert(!address->link && address->network); + + address->link = link; + + r = sd_ipv4acd_new(&address->acd); + if (r < 0) + return r; + + r = sd_ipv4acd_attach_event(address->acd, NULL, 0); + if (r < 0) + return r; + + r = sd_ipv4acd_set_ifindex(address->acd, link->ifindex); + if (r < 0) + return r; + + r = sd_ipv4acd_set_mac(address->acd, &link->mac); + if (r < 0) + return r; + + r = sd_ipv4acd_set_address(address->acd, &address->in_addr.in); + if (r < 0) + return r; + + r = sd_ipv4acd_set_callback(address->acd, static_address_on_acd, address); + if (r < 0) + return r; + + return 0; +} + int config_parse_broadcast( const char *unit, const char *filename, @@ -897,14 +992,12 @@ int config_parse_address_flags(const char *unit, r = parse_boolean(rvalue); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse address flag, ignoring: %s", rvalue); + "Failed to parse %s=, ignoring: %s", lvalue, rvalue); return 0; } if (streq(lvalue, "HomeAddress")) n->home_address = r; - else if (streq(lvalue, "DuplicateAddressDetection")) - n->duplicate_address_detection = r; else if (streq(lvalue, "ManageTemporaryAddress")) n->manage_temporary_address = r; else if (streq(lvalue, "PrefixRoute")) @@ -961,6 +1054,55 @@ int config_parse_address_scope(const char *unit, return 0; } +int config_parse_duplicate_address_detection( + 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) { + Network *network = userdata; + _cleanup_(address_free_or_set_invalidp) Address *n = NULL; + AddressFamily a; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = address_new_static(network, filename, section_line, &n); + if (r < 0) + return r; + + r = parse_boolean(rvalue); + if (r >= 0) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "For historical reasons, %s=%s means %s=%s. " + "Please use 'both', 'ipv4', 'ipv6' or 'none' instead.", + lvalue, rvalue, lvalue, r ? "none" : "both"); + n->duplicate_address_detection = r ? ADDRESS_FAMILY_NO : ADDRESS_FAMILY_YES; + n = NULL; + return 0; + } + + a = duplicate_address_detection_address_family_from_string(rvalue); + if (a < 0) { + log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), + "Failed to parse %s=, ignoring: %s", lvalue, rvalue); + return 0; + } + + n->duplicate_address_detection = a; + n = NULL; + return 0; +} + bool address_is_ready(const Address *a) { assert(a); diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 455985d225..76a30a54bc 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -13,6 +13,8 @@ typedef struct Address Address; #include "networkd-network.h" #include "networkd-util.h" +#include "sd-ipv4acd.h" + #define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU typedef struct Network Network; @@ -38,11 +40,13 @@ struct Address { union in_addr_union in_addr_peer; bool ip_masquerade_done:1; - bool duplicate_address_detection; - bool manage_temporary_address; - bool home_address; - bool prefix_route; - bool autojoin; + bool manage_temporary_address:1; + bool home_address:1; + bool prefix_route:1; + bool autojoin:1; + AddressFamily duplicate_address_detection; + + sd_ipv4acd *acd; LIST_FIELDS(Address, addresses); }; @@ -59,6 +63,7 @@ int address_remove(Address *address, Link *link, link_netlink_message_handler_t bool address_equal(Address *a1, Address *a2); bool address_is_ready(const Address *a); int address_section_verify(Address *a); +int configure_ipv4_duplicate_address_detection(Link *link, Address *address); DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free); @@ -68,3 +73,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_label); CONFIG_PARSER_PROTOTYPE(config_parse_lifetime); CONFIG_PARSER_PROTOTYPE(config_parse_address_flags); CONFIG_PARSER_PROTOTYPE(config_parse_address_scope); +CONFIG_PARSER_PROTOTYPE(config_parse_duplicate_address_detection); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 710de80fde..47761f2e15 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -774,6 +774,7 @@ static void link_enter_unmanaged(Link *link) { int link_stop_clients(Link *link, bool may_keep_dhcp) { int r = 0, k; + Address *ad; assert(link); assert(link->manager); @@ -798,6 +799,14 @@ int link_stop_clients(Link *link, bool may_keep_dhcp) { r = log_link_warning_errno(link, k, "Could not stop IPv4 link-local: %m"); } + if (link->network) + LIST_FOREACH(addresses, ad, link->network->static_addresses) + if (ad->acd && sd_ipv4acd_is_running(ad->acd) == 0) { + k = sd_ipv4acd_stop(ad->acd); + if (k < 0) + r = log_link_warning_errno(link, k, "Could not stop IPv4 ACD client: %m"); + } + if (link->dhcp6_client) { k = sd_dhcp6_client_stop(link->dhcp6_client); if (k < 0) @@ -2584,6 +2593,24 @@ static int link_drop_config(Link *link) { return 0; } +static int link_configure_ipv4_dad(Link *link) { + Address *address; + int r; + + assert(link); + assert(link->network); + + LIST_FOREACH(addresses, address, link->network->static_addresses) + if (address->family == AF_INET && + FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV4)) { + r = configure_ipv4_duplicate_address_detection(link, address); + if (r < 0) + return log_link_error_errno(link, r, "Failed to configure IPv4ACD: %m"); + } + + return 0; +} + static int link_configure_qdiscs(Link *link) { QDisc *qdisc; Iterator i; @@ -2732,6 +2759,10 @@ static int link_configure(Link *link) { if (r < 0) return r; + r = link_configure_ipv4_dad(link); + if (r < 0) + return r; + return link_configure_after_setting_mtu(link); } diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 1bfd76ec73..6f76f74ec9 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -105,10 +105,10 @@ Address.Broadcast, config_parse_broadcast, Address.Label, config_parse_label, 0, 0 Address.PreferredLifetime, config_parse_lifetime, 0, 0 Address.HomeAddress, config_parse_address_flags, 0, 0 -Address.DuplicateAddressDetection, config_parse_address_flags, 0, 0 Address.ManageTemporaryAddress, config_parse_address_flags, 0, 0 Address.PrefixRoute, config_parse_address_flags, 0, 0 Address.AutoJoin, config_parse_address_flags, 0, 0 +Address.DuplicateAddressDetection, config_parse_duplicate_address_detection, 0, 0 Address.Scope, config_parse_address_scope, 0, 0 IPv6AddressLabel.Prefix, config_parse_address_label_prefix, 0, 0 IPv6AddressLabel.Label, config_parse_address_label, 0, 0 diff --git a/src/network/networkd-util.c b/src/network/networkd-util.c index 3fd26836c6..deba995372 100644 --- a/src/network/networkd-util.c +++ b/src/network/networkd-util.c @@ -30,9 +30,17 @@ static const char * const routing_policy_rule_address_family_table[_ADDRESS_FAMI [ADDRESS_FAMILY_IPV6] = "ipv6", }; +static const char * const duplicate_address_detection_address_family_table[_ADDRESS_FAMILY_MAX] = { + [ADDRESS_FAMILY_NO] = "none", + [ADDRESS_FAMILY_YES] = "both", + [ADDRESS_FAMILY_IPV4] = "ipv4", + [ADDRESS_FAMILY_IPV6] = "ipv6", +}; + DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(address_family, AddressFamily, ADDRESS_FAMILY_YES); DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(link_local_address_family, AddressFamily, ADDRESS_FAMILY_YES); DEFINE_STRING_TABLE_LOOKUP(routing_policy_rule_address_family, AddressFamily); +DEFINE_STRING_TABLE_LOOKUP(duplicate_address_detection_address_family, AddressFamily); DEFINE_CONFIG_PARSE_ENUM(config_parse_link_local_address_family, link_local_address_family, AddressFamily, "Failed to parse option"); diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h index 3a57819f39..28dd9d3fe5 100644 --- a/src/network/networkd-util.h +++ b/src/network/networkd-util.h @@ -35,6 +35,9 @@ AddressFamily link_local_address_family_from_string(const char *s) _pure_; const char *routing_policy_rule_address_family_to_string(AddressFamily b) _const_; AddressFamily routing_policy_rule_address_family_from_string(const char *s) _pure_; +const char *duplicate_address_detection_address_family_to_string(AddressFamily b) _const_; +AddressFamily duplicate_address_detection_address_family_from_string(const char *s) _pure_; + int kernel_route_expiration_supported(void); int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s); diff --git a/test/test-network/conf/25-address-dad-veth-peer.network b/test/test-network/conf/25-address-dad-veth-peer.network new file mode 100644 index 0000000000..2827a751af --- /dev/null +++ b/test/test-network/conf/25-address-dad-veth-peer.network @@ -0,0 +1,9 @@ +[Match] +Name=veth-peer + +[Network] +IPv6AcceptRA=no + +[Address] +Address=192.168.100.10/24 +DuplicateAddressDetection=ipv4 diff --git a/test/test-network/conf/25-address-dad-veth99.network b/test/test-network/conf/25-address-dad-veth99.network new file mode 100644 index 0000000000..8e323d8395 --- /dev/null +++ b/test/test-network/conf/25-address-dad-veth99.network @@ -0,0 +1,8 @@ +[Match] +Name=veth99 + +[Network] +IPv6AcceptRA=no + +[Address] +Address=192.168.100.10/24 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index f47463956e..cab8fb6afc 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -1479,6 +1479,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): '23-active-slave.network', '24-keep-configuration-static.network', '24-search-domain.network', + '25-address-dad-veth-peer.network', + '25-address-dad-veth99.network', '25-address-link-section.network', '25-address-preferred-lifetime-zero.network', '25-address-static.network', @@ -1581,6 +1583,20 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'default via 20.20.20.1 proto static') + def test_address_dad(self): + copy_unit_to_networkd_unit_path('25-address-dad-veth99.network', '25-address-dad-veth-peer.network', + '25-veth.netdev') + start_networkd() + self.wait_online(['veth99:routable', 'veth-peer:degraded']) + + output = check_output('ip -4 address show dev veth99') + print(output) + self.assertRegex(output, '192.168.100.10/24') + + output = check_output('ip -4 address show dev veth-peer') + print(output) + self.assertNotRegex(output, '192.168.100.10/24') + def test_configure_without_carrier(self): copy_unit_to_networkd_unit_path('configure-without-carrier.network', '11-dummy.netdev') start_networkd() |