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
static_ipv4acd_address_remove(Link * link,Address * address,bool on_conflict)12 static int static_ipv4acd_address_remove(Link *link, Address *address, bool on_conflict) {
13 int r;
14
15 assert(link);
16 assert(address);
17
18 /* Prevent form the address being freed. */
19 address_enter_probing(address);
20
21 if (!address_exists(address))
22 return 0; /* Not assigned. */
23
24 if (on_conflict)
25 log_link_warning(link, "Dropping address "IPV4_ADDRESS_FMT_STR", as an address conflict was detected.",
26 IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
27 else
28 log_link_debug(link, "Removing address "IPV4_ADDRESS_FMT_STR", as the ACD client is stopped.",
29 IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
30
31 r = address_remove(address);
32 if (r < 0)
33 return log_link_warning_errno(link, r, "Failed to remove address "IPV4_ADDRESS_FMT_STR": %m",
34 IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
35
36 return 0;
37 }
38
dhcp4_address_on_conflict(Link * link,Address * address)39 static int dhcp4_address_on_conflict(Link *link, Address *address) {
40 int r;
41
42 assert(link);
43 assert(link->dhcp_client);
44
45 r = sd_dhcp_client_send_decline(link->dhcp_client);
46 if (r < 0)
47 log_link_warning_errno(link, r, "Failed to send DHCP DECLINE, ignoring: %m");
48
49 if (!link->dhcp_lease)
50 /* Unlikely, but during probing the address, the lease may be lost. */
51 return 0;
52
53 log_link_warning(link, "Dropping DHCPv4 lease, as an address conflict was detected.");
54 r = dhcp4_lease_lost(link);
55 if (r < 0)
56 return log_link_warning_errno(link, r, "Failed to drop DHCPv4 lease: %m");
57
58 /* make the address will be freed. */
59 address_cancel_probing(address);
60
61 /* It is not necessary to call address_remove() here, as dhcp4_lease_lost() removes it. */
62 return 0;
63 }
64
on_acd(sd_ipv4acd * acd,int event,void * userdata)65 static void on_acd(sd_ipv4acd *acd, int event, void *userdata) {
66 Address *address = userdata;
67 Link *link;
68 int r;
69
70 assert(acd);
71 assert(address);
72 assert(address->acd == acd);
73 assert(address->link);
74 assert(address->family == AF_INET);
75 assert(IN_SET(address->source, NETWORK_CONFIG_SOURCE_STATIC, NETWORK_CONFIG_SOURCE_DHCP4));
76
77 link = address->link;
78
79 switch (event) {
80 case SD_IPV4ACD_EVENT_STOP:
81 if (address->source == NETWORK_CONFIG_SOURCE_STATIC) {
82 r = static_ipv4acd_address_remove(link, address, /* on_conflict = */ false);
83 if (r < 0)
84 link_enter_failed(link);
85 }
86
87 /* We have nothing to do for DHCPv4 lease here, as the dhcp client is already stopped
88 * when stopping the ipv4acd client. See link_stop_engines(). */
89 break;
90
91 case SD_IPV4ACD_EVENT_BIND:
92 log_link_debug(link, "Successfully claimed address "IPV4_ADDRESS_FMT_STR,
93 IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
94
95 address_cancel_probing(address);
96 break;
97
98 case SD_IPV4ACD_EVENT_CONFLICT:
99 log_link_warning(link, "Dropping address "IPV4_ADDRESS_FMT_STR", as an address conflict was detected.",
100 IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
101
102 if (address->source == NETWORK_CONFIG_SOURCE_STATIC)
103 r = static_ipv4acd_address_remove(link, address, /* on_conflict = */ true);
104 else
105 r = dhcp4_address_on_conflict(link, address);
106 if (r < 0)
107 link_enter_failed(link);
108 break;
109
110 default:
111 assert_not_reached();
112 }
113 }
114
ipv4acd_check_mac(sd_ipv4acd * acd,const struct ether_addr * mac,void * userdata)115 static int ipv4acd_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata) {
116 Manager *m = userdata;
117 struct hw_addr_data hw_addr;
118
119 assert(m);
120 assert(mac);
121
122 hw_addr = (struct hw_addr_data) {
123 .length = ETH_ALEN,
124 .ether = *mac,
125 };
126
127 return link_get_by_hw_addr(m, &hw_addr, NULL) >= 0;
128 }
129
ipv4acd_configure(Address * address)130 int ipv4acd_configure(Address *address) {
131 Link *link;
132 int r;
133
134 assert(address);
135
136 link = ASSERT_PTR(address->link);
137
138 if (address->family != AF_INET)
139 return 0;
140
141 if (!FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV4))
142 return 0;
143
144 if (link->hw_addr.length != ETH_ALEN || hw_addr_is_null(&link->hw_addr))
145 return 0;
146
147 /* Currently, only static and DHCP4 addresses are supported. */
148 assert(IN_SET(address->source, NETWORK_CONFIG_SOURCE_STATIC, NETWORK_CONFIG_SOURCE_DHCP4));
149
150 if (address->acd) {
151 address_enter_probing(address);
152 return 0;
153 }
154
155 log_link_debug(link, "Configuring IPv4ACD for address "IPV4_ADDRESS_FMT_STR,
156 IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
157
158 r = sd_ipv4acd_new(&address->acd);
159 if (r < 0)
160 return r;
161
162 r = sd_ipv4acd_attach_event(address->acd, link->manager->event, 0);
163 if (r < 0)
164 return r;
165
166 r = sd_ipv4acd_set_ifindex(address->acd, link->ifindex);
167 if (r < 0)
168 return r;
169
170 r = sd_ipv4acd_set_mac(address->acd, &link->hw_addr.ether);
171 if (r < 0)
172 return r;
173
174 r = sd_ipv4acd_set_address(address->acd, &address->in_addr.in);
175 if (r < 0)
176 return r;
177
178 r = sd_ipv4acd_set_callback(address->acd, on_acd, address);
179 if (r < 0)
180 return r;
181
182 r = sd_ipv4acd_set_check_mac_callback(address->acd, ipv4acd_check_mac, link->manager);
183 if (r < 0)
184 return r;
185
186 if (link_has_carrier(link)) {
187 r = sd_ipv4acd_start(address->acd, true);
188 if (r < 0)
189 return r;
190 }
191
192 address_enter_probing(address);
193 return 0;
194 }
195
ipv4acd_update_mac(Link * link)196 int ipv4acd_update_mac(Link *link) {
197 Address *address;
198 int k, r = 0;
199
200 assert(link);
201
202 if (link->hw_addr.length != ETH_ALEN)
203 return 0;
204 if (ether_addr_is_null(&link->hw_addr.ether))
205 return 0;
206
207 SET_FOREACH(address, link->addresses) {
208 if (!address->acd)
209 continue;
210
211 k = sd_ipv4acd_set_mac(address->acd, &link->hw_addr.ether);
212 if (k < 0)
213 r = k;
214 }
215 if (r < 0)
216 link_enter_failed(link);
217
218 return r;
219 }
220
ipv4acd_start(Link * link)221 int ipv4acd_start(Link *link) {
222 Address *address;
223 int r;
224
225 assert(link);
226
227 SET_FOREACH(address, link->addresses) {
228 if (!address->acd)
229 continue;
230
231 if (sd_ipv4acd_is_running(address->acd))
232 continue;
233
234 r = sd_ipv4acd_start(address->acd, true);
235 if (r < 0)
236 return r;
237 }
238
239 return 0;
240 }
241
ipv4acd_stop(Link * link)242 int ipv4acd_stop(Link *link) {
243 Address *address;
244 int k, r = 0;
245
246 assert(link);
247
248 SET_FOREACH(address, link->addresses) {
249 if (!address->acd)
250 continue;
251
252 k = sd_ipv4acd_stop(address->acd);
253 if (k < 0)
254 r = k;
255 }
256
257 return r;
258 }
259
ipv4acd_set_ifname(Link * link)260 int ipv4acd_set_ifname(Link *link) {
261 Address *address;
262 int r;
263
264 assert(link);
265
266 SET_FOREACH(address, link->addresses) {
267 if (!address->acd)
268 continue;
269
270 r = sd_ipv4acd_set_ifname(address->acd, link->ifname);
271 if (r < 0)
272 return r;
273 }
274
275 return 0;
276 }
277