diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 40aa1b449b..d77ce61160 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -189,13 +189,14 @@
ARP=
- Takes a boolean. If set to true, the IPv4 ARP (low-level Address Resolution Protocol)
- and IPv6 NDP (Neighbor Discovery Protocol) for this interface are enabled. When unset, the
- kernel's default will be used.
- For example, disabling ARP is useful when creating multiple MACVLAN or VLAN virtual
- interfaces atop a single lower-level physical interface, which will then only serve as a
- link/"bridge" device aggregating traffic to the same physical link and not participate in
- the network otherwise. Defaults to unset.
+ Takes a boolean. If set to true, the IPv4 ARP (low-level Address Resolution Protocol) and
+ IPv6 NDP (Neighbor Discovery Protocol) for this interface are enabled. For example, disabling ARP
+ is useful when creating multiple MACVLAN or VLAN virtual interfaces atop a single lower-level
+ physical interface, which will then only serve as a link/"bridge" device aggregating traffic to the
+ same physical link and not participate in the network otherwise. Defaults to unset, and enabled
+ when the IPv4 link-local addressing is enabled in LinkLocalAddressing= or an
+ IPv4 address with DuplicateAddressDetection= enabled is requested. Otherwise,
+ the kernel's default will be used.
@@ -459,6 +460,9 @@
MACVLAN=/MACVTAP= has Mode=passthru,
or otherwise.
+ When IPv4 link-local addressing is enabled, ARP= is enabled unless if it
+ is explicitly configured.
+
@@ -1439,6 +1443,9 @@ DuplicateAddressDetection=none
ipv4 for IPv4 link-local addresses (169.254.0.0/16), ipv6
for IPv6 addresses, and none otherwise.
+ When enabled, regradless implicitly or not, ARP= is enabled unless it is
+ explicitly configured.
+
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 85493334f5..d793d60362 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -16,7 +16,7 @@
#include "networkd-dhcp-prefix-delegation.h"
#include "networkd-dhcp4-bus.h"
#include "networkd-dhcp4.h"
-#include "networkd-ipv4acd.h"
+#include "networkd-ipv4ll.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-network.h"
@@ -1192,13 +1192,11 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
return 0;
}
- if (link->ipv4ll && !sd_ipv4ll_is_running(link->ipv4ll)) {
- log_link_debug(link, "DHCP client is stopped. Acquiring IPv4 link-local address");
-
- r = sd_ipv4ll_start(link->ipv4ll);
- if (r < 0 && r != -ESTALE) /* On exit, we cannot and should not start sd-ipv4ll. */
- return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
- }
+ r = ipv4ll_start(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
+ if (r > 0)
+ log_link_debug(link, "DHCP client is stopped. Acquiring IPv4 link-local address.");
if (link->dhcp_lease) {
if (link->network->dhcp_send_release) {
@@ -1270,13 +1268,11 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
break;
case SD_DHCP_CLIENT_EVENT_TRANSIENT_FAILURE:
- if (link->ipv4ll && !sd_ipv4ll_is_running(link->ipv4ll)) {
+ r = ipv4ll_start(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
+ if (r > 0)
log_link_debug(link, "Problems acquiring DHCP lease, acquiring IPv4 link-local address");
-
- r = sd_ipv4ll_start(link->ipv4ll);
- if (r < 0)
- return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
- }
break;
default:
diff --git a/src/network/networkd-ipv4acd.c b/src/network/networkd-ipv4acd.c
index 4785b96f9f..0ecdee0893 100644
--- a/src/network/networkd-ipv4acd.c
+++ b/src/network/networkd-ipv4acd.c
@@ -66,6 +66,18 @@ static bool address_ipv4acd_enabled(Link *link, const Address *address) {
return link_ipv4acd_supported(link);
}
+bool link_ipv4acd_enabled(Link *link) {
+ assert(link);
+ assert(link->network);
+
+ Address *address;
+ ORDERED_HASHMAP_FOREACH(address, link->network->addresses_by_section)
+ if (address_ipv4acd_enabled(link, address))
+ return true;
+
+ return false;
+}
+
bool ipv4acd_bound(Link *link, const Address *address) {
sd_ipv4acd *acd;
diff --git a/src/network/networkd-ipv4acd.h b/src/network/networkd-ipv4acd.h
index 54da435679..fc9f92d4f4 100644
--- a/src/network/networkd-ipv4acd.h
+++ b/src/network/networkd-ipv4acd.h
@@ -5,6 +5,7 @@ typedef struct Address Address;
typedef struct Link Link;
bool link_ipv4acd_supported(Link *link);
+bool link_ipv4acd_enabled(Link *link);
bool ipv4acd_bound(Link *link, const Address *address);
int ipv4acd_configure(Link *link, const Address *address);
void ipv4acd_detach(Link *link, const Address *address);
diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c
index ef8b0876b5..7522bca0aa 100644
--- a/src/network/networkd-ipv4ll.c
+++ b/src/network/networkd-ipv4ll.c
@@ -264,6 +264,34 @@ int ipv4ll_configure(Link *link) {
return sd_ipv4ll_set_check_mac_callback(link->ipv4ll, ipv4ll_check_mac, link->manager);
}
+int ipv4ll_start(Link *link) {
+ int r;
+
+ assert(link);
+
+ if (!link->ipv4ll)
+ return 0;
+
+ if (sd_ipv4ll_is_running(link->ipv4ll))
+ return 0;
+
+ if (!link_has_carrier(link))
+ return 0;
+
+ /* On exit, we cannot and should not start sd-ipv4ll. */
+ r = sd_event_get_state(link->manager->event);
+ if (r < 0)
+ return r;
+ if (r == SD_EVENT_FINISHED)
+ return 0;
+
+ r = sd_ipv4ll_start(link->ipv4ll);
+ if (r < 0)
+ return r;
+
+ return 1; /* started */
+}
+
int link_drop_ipv4ll_config(Link *link, Network *network) {
int ret = 0;
diff --git a/src/network/networkd-ipv4ll.h b/src/network/networkd-ipv4ll.h
index f380afe9ec..71cddbdd5b 100644
--- a/src/network/networkd-ipv4ll.h
+++ b/src/network/networkd-ipv4ll.h
@@ -11,6 +11,7 @@ typedef struct Network Network;
bool link_ipv4ll_enabled(Link *link);
int ipv4ll_configure(Link *link);
+int ipv4ll_start(Link *link);
int link_drop_ipv4ll_config(Link *link, Network *network);
int ipv4ll_update_mac(Link *link);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 9cc19fb4d3..b13b668c1a 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -728,11 +728,11 @@ static int link_acquire_dynamic_ipv4_conf(Link *link) {
return log_link_warning_errno(link, r, "Could not set IPv4 link-local start address: %m");
}
- r = sd_ipv4ll_start(link->ipv4ll);
+ r = ipv4ll_start(link);
if (r < 0)
return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
-
- log_link_debug(link, "Acquiring IPv4 link-local address.");
+ if (r > 0)
+ log_link_debug(link, "Acquiring IPv4 link-local address.");
}
r = link_start_dhcp4_server(link);
@@ -1747,6 +1747,8 @@ static int link_carrier_gained(Link *link) {
assert(link);
+ log_link_info(link, "Gained carrier");
+
r = event_source_disable(link->carrier_lost_timer);
if (r < 0)
log_link_warning_errno(link, r, "Failed to disable carrier lost timer, ignoring: %m");
@@ -1849,6 +1851,8 @@ static int link_carrier_lost(Link *link) {
assert(link);
+ log_link_info(link, "Lost carrier");
+
if (link->iftype == ARPHRD_CAN)
/* let's shortcut things for CAN which doesn't need most of what's done below. */
usec = 0;
@@ -1902,6 +1906,8 @@ static int link_admin_state_up(Link *link) {
/* This is called every time an interface admin state changes to up;
* specifically, when IFF_UP flag changes from unset to set. */
+ log_link_info(link, "Link UP");
+
if (!link->network)
return 0;
@@ -1920,6 +1926,8 @@ static int link_admin_state_up(Link *link) {
static int link_admin_state_down(Link *link) {
assert(link);
+ log_link_info(link, "Link DOWN");
+
link_forget_nexthops(link);
link_forget_routes(link);
@@ -2167,33 +2175,21 @@ static int link_update_flags(Link *link, sd_netlink_message *message) {
link_update_operstate(link, true);
- if (!link_was_admin_up && (link->flags & IFF_UP)) {
- log_link_info(link, "Link UP");
+ r = 0;
+ if (!link_was_admin_up && (link->flags & IFF_UP))
r = link_admin_state_up(link);
- if (r < 0)
- return r;
- } else if (link_was_admin_up && !(link->flags & IFF_UP)) {
- log_link_info(link, "Link DOWN");
-
+ else if (link_was_admin_up && !(link->flags & IFF_UP))
r = link_admin_state_down(link);
- if (r < 0)
- return r;
- }
-
- if (!had_carrier && link_has_carrier(link)) {
- log_link_info(link, "Gained carrier");
+ if (r < 0)
+ return r;
+ if (!had_carrier && link_has_carrier(link))
r = link_carrier_gained(link);
- if (r < 0)
- return r;
- } else if (had_carrier && !link_has_carrier(link)) {
- log_link_info(link, "Lost carrier");
-
- r = link_carrier_lost(link);
- if (r < 0)
- return r;
- }
+ else if (had_carrier && !link_has_carrier(link))
+ link_carrier_lost(link);
+ if (r < 0)
+ return r;
return 0;
}
diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c
index c0c1da1b16..d4606533a7 100644
--- a/src/network/networkd-setlink.c
+++ b/src/network/networkd-setlink.c
@@ -12,6 +12,8 @@
#include "netlink-util.h"
#include "networkd-address.h"
#include "networkd-can.h"
+#include "networkd-ipv4acd.h"
+#include "networkd-ipv4ll.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-queue.h"
@@ -178,6 +180,30 @@ static int link_set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Request
return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, get_link_default_handler);
}
+static int link_get_arp(Link *link) {
+ assert(link);
+
+ /* This returns tristate. */
+
+ if (!link->network)
+ return -1;
+
+ /* If ARP= is explicitly specified, use the setting. */
+ if (link->network->arp >= 0)
+ return link->network->arp;
+
+ /* Enable ARP when IPv4ACD is enabled. */
+ if (link_ipv4acd_enabled(link))
+ return true;
+
+ /* Similary, enable ARP when IPv4LL is enabled. */
+ if (link_ipv4ll_enabled(link))
+ return true;
+
+ /* Otherwise, do not change the flag. */
+ return -1;
+}
+
static int link_configure_fill_message(
Link *link,
sd_netlink_message *req,
@@ -360,9 +386,11 @@ static int link_configure_fill_message(
case REQUEST_TYPE_SET_LINK_FLAGS: {
unsigned ifi_change = 0, ifi_flags = 0;
- if (link->network->arp >= 0) {
+ int arp = link_get_arp(link);
+
+ if (arp >= 0) {
ifi_change |= IFF_NOARP;
- SET_FLAG(ifi_flags, IFF_NOARP, link->network->arp == 0);
+ SET_FLAG(ifi_flags, IFF_NOARP, arp == 0);
}
if (link->network->multicast >= 0) {
@@ -844,7 +872,7 @@ int link_request_to_set_flags(Link *link) {
assert(link);
assert(link->network);
- if (link->network->arp < 0 &&
+ if (link_get_arp(link) < 0 &&
link->network->multicast < 0 &&
link->network->allmulticast < 0 &&
link->network->promiscuous < 0)