]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-ipv4ll.c
ee78fffdfb383320430c3ad36ae6b25a3f3c504b
[thirdparty/systemd.git] / src / network / networkd-ipv4ll.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <netinet/in.h>
4 #include <linux/if.h>
5
6 #include "netif-util.h"
7 #include "networkd-address.h"
8 #include "networkd-ipv4acd.h"
9 #include "networkd-ipv4ll.h"
10 #include "networkd-link.h"
11 #include "networkd-manager.h"
12 #include "networkd-queue.h"
13 #include "parse-util.h"
14
15 bool link_ipv4ll_enabled(Link *link) {
16 assert(link);
17
18 if (!link_ipv4acd_supported(link))
19 return false;
20
21 if (!link->network)
22 return false;
23
24 if (link->network->bond)
25 return false;
26
27 return link->network->link_local & ADDRESS_FAMILY_IPV4;
28 }
29
30 static int address_new_from_ipv4ll(Link *link, Address **ret) {
31 _cleanup_(address_freep) Address *address = NULL;
32 struct in_addr addr;
33 int r;
34
35 assert(link);
36 assert(link->ipv4ll);
37 assert(ret);
38
39 r = sd_ipv4ll_get_address(link->ipv4ll, &addr);
40 if (r < 0)
41 return r;
42
43 r = address_new(&address);
44 if (r < 0)
45 return -ENOMEM;
46
47 address->source = NETWORK_CONFIG_SOURCE_IPV4LL;
48 address->family = AF_INET;
49 address->in_addr.in = addr;
50 address->prefixlen = 16;
51 address->scope = RT_SCOPE_LINK;
52 address->route_metric = IPV4LL_ROUTE_METRIC;
53 address_set_broadcast(address, link);
54
55 *ret = TAKE_PTR(address);
56 return 0;
57 }
58
59 static int ipv4ll_address_lost(Link *link) {
60 _cleanup_(address_freep) Address *address = NULL;
61 Address *existing;
62 int r;
63
64 assert(link);
65
66 link->ipv4ll_address_configured = false;
67
68 r = address_new_from_ipv4ll(link, &address);
69 if (r == -ENOENT)
70 return 0;
71 if (r < 0)
72 return r;
73
74 if (address_get(link, address, &existing) < 0)
75 return 0;
76
77 if (existing->source != NETWORK_CONFIG_SOURCE_IPV4LL)
78 return 0;
79
80 if (!address_exists(existing))
81 return 0;
82
83 log_link_debug(link, "IPv4 link-local release "IPV4_ADDRESS_FMT_STR,
84 IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
85
86 return address_remove(existing);
87 }
88
89 static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) {
90 int r;
91
92 assert(link);
93 assert(!link->ipv4ll_address_configured);
94
95 r = address_configure_handler_internal(rtnl, m, link, "Could not set ipv4ll address");
96 if (r <= 0)
97 return r;
98
99 link->ipv4ll_address_configured = true;
100 link_check_ready(link);
101
102 return 1;
103 }
104
105 static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
106 _cleanup_(address_freep) Address *address = NULL;
107 int r;
108
109 assert(ll);
110 assert(link);
111
112 link->ipv4ll_address_configured = false;
113
114 r = address_new_from_ipv4ll(link, &address);
115 if (r == -ENOENT)
116 return 0;
117 if (r < 0)
118 return r;
119
120 log_link_debug(link, "IPv4 link-local claim "IPV4_ADDRESS_FMT_STR,
121 IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
122
123 return link_request_address(link, address, NULL, ipv4ll_address_handler, NULL);
124 }
125
126 static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) {
127 Link *link = ASSERT_PTR(userdata);
128 int r;
129
130 assert(link->network);
131
132 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
133 return;
134
135 switch (event) {
136 case SD_IPV4LL_EVENT_STOP:
137 r = ipv4ll_address_lost(link);
138 if (r < 0) {
139 link_enter_failed(link);
140 return;
141 }
142 break;
143 case SD_IPV4LL_EVENT_CONFLICT:
144 r = ipv4ll_address_lost(link);
145 if (r < 0) {
146 link_enter_failed(link);
147 return;
148 }
149
150 r = sd_ipv4ll_restart(ll);
151 if (r < 0) {
152 log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
153 link_enter_failed(link);
154 }
155 break;
156 case SD_IPV4LL_EVENT_BIND:
157 r = ipv4ll_address_claimed(ll, link);
158 if (r < 0) {
159 log_link_error(link, "Failed to configure ipv4ll address: %m");
160 link_enter_failed(link);
161 return;
162 }
163 break;
164 default:
165 log_link_warning(link, "IPv4 link-local unknown event: %d", event);
166 break;
167 }
168 }
169
170 static int ipv4ll_check_mac(sd_ipv4ll *ll, const struct ether_addr *mac, void *userdata) {
171 Manager *m = ASSERT_PTR(userdata);
172 struct hw_addr_data hw_addr;
173
174 assert(mac);
175
176 hw_addr = (struct hw_addr_data) {
177 .length = ETH_ALEN,
178 .ether = *mac,
179 };
180
181 return link_get_by_hw_addr(m, &hw_addr, NULL) >= 0;
182 }
183
184 int ipv4ll_configure(Link *link) {
185 uint64_t seed;
186 int r;
187
188 assert(link);
189
190 if (!link_ipv4ll_enabled(link))
191 return 0;
192
193 if (link->ipv4ll)
194 return -EBUSY;
195
196 r = sd_ipv4ll_new(&link->ipv4ll);
197 if (r < 0)
198 return r;
199
200 r = sd_ipv4ll_attach_event(link->ipv4ll, link->manager->event, 0);
201 if (r < 0)
202 return r;
203
204 if (link->dev &&
205 net_get_unique_predictable_data(link->dev, true, &seed) >= 0) {
206 r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed);
207 if (r < 0)
208 return r;
209 }
210
211 r = sd_ipv4ll_set_mac(link->ipv4ll, &link->hw_addr.ether);
212 if (r < 0)
213 return r;
214
215 r = sd_ipv4ll_set_ifindex(link->ipv4ll, link->ifindex);
216 if (r < 0)
217 return r;
218
219 r = sd_ipv4ll_set_callback(link->ipv4ll, ipv4ll_handler, link);
220 if (r < 0)
221 return r;
222
223 return sd_ipv4ll_set_check_mac_callback(link->ipv4ll, ipv4ll_check_mac, link->manager);
224 }
225
226 int ipv4ll_update_mac(Link *link) {
227 assert(link);
228
229 if (link->hw_addr.length != ETH_ALEN)
230 return 0;
231 if (ether_addr_is_null(&link->hw_addr.ether))
232 return 0;
233 if (!link->ipv4ll)
234 return 0;
235
236 return sd_ipv4ll_set_mac(link->ipv4ll, &link->hw_addr.ether);
237 }
238
239 int config_parse_ipv4ll(
240 const char* unit,
241 const char *filename,
242 unsigned line,
243 const char *section,
244 unsigned section_line,
245 const char *lvalue,
246 int ltype,
247 const char *rvalue,
248 void *data,
249 void *userdata) {
250
251 AddressFamily *link_local = ASSERT_PTR(data);
252 int r;
253
254 assert(filename);
255 assert(lvalue);
256 assert(rvalue);
257
258 /* Note that this is mostly like
259 * config_parse_address_family(), except that it
260 * applies only to IPv4 */
261
262 r = parse_boolean(rvalue);
263 if (r < 0) {
264 log_syntax(unit, LOG_WARNING, filename, line, r,
265 "Failed to parse %s=%s, ignoring assignment. "
266 "Note that the setting %s= is deprecated, please use LinkLocalAddressing= instead.",
267 lvalue, rvalue, lvalue);
268 return 0;
269 }
270
271 SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, r);
272
273 log_syntax(unit, LOG_WARNING, filename, line, 0,
274 "%s=%s is deprecated, please use LinkLocalAddressing=%s instead.",
275 lvalue, rvalue, address_family_to_string(*link_local));
276
277 return 0;
278 }
279
280 int config_parse_ipv4ll_address(
281 const char* unit,
282 const char *filename,
283 unsigned line,
284 const char *section,
285 unsigned section_line,
286 const char *lvalue,
287 int ltype,
288 const char *rvalue,
289 void *data,
290 void *userdata) {
291
292 union in_addr_union a;
293 struct in_addr *ipv4ll_address = ASSERT_PTR(data);
294 int r;
295
296 assert(filename);
297 assert(lvalue);
298 assert(rvalue);
299
300 if (isempty(rvalue)) {
301 *ipv4ll_address = (struct in_addr) {};
302 return 0;
303 }
304
305 r = in_addr_from_string(AF_INET, rvalue, &a);
306 if (r < 0) {
307 log_syntax(unit, LOG_WARNING, filename, line, r,
308 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
309 return 0;
310 }
311 if (!in4_addr_is_link_local_dynamic(&a.in)) {
312 log_syntax(unit, LOG_WARNING, filename, line, 0,
313 "Specified address cannot be used as an IPv4 link local address, ignoring assignment: %s",
314 rvalue);
315 return 0;
316 }
317
318 *ipv4ll_address = a.in;
319 return 0;
320 }