]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: introduce DAD for static address
authorSusant Sahani <ssahani@vmware.com>
Thu, 21 Nov 2019 15:54:52 +0000 (16:54 +0100)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 6 Dec 2019 14:29:07 +0000 (23:29 +0900)
Closes #2527.

man/systemd.network.xml
src/network/networkd-address.c
src/network/networkd-address.h
src/network/networkd-link.c
src/network/networkd-network-gperf.gperf
src/network/networkd-util.c
src/network/networkd-util.h

index a2ac24059a9dcc5eb9faf94c27be0735971de769..63dfb517b6a9fabd140b63447bbfb88551381dee 100644 (file)
         <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>
index 2810a6420e3f366aec6448a41f764c4dc2329e2c..23a970a13f629cf2be291fe10630d1a5a2b39e09 100644 (file)
@@ -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);
 
index 455985d225263e8063bc60c26079881ebbc5bf03..76a30a54bcde39f467e264dc0c6b795e03bea764 100644 (file)
@@ -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);
index 710de80fdef19ded57e24a52fa75624965fa17b8..47761f2e15e3a0a102a47236b1f24e0bb1f06a21 100644 (file)
@@ -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);
 }
 
index 1bfd76ec735d4918fdc5e816e881007f4111cd06..6f76f74ec9b5053bdc46281853e72498392efc1c 100644 (file)
@@ -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
index 3fd26836c6ac9841ef3078410a995a78620d8976..deba9953726afbfafd33dc2afb60f95154ead026 100644 (file)
@@ -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");
 
index 3a57819f398bb86d6f2f68485a389700cba445ed..28dd9d3fe564720a3761ebc17ac54becfcc85dd6 100644 (file)
@@ -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);