1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <net/if.h> /* IFF_LOOPBACK */
4 #include <net/if_arp.h> /* ARPHRD_ETHER */
6 #include "sd-dhcp-client.h"
7 #include "sd-ipv4acd.h"
10 #include "networkd-address.h"
11 #include "networkd-dhcp4.h"
12 #include "networkd-ipv4acd.h"
13 #include "networkd-link.h"
14 #include "networkd-manager.h"
16 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
18 void, trivial_hash_func
, trivial_compare_func
,
19 sd_ipv4acd
, sd_ipv4acd_unref
);
21 bool link_ipv4acd_supported(Link
*link
) {
24 if (link
->flags
& IFF_LOOPBACK
)
27 /* ARPHRD_INFINIBAND seems to potentially support IPv4ACD.
28 * But currently sd-ipv4acd only supports ARPHRD_ETHER. */
29 if (link
->iftype
!= ARPHRD_ETHER
)
32 if (link
->hw_addr
.length
!= ETH_ALEN
)
35 if (ether_addr_is_null(&link
->hw_addr
.ether
))
38 if (streq_ptr(link
->kind
, "vrf"))
41 /* L3 or L3S mode do not support ARP. */
42 if (IN_SET(link_get_ipvlan_mode(link
), NETDEV_IPVLAN_MODE_L3
, NETDEV_IPVLAN_MODE_L3S
))
48 static bool address_ipv4acd_enabled(Link
*link
, const Address
*address
) {
52 if (address
->family
!= AF_INET
)
55 if (!FLAGS_SET(address
->duplicate_address_detection
, ADDRESS_FAMILY_IPV4
))
58 /* Currently, only static and DHCP4 addresses are supported. */
59 if (!IN_SET(address
->source
, NETWORK_CONFIG_SOURCE_STATIC
, NETWORK_CONFIG_SOURCE_DHCP4
))
62 return link_ipv4acd_supported(link
);
65 bool ipv4acd_bound(Link
*link
, const Address
*address
) {
71 if (address
->family
!= AF_INET
)
74 acd
= hashmap_get(link
->ipv4acd_by_address
, IN4_ADDR_TO_PTR(&address
->in_addr
.in
));
78 return sd_ipv4acd_is_bound(acd
) > 0;
81 static int static_ipv4acd_address_remove(Link
*link
, Address
*address
, bool on_conflict
) {
87 if (!address_exists(address
))
88 return 0; /* Not assigned. */
91 log_link_warning(link
, "Dropping address %s, as an address conflict was detected.", IN4_ADDR_TO_STRING(&address
->in_addr
.in
));
93 log_link_debug(link
, "Removing address %s, as the ACD client is stopped.", IN4_ADDR_TO_STRING(&address
->in_addr
.in
));
95 /* Do not call address_remove_and_cancel() here. Otherwise, the request is cancelled, and the
96 * interface may be in configured state without the address. */
97 r
= address_remove(address
, link
);
99 return log_link_warning_errno(link
, r
, "Failed to remove address %s: %m", IN4_ADDR_TO_STRING(&address
->in_addr
.in
));
104 static int dhcp4_address_on_conflict(Link
*link
) {
108 assert(link
->dhcp_client
);
110 r
= sd_dhcp_client_send_decline(link
->dhcp_client
);
112 log_link_warning_errno(link
, r
, "Failed to send DHCP DECLINE, ignoring: %m");
114 if (!link
->dhcp_lease
)
115 /* Unlikely, but during probing the address, the lease may be lost. */
118 log_link_warning(link
, "Dropping DHCPv4 lease, as an address conflict was detected.");
119 r
= dhcp4_lease_lost(link
);
121 return log_link_warning_errno(link
, r
, "Failed to drop DHCPv4 lease: %m");
123 /* It is not necessary to call address_remove() here, as dhcp4_lease_lost() removes it. */
127 static void on_acd(sd_ipv4acd
*acd
, int event
, void *userdata
) {
128 Link
*link
= ASSERT_PTR(userdata
);
129 Address
*address
= NULL
;
135 r
= sd_ipv4acd_get_address(acd
, &a
);
137 log_link_warning_errno(link
, r
, "Failed to get address from IPv4ACD: %m");
138 link_enter_failed(link
);
141 (void) link_get_ipv4_address(link
, &a
, 0, &address
);
144 case SD_IPV4ACD_EVENT_STOP
:
148 if (address
->source
== NETWORK_CONFIG_SOURCE_STATIC
) {
149 r
= static_ipv4acd_address_remove(link
, address
, /* on_conflict = */ false);
151 link_enter_failed(link
);
154 /* We have nothing to do for DHCPv4 lease here, as the dhcp client is already stopped
155 * when stopping the ipv4acd client. See link_stop_engines(). */
158 case SD_IPV4ACD_EVENT_BIND
:
159 log_link_debug(link
, "Successfully claimed address %s", IN4_ADDR_TO_STRING(&a
));
162 case SD_IPV4ACD_EVENT_CONFLICT
:
166 log_link_warning(link
, "Dropping address %s, as an address conflict was detected.", IN4_ADDR_TO_STRING(&a
));
168 if (address
->source
== NETWORK_CONFIG_SOURCE_STATIC
)
169 r
= static_ipv4acd_address_remove(link
, address
, /* on_conflict = */ true);
171 r
= dhcp4_address_on_conflict(link
);
173 link_enter_failed(link
);
177 assert_not_reached();
181 static int ipv4acd_check_mac(sd_ipv4acd
*acd
, const struct ether_addr
*mac
, void *userdata
) {
182 Manager
*m
= ASSERT_PTR(userdata
);
183 struct hw_addr_data hw_addr
;
187 hw_addr
= (struct hw_addr_data
) {
192 return link_get_by_hw_addr(m
, &hw_addr
, NULL
) >= 0;
195 static int ipv4acd_start_one(Link
*link
, sd_ipv4acd
*acd
) {
199 if (sd_ipv4acd_is_running(acd
))
202 if (!link_has_carrier(link
))
205 return sd_ipv4acd_start(acd
, /* reset_conflicts = */ true);
208 int ipv4acd_configure(Link
*link
, const Address
*address
) {
209 _cleanup_(sd_ipv4acd_unrefp
) sd_ipv4acd
*acd
= NULL
;
210 sd_ipv4acd
*existing
;
214 assert(link
->manager
);
217 if (address
->family
!= AF_INET
)
220 existing
= hashmap_get(link
->ipv4acd_by_address
, IN4_ADDR_TO_PTR(&address
->in_addr
.in
));
222 if (!address_ipv4acd_enabled(link
, address
))
223 return sd_ipv4acd_stop(existing
);
226 return ipv4acd_start_one(link
, existing
);
228 log_link_debug(link
, "Configuring IPv4ACD for address %s.", IN4_ADDR_TO_STRING(&address
->in_addr
.in
));
230 r
= sd_ipv4acd_new(&acd
);
234 r
= sd_ipv4acd_attach_event(acd
, link
->manager
->event
, 0);
238 r
= sd_ipv4acd_set_ifindex(acd
, link
->ifindex
);
242 r
= sd_ipv4acd_set_mac(acd
, &link
->hw_addr
.ether
);
246 r
= sd_ipv4acd_set_address(acd
, &address
->in_addr
.in
);
250 r
= sd_ipv4acd_set_callback(acd
, on_acd
, link
);
254 r
= sd_ipv4acd_set_check_mac_callback(acd
, ipv4acd_check_mac
, link
->manager
);
258 r
= hashmap_ensure_put(&link
->ipv4acd_by_address
, &ipv4acd_hash_ops
, IN4_ADDR_TO_PTR(&address
->in_addr
.in
), acd
);
262 return ipv4acd_start_one(link
, TAKE_PTR(acd
));
265 void ipv4acd_detach(Link
*link
, const Address
*address
) {
269 if (address
->family
!= AF_INET
)
272 sd_ipv4acd_unref(hashmap_remove(link
->ipv4acd_by_address
, IN4_ADDR_TO_PTR(&address
->in_addr
.in
)));
275 int ipv4acd_update_mac(Link
*link
) {
281 if (link
->hw_addr
.length
!= ETH_ALEN
)
283 if (ether_addr_is_null(&link
->hw_addr
.ether
))
286 HASHMAP_FOREACH(acd
, link
->ipv4acd_by_address
) {
287 r
= sd_ipv4acd_set_mac(acd
, &link
->hw_addr
.ether
);
295 int ipv4acd_start(Link
*link
) {
301 HASHMAP_FOREACH(acd
, link
->ipv4acd_by_address
) {
302 r
= ipv4acd_start_one(link
, acd
);
310 int ipv4acd_stop(Link
*link
) {
316 HASHMAP_FOREACH(acd
, link
->ipv4acd_by_address
) {
317 k
= sd_ipv4acd_stop(acd
);
325 int ipv4acd_set_ifname(Link
*link
) {
331 HASHMAP_FOREACH(acd
, link
->ipv4acd_by_address
) {
332 r
= sd_ipv4acd_set_ifname(acd
, link
->ifname
);