Compare commits

...

3 Commits

Author SHA1 Message Date
Yu Watanabe 43a20059a5
Merge pull request #14102 from ssahani/acd-duplicate-ip
network: introduce DAD for static IPV4 address
2019-12-07 14:11:44 +09:00
Yu Watanabe dc7d3c5fd4 test-network: add test case for IPv4 DAD 2019-12-06 23:29:28 +09:00
Susant Sahani 051e77cac1 network: introduce DAD for static address
Closes #2527.
2019-12-06 23:29:07 +09:00
10 changed files with 241 additions and 14 deletions

View File

@ -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>

View 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);

View 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);

View 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);
}

View 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

View 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");

View 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);

View File

@ -0,0 +1,9 @@
[Match]
Name=veth-peer
[Network]
IPv6AcceptRA=no
[Address]
Address=192.168.100.10/24
DuplicateAddressDetection=ipv4

View File

@ -0,0 +1,8 @@
[Match]
Name=veth99
[Network]
IPv6AcceptRA=no
[Address]
Address=192.168.100.10/24

View File

@ -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()