Skip to content

Commit

Permalink
network: ipv4acd: first probe address and then assign it
Browse files Browse the repository at this point in the history
Previously, if IPv4 ACD is enabled on an address, then we first
assign the address, and start sd-ipv4acd daemon for the address.
This is not only RFC incompliant, but also the address is always
dropped, as the daemon always considers the address is conflicted.

This commit makes networkd first starts sd-ipv4acd daemon to probe
the address, and then the address is configured if no conflict is
detected.

Fixes systemd#17235.
  • Loading branch information
yuwata committed Jun 30, 2021
1 parent 475ec33 commit 76a86ff
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 342 deletions.
2 changes: 2 additions & 0 deletions src/network/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ sources = files('''
networkd-dhcp4.h
networkd-dhcp6.c
networkd-dhcp6.h
networkd-ipv4acd.c
networkd-ipv4acd.h
networkd-ipv4ll.c
networkd-ipv4ll.h
networkd-ipv6-proxy-ndp.c
Expand Down
168 changes: 9 additions & 159 deletions src/network/networkd-address.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "netlink-util.h"
#include "networkd-address-pool.h"
#include "networkd-address.h"
#include "networkd-ipv4acd.h"
#include "networkd-manager.h"
#include "networkd-network.h"
#include "networkd-queue.h"
Expand Down Expand Up @@ -129,6 +130,7 @@ static int address_new_static(Network *network, const char *filename, unsigned s

address->network = network;
address->section = TAKE_PTR(n);
address->is_static = true;

r = ordered_hashmap_ensure_put(&network->addresses_by_section, &network_config_hash_ops, address->section, address);
if (r < 0)
Expand All @@ -152,6 +154,7 @@ Address *address_free(Address *address) {

set_remove(address->link->addresses, address);
set_remove(address->link->addresses_foreign, address);
set_remove(address->link->addresses_ipv4acd, address);
set_remove(address->link->static_addresses, address);
if (address->link->dhcp_address == address)
address->link->dhcp_address = NULL;
Expand Down Expand Up @@ -994,8 +997,6 @@ int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m,
return 1;
}

static int ipv4_dad_configure(Address *address);

static int address_configure(
const Address *address,
Link *link,
Expand Down Expand Up @@ -1242,6 +1243,12 @@ static int address_is_ready_to_configure(Link *link, const Address *address) {
return log_link_warning_errno(link, SYNTHETIC_ERRNO(E2BIG),
"Too many addresses are configured, refusing: %m");

if (address->family == AF_INET &&
address->duplicate_address_detection & ADDRESS_FAMILY_IPV4 &&
link->hw_addr.length == ETH_ALEN &&
!ether_addr_is_null(&link->hw_addr.ether))
return ipv4acd_address_is_ready_to_configure(link, address);

r = address_add(link, address, NULL);
if (r < 0)
return log_link_warning_errno(link, r, "Could not add address: %m");;
Expand Down Expand Up @@ -1286,12 +1293,6 @@ int request_process_address(Request *req) {
if (r < 0)
log_link_warning_errno(link, r, "Could not enable IP masquerading, ignoring: %m");

if (FLAGS_SET(a->duplicate_address_detection, ADDRESS_FAMILY_IPV4)) {
r = ipv4_dad_configure(a);
if (r < 0)
log_link_warning_errno(link, r, "Failed to start IPv4ACD client, ignoring: %m");
}

return 1;
}

Expand Down Expand Up @@ -1475,157 +1476,6 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
return 1;
}

static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
Address *address;
Link *link;
int r;

assert(acd);
assert(userdata);

address = (Address *) userdata;
link = address->link;

assert(address->family == AF_INET);

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 "IPV4_ADDRESS_FMT_STR,
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
link_check_ready(link);
break;

case SD_IPV4ACD_EVENT_CONFLICT:
log_link_warning(link, "DAD conflict. Dropping address "IPV4_ADDRESS_FMT_STR,
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
r = address_remove(address, link);
if (r < 0)
log_link_error_errno(link, r, "Failed to drop DAD conflicted address "IPV4_ADDRESS_FMT_STR,
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));

link_check_ready(link);
break;

default:
assert_not_reached("Invalid IPv4ACD event.");
}

(void) sd_ipv4acd_stop(acd);

return;
}

static int ipv4_dad_configure(Address *address) {
int r;

assert(address);
assert(address->link);

if (address->family != AF_INET)
return 0;

log_address_debug(address, "Starting IPv4ACD client. Probing", address->link);

if (!address->acd) {
r = sd_ipv4acd_new(&address->acd);
if (r < 0)
return r;

r = sd_ipv4acd_attach_event(address->acd, address->link->manager->event, 0);
if (r < 0)
return r;
}

r = sd_ipv4acd_set_ifindex(address->acd, address->link->ifindex);
if (r < 0)
return r;

r = sd_ipv4acd_set_mac(address->acd, &address->link->hw_addr.ether);
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 sd_ipv4acd_start(address->acd, true);
}

static int ipv4_dad_update_mac_one(Address *address) {
bool running;
int r;

assert(address);

if (!address->acd)
return 0;

running = sd_ipv4acd_is_running(address->acd);

r = sd_ipv4acd_stop(address->acd);
if (r < 0)
return r;

r = sd_ipv4acd_set_mac(address->acd, &address->link->hw_addr.ether);
if (r < 0)
return r;

if (running) {
r = sd_ipv4acd_start(address->acd, true);
if (r < 0)
return r;
}

return 0;
}

int ipv4_dad_update_mac(Link *link) {
Address *address;
int k, r = 0;

assert(link);

SET_FOREACH(address, link->addresses) {
k = ipv4_dad_update_mac_one(address);
if (k < 0 && r >= 0)
r = k;
}

return r;
}

int ipv4_dad_stop(Link *link) {
Address *address;
int k, r = 0;

assert(link);

SET_FOREACH(address, link->addresses) {
k = sd_ipv4acd_stop(address->acd);
if (k < 0 && r >= 0)
r = k;
}

return r;
}

void ipv4_dad_unref(Link *link) {
Address *address;

assert(link);

SET_FOREACH(address, link->addresses)
address->acd = sd_ipv4acd_unref(address->acd);
}

int config_parse_broadcast(
const char *unit,
const char *filename,
Expand Down
9 changes: 3 additions & 6 deletions src/network/networkd-address.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ typedef struct Address {

bool scope_set:1;
bool ip_masquerade_done:1;
bool is_static:1; /* currently only used by IPv4ACD */
bool acd_announced:1;
AddressFamily duplicate_address_detection;
sd_ipv4acd *acd;

/* Called when address become ready */
address_ready_callback_t callback;

sd_ipv4acd *acd;
} Address;

int address_new(Address **ret);
Expand All @@ -71,10 +72,6 @@ int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **
int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret);
int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready);

void ipv4_dad_unref(Link *link);
int ipv4_dad_stop(Link *link);
int ipv4_dad_update_mac(Link *link);

int link_request_address(
Link *link,
Address *address,
Expand Down
Loading

0 comments on commit 76a86ff

Please sign in to comment.