]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp6.c
08e867ad9406593db069e0ca890e52372834f827
[thirdparty/systemd.git] / src / network / networkd-dhcp6.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 Copyright (C) 2014 Intel Corporation. All rights reserved.
4 ***/
5
6 #include <netinet/ether.h>
7 #include <linux/if.h>
8 #include "sd-radv.h"
9
10 #include "sd-dhcp6-client.h"
11
12 #include "hashmap.h"
13 #include "hostname-util.h"
14 #include "network-internal.h"
15 #include "networkd-link.h"
16 #include "networkd-manager.h"
17 #include "siphash24.h"
18 #include "string-util.h"
19 #include "radv-internal.h"
20
21 static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
22
23 static bool dhcp6_verify_link(Link *link) {
24 if (!link->network) {
25 log_link_info(link, "Link is not managed by us");
26 return false;
27 }
28
29 if (!IN_SET(link->network->router_prefix_delegation,
30 RADV_PREFIX_DELEGATION_DHCP6,
31 RADV_PREFIX_DELEGATION_BOTH)) {
32 log_link_debug(link, "Link does not request DHCPv6 prefix delegation");
33 return false;
34 }
35
36 return true;
37 }
38
39 static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) {
40 Manager *manager;
41 Link *l;
42 Iterator i;
43
44 assert(dhcp6_link);
45
46 manager = dhcp6_link->manager;
47 assert(manager);
48
49 HASHMAP_FOREACH(l, manager->links, i) {
50 if (l == dhcp6_link)
51 continue;
52
53 if (!dhcp6_verify_link(l))
54 continue;
55
56 return true;
57 }
58
59 return false;
60 }
61
62 static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
63 Link *link) {
64 return 0;
65 }
66
67 static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix,
68 uint8_t prefix_len,
69 uint32_t lifetime_preferred,
70 uint32_t lifetime_valid) {
71 sd_radv *radv = link->radv;
72 int r;
73 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
74
75 r = sd_radv_prefix_new(&p);
76 if (r < 0)
77 return r;
78
79 r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
80 if (r < 0)
81 return r;
82
83 r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred);
84 if (r < 0)
85 return r;
86
87 r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid);
88 if (r < 0)
89 return r;
90
91 r = sd_radv_stop(radv);
92 if (r < 0)
93 return r;
94
95 r = sd_radv_add_prefix(radv, p, true);
96 if (r < 0 && r != -EEXIST)
97 return r;
98
99 r = manager_dhcp6_prefix_add(link->manager, &p->opt.in6_addr, link);
100 if (r < 0)
101 return r;
102
103 return sd_radv_start(radv);
104 }
105
106 static Network *dhcp6_reset_pd_prefix_network(Link *link) {
107 assert(link);
108 assert(link->manager);
109 assert(link->manager->networks);
110
111 return link->manager->networks;
112 }
113
114 static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
115 struct in6_addr *pd_prefix,
116 uint8_t pd_prefix_len,
117 uint32_t lifetime_preferred,
118 uint32_t lifetime_valid) {
119 Link *link;
120 Manager *manager = dhcp6_link->manager;
121 union in_addr_union prefix;
122 uint8_t n_prefixes, n_used = 0;
123 _cleanup_free_ char *buf = NULL;
124 int r;
125
126 assert(manager);
127 assert(pd_prefix_len <= 64);
128
129 prefix.in6 = *pd_prefix;
130
131 r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len);
132 if (r < 0)
133 return r;
134
135 n_prefixes = 1 << (64 - pd_prefix_len);
136
137 (void) in_addr_to_string(AF_INET6, &prefix, &buf);
138 log_link_debug(dhcp6_link, "Assigning up to %u prefixes from %s/%u",
139 n_prefixes, strnull(buf), pd_prefix_len);
140
141 while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) {
142 Link *assigned_link;
143
144 if (n_used == n_prefixes) {
145 log_link_debug(dhcp6_link, "Assigned %u/%u prefixes from %s/%u",
146 n_used, n_prefixes, strnull(buf), pd_prefix_len);
147
148 return -EAGAIN;
149 }
150
151 if (link == dhcp6_link)
152 continue;
153
154 if (!dhcp6_verify_link(link))
155 continue;
156
157 assigned_link = manager_dhcp6_prefix_get(manager, &prefix.in6);
158 if (assigned_link != NULL && assigned_link != link)
159 continue;
160
161 r = dhcp6_pd_prefix_assign(link, &prefix.in6, 64,
162 lifetime_preferred, lifetime_valid);
163 if (r < 0) {
164 log_link_error_errno(link, r, "Unable to %s prefix %s/%u for link: %m",
165 assigned_link ? "update": "assign",
166 strnull(buf), pd_prefix_len);
167
168 if (assigned_link == NULL)
169 continue;
170
171 } else
172 log_link_debug(link, "Assigned prefix %u/%u %s/64 to link",
173 n_used + 1, n_prefixes, strnull(buf));
174
175 n_used++;
176
177 r = in_addr_prefix_next(AF_INET6, &prefix, pd_prefix_len);
178 if (r < 0 && n_used < n_prefixes)
179 return r;
180 }
181
182 if (n_used < n_prefixes) {
183 Route *route;
184 int n = n_used;
185
186 r = route_new(&route);
187 if (r < 0)
188 return r;
189
190 route->family = AF_INET6;
191
192 while (n < n_prefixes) {
193 route_update(route, &prefix, pd_prefix_len, NULL, NULL,
194 0, 0, RTN_UNREACHABLE);
195
196 r = route_configure(route, dhcp6_link, NULL);
197 if (r < 0) {
198 route_free(route);
199 return r;
200 }
201
202 r = in_addr_prefix_next(AF_INET6, &prefix, pd_prefix_len);
203 if (r < 0)
204 return r;
205 }
206 }
207
208 return n_used;
209 }
210
211 static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
212 int r;
213 sd_dhcp6_lease *lease;
214 struct in6_addr pd_prefix;
215 uint8_t pd_prefix_len;
216 uint32_t lifetime_preferred, lifetime_valid;
217 _cleanup_free_ char *buf = NULL;
218 Iterator i = ITERATOR_FIRST;
219
220 r = sd_dhcp6_client_get_lease(client, &lease);
221 if (r < 0)
222 return r;
223
224 (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &pd_prefix, &buf);
225
226 dhcp6_reset_pd_prefix_network(link);
227 sd_dhcp6_lease_reset_pd_prefix_iter(lease);
228
229 while (sd_dhcp6_lease_get_pd(lease, &pd_prefix, &pd_prefix_len,
230 &lifetime_preferred,
231 &lifetime_valid) >= 0) {
232
233 if (pd_prefix_len > 64) {
234 log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
235 strnull(buf), pd_prefix_len);
236 continue;
237 }
238
239 r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix,
240 pd_prefix_len,
241 lifetime_preferred,
242 lifetime_valid);
243 if (r < 0 && r != -EAGAIN)
244 return r;
245
246 if (r >= 0)
247 i = ITERATOR_FIRST;
248 }
249
250 return 0;
251 }
252
253 static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
254 void *userdata) {
255 _cleanup_(link_unrefp) Link *link = userdata;
256 int r;
257
258 assert(link);
259
260 r = sd_netlink_message_get_errno(m);
261 if (r < 0 && r != -EEXIST) {
262 if (link->rtnl_extended_attrs) {
263 log_link_warning(link, "Could not set extended netlink attributes, reverting to fallback mechanism");
264
265 link->rtnl_extended_attrs = false;
266 dhcp6_lease_address_acquired(link->dhcp6_client, link);
267
268 return 1;
269 }
270
271 log_link_error_errno(link, r, "Could not set DHCPv6 address: %m");
272
273 link_enter_failed(link);
274
275 } else if (r >= 0)
276 manager_rtnl_process_address(rtnl, m, link->manager);
277
278 return 1;
279 }
280
281 static int dhcp6_address_change(
282 Link *link,
283 struct in6_addr *ip6_addr,
284 uint32_t lifetime_preferred,
285 uint32_t lifetime_valid) {
286
287 _cleanup_(address_freep) Address *addr = NULL;
288 char buffer[INET6_ADDRSTRLEN];
289 int r;
290
291 r = address_new(&addr);
292 if (r < 0)
293 return r;
294
295 addr->family = AF_INET6;
296 memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr));
297
298 addr->flags = IFA_F_NOPREFIXROUTE;
299 addr->prefixlen = 128;
300
301 addr->cinfo.ifa_prefered = lifetime_preferred;
302 addr->cinfo.ifa_valid = lifetime_valid;
303
304 log_link_info(link,
305 "DHCPv6 address %s/%d timeout preferred %d valid %d",
306 inet_ntop(AF_INET6, &addr->in_addr.in6, buffer, sizeof(buffer)),
307 addr->prefixlen, lifetime_preferred, lifetime_valid);
308
309 r = address_configure(addr, link, dhcp6_address_handler, true);
310 if (r < 0)
311 log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
312
313 return r;
314 }
315
316 static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
317 int r;
318 sd_dhcp6_lease *lease;
319 struct in6_addr ip6_addr;
320 uint32_t lifetime_preferred, lifetime_valid;
321
322 r = sd_dhcp6_client_get_lease(client, &lease);
323 if (r < 0)
324 return r;
325
326 sd_dhcp6_lease_reset_address_iter(lease);
327
328 while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
329 &lifetime_preferred,
330 &lifetime_valid) >= 0) {
331
332 r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid);
333 if (r < 0)
334 return r;
335 }
336
337 return 0;
338 }
339
340 static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
341 int r;
342 Link *link = userdata;
343
344 assert(link);
345 assert(link->network);
346
347 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
348 return;
349
350 switch(event) {
351 case SD_DHCP6_CLIENT_EVENT_STOP:
352 case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
353 case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
354 if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
355 log_link_warning(link, "DHCPv6 lease lost");
356
357 (void) manager_dhcp6_prefix_remove_all(link->manager, link);
358
359 link->dhcp6_configured = false;
360 break;
361
362 case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
363 r = dhcp6_lease_address_acquired(client, link);
364 if (r < 0) {
365 link_enter_failed(link);
366 return;
367 }
368
369 r = dhcp6_lease_pd_prefix_acquired(client, link);
370 if (r < 0)
371 log_link_debug(link, "DHCPv6 did not receive prefixes to delegate");
372
373 _fallthrough_;
374 case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
375 r = dhcp6_lease_information_acquired(client, link);
376 if (r < 0) {
377 link_enter_failed(link);
378 return;
379 }
380
381 link->dhcp6_configured = true;
382 break;
383
384 default:
385 if (event < 0)
386 log_link_warning_errno(link, event, "DHCPv6 error: %m");
387 else
388 log_link_warning(link, "DHCPv6 unknown event: %d", event);
389 return;
390 }
391
392 link_check_ready(link);
393 }
394
395 int dhcp6_request_address(Link *link, int ir) {
396 int r, inf_req;
397 bool running;
398
399 assert(link);
400 assert(link->dhcp6_client);
401 assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
402
403 r = sd_dhcp6_client_is_running(link->dhcp6_client);
404 if (r < 0)
405 return r;
406 else
407 running = r;
408
409 if (running) {
410 r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
411 if (r < 0)
412 return r;
413
414 if (inf_req == ir)
415 return 0;
416
417 r = sd_dhcp6_client_stop(link->dhcp6_client);
418 if (r < 0)
419 return r;
420 } else {
421 r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
422 if (r < 0)
423 return r;
424 }
425
426 r = sd_dhcp6_client_set_information_request(link->dhcp6_client, ir);
427 if (r < 0)
428 return r;
429
430 r = sd_dhcp6_client_start(link->dhcp6_client);
431 if (r < 0)
432 return r;
433
434 return 0;
435 }
436
437 static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
438 _cleanup_free_ char *hostname = NULL;
439 const char *hn;
440 int r;
441
442 assert(link);
443
444 if (!link->network->dhcp_send_hostname)
445 hn = NULL;
446 else if (link->network->dhcp_hostname)
447 hn = link->network->dhcp_hostname;
448 else {
449 r = gethostname_strict(&hostname);
450 if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
451 return r;
452
453 hn = hostname;
454 }
455
456 return sd_dhcp6_client_set_fqdn(client, hn);
457 }
458
459 int dhcp6_configure(Link *link) {
460 sd_dhcp6_client *client = NULL;
461 const DUID *duid;
462 int r;
463
464 assert(link);
465 assert(link->network);
466
467 if (link->dhcp6_client)
468 return 0;
469
470 r = sd_dhcp6_client_new(&client);
471 if (r < 0)
472 return r;
473
474 r = sd_dhcp6_client_attach_event(client, NULL, 0);
475 if (r < 0)
476 goto error;
477
478 r = sd_dhcp6_client_set_mac(client,
479 (const uint8_t *) &link->mac,
480 sizeof (link->mac), ARPHRD_ETHER);
481 if (r < 0)
482 goto error;
483
484 r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
485 if (r < 0)
486 goto error;
487
488 duid = link_duid(link);
489 r = sd_dhcp6_client_set_duid(client,
490 duid->type,
491 duid->raw_data_len > 0 ? duid->raw_data : NULL,
492 duid->raw_data_len);
493 if (r < 0)
494 goto error;
495
496 r = dhcp6_set_hostname(client, link);
497 if (r < 0)
498 goto error;
499
500 r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
501 if (r < 0)
502 goto error;
503
504 if (link->network->rapid_commit) {
505 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT);
506 if (r < 0)
507 goto error;
508 }
509
510 r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
511 if (r < 0)
512 goto error;
513
514 if (dhcp6_enable_prefix_delegation(link)) {
515 r = sd_dhcp6_client_set_prefix_delegation(client, true);
516 if (r < 0)
517 goto error;
518 }
519
520 link->dhcp6_client = client;
521
522 return 0;
523
524 error:
525 sd_dhcp6_client_unref(client);
526 return r;
527 }