1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <linux/if_arp.h>
5 #include "sd-dhcp-client.h"
6 #include "sd-ipv4acd.h"
9 #include "networkd-address.h"
10 #include "networkd-dhcp4.h"
11 #include "networkd-ipv4acd.h"
12 #include "networkd-link.h"
13 #include "networkd-manager.h"
15 bool link_ipv4acd_supported(Link
*link
) {
18 if (link
->flags
& IFF_LOOPBACK
)
21 /* ARPHRD_INFINIBAND seems to potentially support IPv4ACD.
22 * But currently sd-ipv4acd only supports ARPHRD_ETHER. */
23 if (link
->iftype
!= ARPHRD_ETHER
)
26 if (link
->hw_addr
.length
!= ETH_ALEN
)
29 if (ether_addr_is_null(&link
->hw_addr
.ether
))
32 if (streq_ptr(link
->kind
, "vrf"))
35 /* L3 or L3S mode do not support ARP. */
36 if (IN_SET(link_get_ipvlan_mode(link
), NETDEV_IPVLAN_MODE_L3
, NETDEV_IPVLAN_MODE_L3S
))
42 static bool address_ipv4acd_enabled(Address
*address
) {
44 assert(address
->link
);
46 if (address
->family
!= AF_INET
)
49 if (!FLAGS_SET(address
->duplicate_address_detection
, ADDRESS_FAMILY_IPV4
))
52 /* Currently, only static and DHCP4 addresses are supported. */
53 if (!IN_SET(address
->source
, NETWORK_CONFIG_SOURCE_STATIC
, NETWORK_CONFIG_SOURCE_DHCP4
))
56 if (!link_ipv4acd_supported(address
->link
))
62 bool ipv4acd_bound(const Address
*address
) {
68 return address
->acd_bound
;
71 static int static_ipv4acd_address_remove(Link
*link
, Address
*address
, bool on_conflict
) {
77 if (!address_exists(address
))
78 return 0; /* Not assigned. */
81 log_link_warning(link
, "Dropping address "IPV4_ADDRESS_FMT_STR
", as an address conflict was detected.",
82 IPV4_ADDRESS_FMT_VAL(address
->in_addr
.in
));
84 log_link_debug(link
, "Removing address "IPV4_ADDRESS_FMT_STR
", as the ACD client is stopped.",
85 IPV4_ADDRESS_FMT_VAL(address
->in_addr
.in
));
87 r
= address_remove(address
);
89 return log_link_warning_errno(link
, r
, "Failed to remove address "IPV4_ADDRESS_FMT_STR
": %m",
90 IPV4_ADDRESS_FMT_VAL(address
->in_addr
.in
));
95 static int dhcp4_address_on_conflict(Link
*link
, Address
*address
) {
99 assert(link
->dhcp_client
);
101 r
= sd_dhcp_client_send_decline(link
->dhcp_client
);
103 log_link_warning_errno(link
, r
, "Failed to send DHCP DECLINE, ignoring: %m");
105 if (!link
->dhcp_lease
)
106 /* Unlikely, but during probing the address, the lease may be lost. */
109 log_link_warning(link
, "Dropping DHCPv4 lease, as an address conflict was detected.");
110 r
= dhcp4_lease_lost(link
);
112 return log_link_warning_errno(link
, r
, "Failed to drop DHCPv4 lease: %m");
114 /* It is not necessary to call address_remove() here, as dhcp4_lease_lost() removes it. */
118 static void on_acd(sd_ipv4acd
*acd
, int event
, void *userdata
) {
119 Address
*address
= userdata
;
125 assert(address
->acd
== acd
);
126 assert(address
->link
);
127 assert(address
->family
== AF_INET
);
128 assert(IN_SET(address
->source
, NETWORK_CONFIG_SOURCE_STATIC
, NETWORK_CONFIG_SOURCE_DHCP4
));
130 link
= address
->link
;
133 case SD_IPV4ACD_EVENT_STOP
:
134 address
->acd_bound
= false;
136 if (address
->source
== NETWORK_CONFIG_SOURCE_STATIC
) {
137 r
= static_ipv4acd_address_remove(link
, address
, /* on_conflict = */ false);
139 link_enter_failed(link
);
142 /* We have nothing to do for DHCPv4 lease here, as the dhcp client is already stopped
143 * when stopping the ipv4acd client. See link_stop_engines(). */
146 case SD_IPV4ACD_EVENT_BIND
:
147 address
->acd_bound
= true;
149 log_link_debug(link
, "Successfully claimed address "IPV4_ADDRESS_FMT_STR
,
150 IPV4_ADDRESS_FMT_VAL(address
->in_addr
.in
));
153 case SD_IPV4ACD_EVENT_CONFLICT
:
154 address
->acd_bound
= false;
156 log_link_warning(link
, "Dropping address "IPV4_ADDRESS_FMT_STR
", as an address conflict was detected.",
157 IPV4_ADDRESS_FMT_VAL(address
->in_addr
.in
));
159 if (address
->source
== NETWORK_CONFIG_SOURCE_STATIC
)
160 r
= static_ipv4acd_address_remove(link
, address
, /* on_conflict = */ true);
162 r
= dhcp4_address_on_conflict(link
, address
);
164 link_enter_failed(link
);
168 assert_not_reached();
172 static int ipv4acd_check_mac(sd_ipv4acd
*acd
, const struct ether_addr
*mac
, void *userdata
) {
173 Manager
*m
= userdata
;
174 struct hw_addr_data hw_addr
;
179 hw_addr
= (struct hw_addr_data
) {
184 return link_get_by_hw_addr(m
, &hw_addr
, NULL
) >= 0;
187 static int address_ipv4acd_start(Address
*address
) {
189 assert(address
->link
);
194 if (sd_ipv4acd_is_running(address
->acd
))
197 if (!link_has_carrier(address
->link
))
200 return sd_ipv4acd_start(address
->acd
, true);
203 int ipv4acd_configure(Address
*address
) {
209 link
= ASSERT_PTR(address
->link
);
211 if (!address_ipv4acd_enabled(address
)) {
212 address
->acd
= sd_ipv4acd_unref(address
->acd
);
213 address
->acd_bound
= false;
218 return address_ipv4acd_start(address
);
220 log_link_debug(link
, "Configuring IPv4ACD for address "IPV4_ADDRESS_FMT_STR
,
221 IPV4_ADDRESS_FMT_VAL(address
->in_addr
.in
));
223 r
= sd_ipv4acd_new(&address
->acd
);
227 r
= sd_ipv4acd_attach_event(address
->acd
, link
->manager
->event
, 0);
231 r
= sd_ipv4acd_set_ifindex(address
->acd
, link
->ifindex
);
235 r
= sd_ipv4acd_set_mac(address
->acd
, &link
->hw_addr
.ether
);
239 r
= sd_ipv4acd_set_address(address
->acd
, &address
->in_addr
.in
);
243 r
= sd_ipv4acd_set_callback(address
->acd
, on_acd
, address
);
247 r
= sd_ipv4acd_set_check_mac_callback(address
->acd
, ipv4acd_check_mac
, link
->manager
);
251 return address_ipv4acd_start(address
);
254 int ipv4acd_update_mac(Link
*link
) {
260 if (link
->hw_addr
.length
!= ETH_ALEN
)
262 if (ether_addr_is_null(&link
->hw_addr
.ether
))
265 SET_FOREACH(address
, link
->addresses
) {
269 k
= sd_ipv4acd_set_mac(address
->acd
, &link
->hw_addr
.ether
);
274 link_enter_failed(link
);
279 int ipv4acd_start(Link
*link
) {
285 SET_FOREACH(address
, link
->addresses
) {
286 r
= address_ipv4acd_start(address
);
294 int ipv4acd_stop(Link
*link
) {
300 SET_FOREACH(address
, link
->addresses
) {
304 k
= sd_ipv4acd_stop(address
->acd
);
312 int ipv4acd_set_ifname(Link
*link
) {
318 SET_FOREACH(address
, link
->addresses
) {
322 r
= sd_ipv4acd_set_ifname(address
->acd
, link
->ifname
);