]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-ipv4acd.c
Merge pull request #20303 from andir/sysconfig-example
[thirdparty/systemd.git] / src / network / networkd-ipv4acd.c
CommitLineData
76a86ffd
YW
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include "sd-dhcp-client.h"
4#include "sd-ipv4acd.h"
5
6#include "networkd-address.h"
7#include "networkd-dhcp4.h"
8#include "networkd-ipv4acd.h"
9#include "networkd-link.h"
10#include "networkd-manager.h"
11
12static int static_address_on_stop(Link *link, Address *address) {
13 int r;
14
15 assert(link);
16 assert(address);
17
18 if (address_get(link, address, NULL) < 0)
19 return 0;
20
21 log_link_debug(link, "Removing address "IPV4_ADDRESS_FMT_STR", as the ACD client is stopped.",
22 IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
23
24 r = address_remove(address, link);
25 if (r < 0)
26 return log_link_warning_errno(link, r, "Failed to remove address "IPV4_ADDRESS_FMT_STR": %m",
27 IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
28
29 return 0;
30}
31
32static int static_address_on_conflict(Link *link, Address *address) {
33 int r;
34
35 assert(link);
36 assert(address);
37
38 if (address_get(link, address, NULL) < 0) {
cd3a828c 39 log_link_warning(link, "Cannot configure requested address "IPV4_ADDRESS_FMT_STR", as an address conflict was detected.",
76a86ffd
YW
40 IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
41 return 0;
42 }
43
cd3a828c 44 log_link_warning(link, "Dropping address "IPV4_ADDRESS_FMT_STR", as an address conflict was detected.",
76a86ffd
YW
45 IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
46
47 r = address_remove(address, link);
48 if (r < 0)
49 return log_link_warning_errno(link, r, "Failed to remove address "IPV4_ADDRESS_FMT_STR": %m",
50 IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
51
52 return 0;
53}
54
55static int dhcp4_address_on_conflict(Link *link) {
56 int r;
57
58 assert(link);
59 assert(link->dhcp_client);
60
61 r = sd_dhcp_client_send_decline(link->dhcp_client);
62 if (r < 0)
63 log_link_warning_errno(link, r, "Failed to send DHCP DECLINE, ignoring: %m");
64
65 if (!link->dhcp_lease)
66 /* Unlikely, but during probing the address, the lease may be lost. */
67 return 0;
68
cd3a828c 69 log_link_warning(link, "Dropping DHCPv4 lease, as an address conflict was detected.");
76a86ffd
YW
70 r = dhcp4_lease_lost(link);
71 if (r < 0)
72 return log_link_warning_errno(link, r, "Failed to drop DHCPv4 lease: %m");
73
74 /* It is not necessary to call address_remove() here, as dhcp4_lease_lost() removes it. */
75 return 0;
76}
77
78static void on_acd(sd_ipv4acd *acd, int event, void *userdata, bool is_static) {
79 Address *address = userdata;
80 Link *link;
81 int r;
82
83 assert(acd);
84 assert(address);
85 assert(address->acd == acd);
86 assert(address->link);
87 assert(address->family == AF_INET);
88
89 link = address->link;
90
91 switch (event) {
92 case SD_IPV4ACD_EVENT_STOP:
93 if (is_static) {
94 r = static_address_on_stop(link, address);
95 if (r < 0)
96 link_enter_failed(link);
97 }
98
99 /* We have nothing to do for DHCPv4 lease here, as the dhcp client is already stopped
100 * when stopping the ipv4acd client. See link_stop_engines(). */
101 break;
102
103 case SD_IPV4ACD_EVENT_BIND:
104 log_link_debug(link, "Successfully claimed address "IPV4_ADDRESS_FMT_STR,
105 IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
106
107 address->acd_announced = true;
108 break;
109
110 case SD_IPV4ACD_EVENT_CONFLICT:
111 if (is_static)
112 r = static_address_on_conflict(link, address);
113 else
114 r = dhcp4_address_on_conflict(link);
115 if (r < 0)
116 link_enter_failed(link);
117 break;
118
119 default:
04499a70 120 assert_not_reached();
76a86ffd
YW
121 }
122}
123
124static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
125 on_acd(acd, event, userdata, true);
126}
127
128static void dhcp4_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
129 on_acd(acd, event, userdata, false);
130}
131
d7ab6ef0
YW
132static int ipv4acd_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata) {
133 Manager *m = userdata;
134 struct hw_addr_data hw_addr;
135
136 assert(m);
137 assert(mac);
138
139 hw_addr = (struct hw_addr_data) {
140 .length = ETH_ALEN,
141 .ether = *mac,
142 };
143
144 return link_get_by_hw_addr(m, &hw_addr, NULL) >= 0;
145}
146
76a86ffd
YW
147static int ipv4acd_configure(Link *link, const Address *a) {
148 _cleanup_(address_freep) Address *address = NULL;
149 int r;
150
151 assert(link);
152 assert(a);
153 assert(a->family == AF_INET);
154
155 log_link_debug(link, "Configuring IPv4ACD for address "IPV4_ADDRESS_FMT_STR,
156 IPV4_ADDRESS_FMT_VAL(a->in_addr.in));
157
158 r = address_dup(a, &address);
159 if (r < 0)
160 return r;
161
162 r = set_ensure_put(&link->addresses_ipv4acd, &address_hash_ops, address);
163 if (r < 0)
164 return r;
165 if (r == 0)
166 return -EEXIST;
167 address->link = link;
168
169 r = sd_ipv4acd_new(&address->acd);
170 if (r < 0)
171 return r;
172
173 r = sd_ipv4acd_attach_event(address->acd, link->manager->event, 0);
174 if (r < 0)
175 return r;
176
177 r = sd_ipv4acd_set_ifindex(address->acd, link->ifindex);
178 if (r < 0)
179 return r;
180
181 r = sd_ipv4acd_set_mac(address->acd, &link->hw_addr.ether);
182 if (r < 0)
183 return r;
184
185 r = sd_ipv4acd_set_address(address->acd, &address->in_addr.in);
186 if (r < 0)
187 return r;
188
189 r = sd_ipv4acd_set_callback(address->acd,
190 address->is_static ? static_address_on_acd : dhcp4_address_on_acd,
191 address);
192 if (r < 0)
193 return r;
194
d7ab6ef0
YW
195 r = sd_ipv4acd_set_check_mac_callback(address->acd, ipv4acd_check_mac, link->manager);
196 if (r < 0)
197 return r;
198
76a86ffd
YW
199 if (link_has_carrier(link)) {
200 r = sd_ipv4acd_start(address->acd, true);
201 if (r < 0)
202 return r;
203 }
204
205 TAKE_PTR(address);
206 return 0;
207}
208
209int ipv4acd_address_is_ready_to_configure(Link *link, const Address *address) {
210 Address *acd_address;
211 int r;
212
213 acd_address = set_get(link->addresses_ipv4acd, address);
214 if (!acd_address) {
215 r = ipv4acd_configure(link, address);
216 if (r < 0)
217 return log_link_warning_errno(link, r, "Failed to configure IPv4ACD client: %m");
218
219 return false;
220 }
221
222 if (!acd_address->acd_announced)
223 return false;
224
225 r = set_ensure_put(&link->addresses, &address_hash_ops, acd_address);
226 if (r < 0)
227 return log_oom();
228 if (r == 0)
229 return log_link_warning_errno(link, SYNTHETIC_ERRNO(EEXIST), "Address already exists.");
230
231 acd_address->flags |= IFA_F_TENTATIVE;
232 return true;
233}
234
235int ipv4acd_update_mac(Link *link) {
236 Address *address;
237 int k, r = 0;
238
239 assert(link);
240
241 if (link->hw_addr.length != ETH_ALEN)
242 return 0;
243 if (ether_addr_is_null(&link->hw_addr.ether))
244 return 0;
245
246 SET_FOREACH(address, link->addresses_ipv4acd) {
247 assert(address->acd);
248
249 k = sd_ipv4acd_set_mac(address->acd, &link->hw_addr.ether);
250 if (k < 0)
251 r = k;
252 }
253 if (r < 0)
254 link_enter_failed(link);
255
256 return r;
257}
258
259int ipv4acd_start(Link *link) {
260 Address *address;
261 int r;
262
263 assert(link);
264
265 SET_FOREACH(address, link->addresses_ipv4acd) {
266 if (sd_ipv4acd_is_running(address->acd))
267 continue;
268
269 r = sd_ipv4acd_start(address->acd, true);
270 if (r < 0)
271 return r;
272 }
273
274 return 0;
275}
276
277int ipv4acd_stop(Link *link) {
278 Address *address;
279 int k, r = 0;
280
281 assert(link);
282
283 SET_FOREACH(address, link->addresses_ipv4acd) {
284 k = sd_ipv4acd_stop(address->acd);
285 if (k < 0)
286 r = k;
287 }
288
289 return r;
290}