]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp6.c
Merge pull request #10984 from fbuihuu/tmpfiles-be-more-explicit-with-unsafe-transition
[thirdparty/systemd.git] / src / network / networkd-dhcp6.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 Copyright © 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_get_prefix_delegation(Link *link) {
24 if (!link->network)
25 return false;
26
27 return IN_SET(link->network->router_prefix_delegation,
28 RADV_PREFIX_DELEGATION_DHCP6,
29 RADV_PREFIX_DELEGATION_BOTH);
30 }
31
32 static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) {
33 Manager *manager;
34 Link *l;
35 Iterator i;
36
37 assert(dhcp6_link);
38
39 manager = dhcp6_link->manager;
40 assert(manager);
41
42 HASHMAP_FOREACH(l, manager->links, i) {
43 if (l == dhcp6_link)
44 continue;
45
46 if (!dhcp6_get_prefix_delegation(l))
47 continue;
48
49 return true;
50 }
51
52 return false;
53 }
54
55 static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
56 Link *link) {
57 return 0;
58 }
59
60 static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix,
61 uint8_t prefix_len,
62 uint32_t lifetime_preferred,
63 uint32_t lifetime_valid) {
64 sd_radv *radv = link->radv;
65 int r;
66 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
67
68 r = sd_radv_prefix_new(&p);
69 if (r < 0)
70 return r;
71
72 r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
73 if (r < 0)
74 return r;
75
76 r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred);
77 if (r < 0)
78 return r;
79
80 r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid);
81 if (r < 0)
82 return r;
83
84 r = sd_radv_stop(radv);
85 if (r < 0)
86 return r;
87
88 r = sd_radv_add_prefix(radv, p, true);
89 if (r < 0 && r != -EEXIST)
90 return r;
91
92 r = manager_dhcp6_prefix_add(link->manager, &p->opt.in6_addr, link);
93 if (r < 0)
94 return r;
95
96 return sd_radv_start(radv);
97 }
98
99 static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
100 int r;
101
102 assert(link);
103
104 r = sd_netlink_message_get_errno(m);
105 if (r < 0)
106 log_link_debug_errno(link, r, "Received error on unreachable route removal for DHCPv6 delegated subnetl: %m");
107
108 return 1;
109 }
110
111 int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) {
112 int r;
113 sd_dhcp6_lease *lease;
114 union in_addr_union pd_prefix;
115 uint8_t pd_prefix_len;
116 uint32_t lifetime_preferred, lifetime_valid;
117
118 r = sd_dhcp6_client_get_lease(client, &lease);
119 if (r < 0)
120 return r;
121
122 sd_dhcp6_lease_reset_pd_prefix_iter(lease);
123
124 while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
125 &lifetime_preferred,
126 &lifetime_valid) >= 0) {
127 _cleanup_free_ char *buf = NULL;
128 _cleanup_free_ Route *route = NULL;
129
130 if (pd_prefix_len > 64)
131 continue;
132
133 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
134
135 if (pd_prefix_len < 64) {
136 r = route_new(&route);
137 if (r < 0) {
138 log_link_warning_errno(link, r, "Cannot create unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m",
139 strnull(buf),
140 pd_prefix_len);
141 continue;
142 }
143
144 route_add(link, AF_INET6, &pd_prefix, pd_prefix_len,
145 0, 0, 0, &route);
146 route_update(route, NULL, 0, NULL, NULL, 0, 0,
147 RTN_UNREACHABLE);
148
149 r = route_remove(route, link, dhcp6_route_remove_handler);
150 if (r < 0) {
151 (void) in_addr_to_string(AF_INET6,
152 &pd_prefix, &buf);
153
154 log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m",
155 strnull(buf),
156 pd_prefix_len);
157
158 continue;
159 }
160
161 log_link_debug(link, "Removing unreachable route %s/%u",
162 strnull(buf), pd_prefix_len);
163 }
164 }
165
166 return 0;
167 }
168
169 static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
170 struct in6_addr *pd_prefix,
171 uint8_t pd_prefix_len,
172 uint32_t lifetime_preferred,
173 uint32_t lifetime_valid) {
174 Link *link;
175 Manager *manager = dhcp6_link->manager;
176 union in_addr_union prefix;
177 uint64_t n_prefixes, n_used = 0;
178 _cleanup_free_ char *buf = NULL;
179 _cleanup_free_ char *assigned_buf = NULL;
180 int r;
181
182 assert(manager);
183 assert(pd_prefix_len <= 64);
184
185 prefix.in6 = *pd_prefix;
186
187 r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len);
188 if (r < 0)
189 return r;
190
191 n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
192
193 (void) in_addr_to_string(AF_INET6, &prefix, &buf);
194 log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u",
195 n_prefixes, strnull(buf), pd_prefix_len);
196
197 while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) {
198 Link *assigned_link;
199
200 if (n_used == n_prefixes) {
201 log_link_debug(dhcp6_link, "Assigned %" PRIu64 "/%" PRIu64 " prefixes from %s/%u",
202 n_used, n_prefixes, strnull(buf), pd_prefix_len);
203
204 return -EAGAIN;
205 }
206
207 if (link == dhcp6_link)
208 continue;
209
210 if (!dhcp6_get_prefix_delegation(link))
211 continue;
212
213 assigned_link = manager_dhcp6_prefix_get(manager, &prefix.in6);
214 if (assigned_link != NULL && assigned_link != link)
215 continue;
216
217 (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf);
218 r = dhcp6_pd_prefix_assign(link, &prefix.in6, 64,
219 lifetime_preferred, lifetime_valid);
220 if (r < 0) {
221 log_link_error_errno(link, r, "Unable to %s prefix %s/64 from %s/%u for link: %m",
222 assigned_link ? "update": "assign",
223 strnull(assigned_buf),
224 strnull(buf), pd_prefix_len);
225
226 if (assigned_link == NULL)
227 continue;
228
229 } else
230 log_link_debug(link, "Assigned prefix %" PRIu64 "/%" PRIu64 " %s/64 from %s/%u to link",
231 n_used + 1, n_prefixes,
232 strnull(assigned_buf),
233 strnull(buf), pd_prefix_len);
234
235 n_used++;
236
237 r = in_addr_prefix_next(AF_INET6, &prefix, 64);
238 if (r < 0 && n_used < n_prefixes)
239 return r;
240 }
241
242 return 0;
243 }
244
245 static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
246 int r;
247
248 assert(link);
249
250 r = sd_netlink_message_get_errno(m);
251 if (r < 0 && r != -EEXIST)
252 log_link_debug_errno(link, r, "Received error when adding unreachable route for DHCPv6 delegated subnet: %m");
253
254 return 1;
255 }
256
257
258 static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
259 int r;
260 sd_dhcp6_lease *lease;
261 union in_addr_union pd_prefix;
262 uint8_t pd_prefix_len;
263 uint32_t lifetime_preferred, lifetime_valid;
264 _cleanup_free_ char *buf = NULL;
265 Iterator i = ITERATOR_FIRST;
266
267 r = sd_dhcp6_client_get_lease(client, &lease);
268 if (r < 0)
269 return r;
270
271 sd_dhcp6_lease_reset_pd_prefix_iter(lease);
272
273 while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
274 &lifetime_preferred,
275 &lifetime_valid) >= 0) {
276
277 if (pd_prefix_len > 64) {
278 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
279 log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
280 strnull(buf), pd_prefix_len);
281 continue;
282 }
283
284 if (pd_prefix_len < 48) {
285 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
286 log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u",
287 strnull(buf), pd_prefix_len);
288 }
289
290 if (pd_prefix_len < 64) {
291 Route *route = NULL;
292
293 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
294
295 r = route_new(&route);
296 if (r < 0) {
297 log_link_warning_errno(link, r, "Cannot create unreachable route for DHCPv6 delegated subnet %s/%u: %m",
298 strnull(buf),
299 pd_prefix_len);
300 continue;
301 }
302
303 route_add(link, AF_INET6, &pd_prefix, pd_prefix_len,
304 0, 0, 0, &route);
305 route_update(route, NULL, 0, NULL, NULL, 0, 0,
306 RTN_UNREACHABLE);
307
308 r = route_configure(route, link, dhcp6_route_handler);
309 if (r < 0) {
310 log_link_warning_errno(link, r, "Cannot configure unreachable route for delegated subnet %s/%u: %m",
311 strnull(buf),
312 pd_prefix_len);
313 route_free(route);
314 continue;
315 }
316
317 route_free(route);
318
319 log_link_debug(link, "Configuring unreachable route for %s/%u",
320 strnull(buf), pd_prefix_len);
321
322 } else
323 log_link_debug(link, "Not adding a blocking route since distributed prefix is /64");
324
325 r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix.in6,
326 pd_prefix_len,
327 lifetime_preferred,
328 lifetime_valid);
329 if (r < 0 && r != -EAGAIN)
330 return r;
331
332 if (r >= 0)
333 i = ITERATOR_FIRST;
334 }
335
336 return 0;
337 }
338
339 int dhcp6_request_prefix_delegation(Link *link) {
340 Link *l;
341 Iterator i;
342
343 assert_return(link, -EINVAL);
344 assert_return(link->manager, -EOPNOTSUPP);
345
346 if (dhcp6_get_prefix_delegation(link) <= 0)
347 return 0;
348
349 log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link");
350
351 HASHMAP_FOREACH(l, link->manager->links, i) {
352 int r, enabled;
353
354 if (l == link)
355 continue;
356
357 if (!l->dhcp6_client)
358 continue;
359
360 r = sd_dhcp6_client_get_prefix_delegation(l->dhcp6_client, &enabled);
361 if (r < 0) {
362 log_link_warning_errno(l, r, "Cannot get prefix delegation when adding new link");
363 continue;
364 }
365
366 if (enabled == 0) {
367 r = sd_dhcp6_client_set_prefix_delegation(l->dhcp6_client, 1);
368 if (r < 0) {
369 log_link_warning_errno(l, r, "Cannot enable prefix delegation when adding new link");
370 continue;
371 }
372 }
373
374 r = sd_dhcp6_client_is_running(l->dhcp6_client);
375 if (r <= 0)
376 continue;
377
378 if (enabled != 0) {
379 log_link_debug(l, "Requesting re-assignment of delegated prefixes after adding new link");
380 (void) dhcp6_lease_pd_prefix_acquired(l->dhcp6_client, l);
381
382 continue;
383 }
384
385 r = sd_dhcp6_client_stop(l->dhcp6_client);
386 if (r < 0) {
387 log_link_warning_errno(l, r, "Cannot stop DHCPv6 prefix delegation client after adding new link");
388 continue;
389 }
390
391 r = sd_dhcp6_client_start(l->dhcp6_client);
392 if (r < 0) {
393 log_link_warning_errno(l, r, "Cannot restart DHCPv6 prefix delegation client after adding new link");
394 continue;
395 }
396
397 log_link_debug(l, "Restarted DHCPv6 client to acquire prefix delegations after adding new link");
398 }
399
400 return 0;
401 }
402
403 static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
404 int r;
405
406 assert(link);
407
408 r = sd_netlink_message_get_errno(m);
409 if (r < 0 && r != -EEXIST) {
410 if (link->rtnl_extended_attrs) {
411 log_link_warning(link, "Could not set extended netlink attributes, reverting to fallback mechanism");
412
413 link->rtnl_extended_attrs = false;
414 dhcp6_lease_address_acquired(link->dhcp6_client, link);
415
416 return 1;
417 }
418
419 log_link_error_errno(link, r, "Could not set DHCPv6 address: %m");
420
421 link_enter_failed(link);
422
423 } else if (r >= 0)
424 manager_rtnl_process_address(rtnl, m, link->manager);
425
426 return 1;
427 }
428
429 static int dhcp6_address_change(
430 Link *link,
431 struct in6_addr *ip6_addr,
432 uint32_t lifetime_preferred,
433 uint32_t lifetime_valid) {
434
435 _cleanup_(address_freep) Address *addr = NULL;
436 char buffer[INET6_ADDRSTRLEN];
437 int r;
438
439 r = address_new(&addr);
440 if (r < 0)
441 return r;
442
443 addr->family = AF_INET6;
444 memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr));
445
446 addr->flags = IFA_F_NOPREFIXROUTE;
447 addr->prefixlen = 128;
448
449 addr->cinfo.ifa_prefered = lifetime_preferred;
450 addr->cinfo.ifa_valid = lifetime_valid;
451
452 log_link_info(link,
453 "DHCPv6 address %s/%d timeout preferred %d valid %d",
454 inet_ntop(AF_INET6, &addr->in_addr.in6, buffer, sizeof(buffer)),
455 addr->prefixlen, lifetime_preferred, lifetime_valid);
456
457 r = address_configure(addr, link, dhcp6_address_handler, true);
458 if (r < 0)
459 log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
460
461 return r;
462 }
463
464 static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
465 int r;
466 sd_dhcp6_lease *lease;
467 struct in6_addr ip6_addr;
468 uint32_t lifetime_preferred, lifetime_valid;
469
470 r = sd_dhcp6_client_get_lease(client, &lease);
471 if (r < 0)
472 return r;
473
474 sd_dhcp6_lease_reset_address_iter(lease);
475
476 while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
477 &lifetime_preferred,
478 &lifetime_valid) >= 0) {
479
480 r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid);
481 if (r < 0)
482 return r;
483 }
484
485 return 0;
486 }
487
488 static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
489 int r;
490 Link *link = userdata;
491
492 assert(link);
493 assert(link->network);
494
495 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
496 return;
497
498 switch(event) {
499 case SD_DHCP6_CLIENT_EVENT_STOP:
500 case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
501 case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
502 if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
503 log_link_warning(link, "DHCPv6 lease lost");
504
505 (void) dhcp6_lease_pd_prefix_lost(client, link);
506 (void) manager_dhcp6_prefix_remove_all(link->manager, link);
507
508 link->dhcp6_configured = false;
509 break;
510
511 case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
512 r = dhcp6_lease_address_acquired(client, link);
513 if (r < 0) {
514 link_enter_failed(link);
515 return;
516 }
517
518 r = dhcp6_lease_pd_prefix_acquired(client, link);
519 if (r < 0)
520 log_link_debug(link, "DHCPv6 did not receive prefixes to delegate");
521
522 _fallthrough_;
523 case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
524 r = dhcp6_lease_information_acquired(client, link);
525 if (r < 0) {
526 link_enter_failed(link);
527 return;
528 }
529
530 link->dhcp6_configured = true;
531 break;
532
533 default:
534 if (event < 0)
535 log_link_warning_errno(link, event, "DHCPv6 error: %m");
536 else
537 log_link_warning(link, "DHCPv6 unknown event: %d", event);
538 return;
539 }
540
541 link_check_ready(link);
542 }
543
544 int dhcp6_request_address(Link *link, int ir) {
545 int r, inf_req, pd;
546 bool running;
547
548 assert(link);
549 assert(link->dhcp6_client);
550 assert(link->network);
551 assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
552
553 r = sd_dhcp6_client_is_running(link->dhcp6_client);
554 if (r < 0)
555 return r;
556 else
557 running = r;
558
559 r = sd_dhcp6_client_get_prefix_delegation(link->dhcp6_client, &pd);
560 if (r < 0)
561 return r;
562
563 if (pd && ir && link->network->dhcp6_force_pd_other_information) {
564 log_link_debug(link, "Enabling managed mode to request DHCPv6 PD with 'Other Information' set");
565
566 r = sd_dhcp6_client_set_address_request(link->dhcp6_client,
567 false);
568 if (r < 0 )
569 return r;
570
571 ir = false;
572 }
573
574 if (running) {
575 r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
576 if (r < 0)
577 return r;
578
579 if (inf_req == ir)
580 return 0;
581
582 r = sd_dhcp6_client_stop(link->dhcp6_client);
583 if (r < 0)
584 return r;
585 } else {
586 r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
587 if (r < 0)
588 return r;
589 }
590
591 r = sd_dhcp6_client_set_information_request(link->dhcp6_client, ir);
592 if (r < 0)
593 return r;
594
595 r = sd_dhcp6_client_start(link->dhcp6_client);
596 if (r < 0)
597 return r;
598
599 return 0;
600 }
601
602 static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
603 _cleanup_free_ char *hostname = NULL;
604 const char *hn;
605 int r;
606
607 assert(link);
608
609 if (!link->network->dhcp_send_hostname)
610 hn = NULL;
611 else if (link->network->dhcp_hostname)
612 hn = link->network->dhcp_hostname;
613 else {
614 r = gethostname_strict(&hostname);
615 if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
616 return r;
617
618 hn = hostname;
619 }
620
621 r = sd_dhcp6_client_set_fqdn(client, hn);
622 if (r == -EINVAL && hostname)
623 /* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */
624 log_link_warning_errno(link, r, "DHCP6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
625 else if (r < 0)
626 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set hostname: %m");
627
628 return 0;
629 }
630
631 int dhcp6_configure(Link *link) {
632 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
633 const DUID *duid;
634 int r;
635
636 assert(link);
637 assert(link->network);
638
639 if (link->dhcp6_client)
640 return 0;
641
642 r = sd_dhcp6_client_new(&client);
643 if (r == -ENOMEM)
644 return log_oom();
645 if (r < 0)
646 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m");
647
648 r = sd_dhcp6_client_attach_event(client, NULL, 0);
649 if (r < 0)
650 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m");
651
652 r = sd_dhcp6_client_set_mac(client,
653 (const uint8_t *) &link->mac,
654 sizeof (link->mac), ARPHRD_ETHER);
655 if (r < 0)
656 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MAC address: %m");
657
658 r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
659 if (r < 0)
660 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m");
661
662 duid = link_get_duid(link);
663 if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
664 r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
665 else
666 r = sd_dhcp6_client_set_duid(client,
667 duid->type,
668 duid->raw_data_len > 0 ? duid->raw_data : NULL,
669 duid->raw_data_len);
670 if (r < 0)
671 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m");
672
673 r = dhcp6_set_hostname(client, link);
674 if (r < 0)
675 return r;
676
677 r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
678 if (r < 0)
679 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set ifindex: %m");
680
681 if (link->network->rapid_commit) {
682 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT);
683 if (r < 0)
684 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m");
685 }
686
687 r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
688 if (r < 0)
689 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m");
690
691 if (dhcp6_enable_prefix_delegation(link)) {
692 r = sd_dhcp6_client_set_prefix_delegation(client, true);
693 if (r < 0)
694 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix delegation: %m");
695 }
696
697 link->dhcp6_client = TAKE_PTR(client);
698
699 return 0;
700 }