]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-ipv4acd.c
bus-unit-util: extend the bus call timeout for UnitFreezer
[thirdparty/systemd.git] / src / network / networkd-ipv4acd.c
CommitLineData
76a86ffd
YW
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
239e4a42
KR
3#include <net/if.h> /* IFF_LOOPBACK */
4#include <net/if_arp.h> /* ARPHRD_ETHER */
29104ded 5
76a86ffd
YW
6#include "sd-dhcp-client.h"
7#include "sd-ipv4acd.h"
8
29104ded 9#include "ipvlan.h"
76a86ffd
YW
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"
15
5385e5f9
YW
16DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
17 ipv4acd_hash_ops,
18 void, trivial_hash_func, trivial_compare_func,
19 sd_ipv4acd, sd_ipv4acd_unref);
20
29104ded
YW
21bool link_ipv4acd_supported(Link *link) {
22 assert(link);
23
24 if (link->flags & IFF_LOOPBACK)
25 return false;
26
27 /* ARPHRD_INFINIBAND seems to potentially support IPv4ACD.
28 * But currently sd-ipv4acd only supports ARPHRD_ETHER. */
29 if (link->iftype != ARPHRD_ETHER)
30 return false;
31
32 if (link->hw_addr.length != ETH_ALEN)
33 return false;
34
35 if (ether_addr_is_null(&link->hw_addr.ether))
36 return false;
37
38 if (streq_ptr(link->kind, "vrf"))
39 return false;
40
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))
43 return false;
44
45 return true;
46}
47
5385e5f9
YW
48static bool address_ipv4acd_enabled(Link *link, const Address *address) {
49 assert(link);
03ff3c5a 50 assert(address);
03ff3c5a
YW
51
52 if (address->family != AF_INET)
53 return false;
54
55 if (!FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV4))
56 return false;
57
58 /* Currently, only static and DHCP4 addresses are supported. */
59 if (!IN_SET(address->source, NETWORK_CONFIG_SOURCE_STATIC, NETWORK_CONFIG_SOURCE_DHCP4))
60 return false;
61
5385e5f9 62 return link_ipv4acd_supported(link);
03ff3c5a
YW
63}
64
5385e5f9
YW
65bool ipv4acd_bound(Link *link, const Address *address) {
66 sd_ipv4acd *acd;
67
68 assert(link);
e402e99e
YW
69 assert(address);
70
5385e5f9
YW
71 if (address->family != AF_INET)
72 return true;
73
74 acd = hashmap_get(link->ipv4acd_by_address, IN4_ADDR_TO_PTR(&address->in_addr.in));
75 if (!acd)
e402e99e
YW
76 return true;
77
5385e5f9 78 return sd_ipv4acd_is_bound(acd) > 0;
e402e99e
YW
79}
80
3b6a3bde 81static int static_ipv4acd_address_remove(Link *link, Address *address, bool on_conflict) {
76a86ffd
YW
82 int r;
83
84 assert(link);
85 assert(address);
86
3b6a3bde
YW
87 if (!address_exists(address))
88 return 0; /* Not assigned. */
76a86ffd 89
3b6a3bde 90 if (on_conflict)
262ac7fc 91 log_link_warning(link, "Dropping address %s, as an address conflict was detected.", IN4_ADDR_TO_STRING(&address->in_addr.in));
3b6a3bde 92 else
262ac7fc 93 log_link_debug(link, "Removing address %s, as the ACD client is stopped.", IN4_ADDR_TO_STRING(&address->in_addr.in));
76a86ffd 94
f22b586a
YW
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. */
7f74b00a 97 r = address_remove(address, link);
76a86ffd 98 if (r < 0)
262ac7fc 99 return log_link_warning_errno(link, r, "Failed to remove address %s: %m", IN4_ADDR_TO_STRING(&address->in_addr.in));
76a86ffd
YW
100
101 return 0;
102}
103
722ac246 104static int dhcp4_address_on_conflict(Link *link) {
76a86ffd
YW
105 int r;
106
107 assert(link);
108 assert(link->dhcp_client);
109
110 r = sd_dhcp_client_send_decline(link->dhcp_client);
111 if (r < 0)
112 log_link_warning_errno(link, r, "Failed to send DHCP DECLINE, ignoring: %m");
113
114 if (!link->dhcp_lease)
115 /* Unlikely, but during probing the address, the lease may be lost. */
116 return 0;
117
cd3a828c 118 log_link_warning(link, "Dropping DHCPv4 lease, as an address conflict was detected.");
76a86ffd
YW
119 r = dhcp4_lease_lost(link);
120 if (r < 0)
121 return log_link_warning_errno(link, r, "Failed to drop DHCPv4 lease: %m");
122
123 /* It is not necessary to call address_remove() here, as dhcp4_lease_lost() removes it. */
124 return 0;
125}
126
3b6a3bde 127static void on_acd(sd_ipv4acd *acd, int event, void *userdata) {
5385e5f9
YW
128 Link *link = ASSERT_PTR(userdata);
129 Address *address = NULL;
262ac7fc 130 struct in_addr a;
76a86ffd
YW
131 int r;
132
133 assert(acd);
76a86ffd 134
5385e5f9
YW
135 r = sd_ipv4acd_get_address(acd, &a);
136 if (r < 0) {
137 log_link_warning_errno(link, r, "Failed to get address from IPv4ACD: %m");
138 link_enter_failed(link);
139 }
140
141 (void) link_get_ipv4_address(link, &a, 0, &address);
76a86ffd
YW
142
143 switch (event) {
144 case SD_IPV4ACD_EVENT_STOP:
5385e5f9
YW
145 if (!address)
146 break;
e402e99e 147
3b6a3bde
YW
148 if (address->source == NETWORK_CONFIG_SOURCE_STATIC) {
149 r = static_ipv4acd_address_remove(link, address, /* on_conflict = */ false);
76a86ffd
YW
150 if (r < 0)
151 link_enter_failed(link);
152 }
153
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(). */
156 break;
157
158 case SD_IPV4ACD_EVENT_BIND:
262ac7fc 159 log_link_debug(link, "Successfully claimed address %s", IN4_ADDR_TO_STRING(&a));
76a86ffd
YW
160 break;
161
162 case SD_IPV4ACD_EVENT_CONFLICT:
5385e5f9
YW
163 if (!address)
164 break;
e402e99e 165
262ac7fc 166 log_link_warning(link, "Dropping address %s, as an address conflict was detected.", IN4_ADDR_TO_STRING(&a));
3b6a3bde
YW
167
168 if (address->source == NETWORK_CONFIG_SOURCE_STATIC)
169 r = static_ipv4acd_address_remove(link, address, /* on_conflict = */ true);
76a86ffd 170 else
722ac246 171 r = dhcp4_address_on_conflict(link);
76a86ffd
YW
172 if (r < 0)
173 link_enter_failed(link);
174 break;
175
176 default:
04499a70 177 assert_not_reached();
76a86ffd
YW
178 }
179}
180
d7ab6ef0 181static int ipv4acd_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata) {
99534007 182 Manager *m = ASSERT_PTR(userdata);
d7ab6ef0
YW
183 struct hw_addr_data hw_addr;
184
d7ab6ef0
YW
185 assert(mac);
186
187 hw_addr = (struct hw_addr_data) {
188 .length = ETH_ALEN,
189 .ether = *mac,
190 };
191
192 return link_get_by_hw_addr(m, &hw_addr, NULL) >= 0;
193}
194
5385e5f9
YW
195static int ipv4acd_start_one(Link *link, sd_ipv4acd *acd) {
196 assert(link);
197 assert(acd);
e402e99e 198
5385e5f9 199 if (sd_ipv4acd_is_running(acd))
e402e99e
YW
200 return 0;
201
5385e5f9 202 if (!link_has_carrier(link))
e402e99e
YW
203 return 0;
204
5385e5f9 205 return sd_ipv4acd_start(acd, /* reset_conflicts = */ true);
e402e99e
YW
206}
207
5385e5f9
YW
208int ipv4acd_configure(Link *link, const Address *address) {
209 _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL;
210 sd_ipv4acd *existing;
76a86ffd
YW
211 int r;
212
5385e5f9
YW
213 assert(link);
214 assert(link->manager);
3b6a3bde 215 assert(address);
c565b655 216
5385e5f9 217 if (address->family != AF_INET)
c565b655 218 return 0;
3b6a3bde 219
5385e5f9
YW
220 existing = hashmap_get(link->ipv4acd_by_address, IN4_ADDR_TO_PTR(&address->in_addr.in));
221
222 if (!address_ipv4acd_enabled(link, address))
223 return sd_ipv4acd_stop(existing);
224
225 if (existing)
226 return ipv4acd_start_one(link, existing);
3b6a3bde 227
262ac7fc 228 log_link_debug(link, "Configuring IPv4ACD for address %s.", IN4_ADDR_TO_STRING(&address->in_addr.in));
76a86ffd 229
5385e5f9
YW
230 r = sd_ipv4acd_new(&acd);
231 if (r < 0)
232 return r;
233
234 r = sd_ipv4acd_attach_event(acd, link->manager->event, 0);
76a86ffd
YW
235 if (r < 0)
236 return r;
237
5385e5f9 238 r = sd_ipv4acd_set_ifindex(acd, link->ifindex);
76a86ffd
YW
239 if (r < 0)
240 return r;
241
5385e5f9 242 r = sd_ipv4acd_set_mac(acd, &link->hw_addr.ether);
76a86ffd
YW
243 if (r < 0)
244 return r;
245
5385e5f9 246 r = sd_ipv4acd_set_address(acd, &address->in_addr.in);
76a86ffd
YW
247 if (r < 0)
248 return r;
249
5385e5f9 250 r = sd_ipv4acd_set_callback(acd, on_acd, link);
76a86ffd
YW
251 if (r < 0)
252 return r;
253
5385e5f9 254 r = sd_ipv4acd_set_check_mac_callback(acd, ipv4acd_check_mac, link->manager);
76a86ffd
YW
255 if (r < 0)
256 return r;
257
5385e5f9 258 r = hashmap_ensure_put(&link->ipv4acd_by_address, &ipv4acd_hash_ops, IN4_ADDR_TO_PTR(&address->in_addr.in), acd);
d7ab6ef0
YW
259 if (r < 0)
260 return r;
261
5385e5f9
YW
262 return ipv4acd_start_one(link, TAKE_PTR(acd));
263}
264
265void ipv4acd_detach(Link *link, const Address *address) {
266 assert(link);
267 assert(address);
268
269 if (address->family != AF_INET)
270 return;
271
272 sd_ipv4acd_unref(hashmap_remove(link->ipv4acd_by_address, IN4_ADDR_TO_PTR(&address->in_addr.in)));
76a86ffd
YW
273}
274
76a86ffd 275int ipv4acd_update_mac(Link *link) {
5385e5f9
YW
276 sd_ipv4acd *acd;
277 int r;
76a86ffd
YW
278
279 assert(link);
280
281 if (link->hw_addr.length != ETH_ALEN)
282 return 0;
283 if (ether_addr_is_null(&link->hw_addr.ether))
284 return 0;
285
5385e5f9
YW
286 HASHMAP_FOREACH(acd, link->ipv4acd_by_address) {
287 r = sd_ipv4acd_set_mac(acd, &link->hw_addr.ether);
288 if (r < 0)
289 return r;
76a86ffd 290 }
76a86ffd 291
5385e5f9 292 return 0;
76a86ffd
YW
293}
294
295int ipv4acd_start(Link *link) {
5385e5f9 296 sd_ipv4acd *acd;
76a86ffd
YW
297 int r;
298
299 assert(link);
300
5385e5f9
YW
301 HASHMAP_FOREACH(acd, link->ipv4acd_by_address) {
302 r = ipv4acd_start_one(link, acd);
76a86ffd
YW
303 if (r < 0)
304 return r;
305 }
306
307 return 0;
308}
309
310int ipv4acd_stop(Link *link) {
5385e5f9 311 sd_ipv4acd *acd;
76a86ffd
YW
312 int k, r = 0;
313
314 assert(link);
315
5385e5f9
YW
316 HASHMAP_FOREACH(acd, link->ipv4acd_by_address) {
317 k = sd_ipv4acd_stop(acd);
76a86ffd
YW
318 if (k < 0)
319 r = k;
320 }
321
322 return r;
323}
86173383
YW
324
325int ipv4acd_set_ifname(Link *link) {
5385e5f9 326 sd_ipv4acd *acd;
86173383
YW
327 int r;
328
329 assert(link);
330
5385e5f9
YW
331 HASHMAP_FOREACH(acd, link->ipv4acd_by_address) {
332 r = sd_ipv4acd_set_ifname(acd, link->ifname);
86173383
YW
333 if (r < 0)
334 return r;
335 }
336
337 return 0;
338}