]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp6.c
Merge pull request #16056 from keszybz/minor-doc-updates
[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/in.h>
7 #include <linux/if.h>
8 #include <linux/if_arp.h>
9 #include "sd-radv.h"
10
11 #include "sd-dhcp6-client.h"
12
13 #include "escape.h"
14 #include "hashmap.h"
15 #include "hostname-util.h"
16 #include "missing_network.h"
17 #include "network-internal.h"
18 #include "networkd-dhcp6.h"
19 #include "networkd-link.h"
20 #include "networkd-manager.h"
21 #include "siphash24.h"
22 #include "string-util.h"
23 #include "radv-internal.h"
24 #include "web-util.h"
25
26 static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
27 static Link *dhcp6_prefix_get(Manager *m, struct in6_addr *addr);
28 static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link);
29 static int dhcp6_prefix_remove_all(Manager *m, Link *link);
30 static bool dhcp6_link_has_dhcpv6_prefix(Link *link);
31 static int dhcp6_assign_delegated_prefix(Link *link, const struct in6_addr *prefix,
32 uint8_t prefix_len,
33 uint32_t lifetime_preferred,
34 uint32_t lifetime_valid);
35
36 static bool dhcp6_get_prefix_delegation(Link *link) {
37 if (!link->network)
38 return false;
39
40 return IN_SET(link->network->router_prefix_delegation,
41 RADV_PREFIX_DELEGATION_DHCP6,
42 RADV_PREFIX_DELEGATION_BOTH);
43 }
44
45 static bool dhcp6_has_preferred_subnet_id(Link *link) {
46 if (!link->network)
47 return false;
48
49 return link->network->router_prefix_subnet_id >= 0;
50 }
51
52 static int dhcp6_get_preferred_delegated_prefix(
53 Manager* manager,
54 Link *link,
55 const struct in6_addr *pd_prefix,
56 uint8_t pd_prefix_len,
57 struct in6_addr *ret_addr) {
58 int r;
59 union in_addr_union pd_prefix_union = {
60 .in6 = *pd_prefix,
61 };
62 int64_t subnet_id = link->network->router_prefix_subnet_id;
63
64 assert(pd_prefix_len <= 64);
65
66 uint8_t prefix_bits = 64 - pd_prefix_len;
67 uint64_t n_prefixes = UINT64_C(1) << prefix_bits;
68 _cleanup_free_ char *assigned_buf = NULL;
69
70 /* We start off with the original PD prefix we have been assigned and
71 * iterate from there */
72 union in_addr_union prefix = {
73 .in6 = *pd_prefix,
74 };
75
76 if (subnet_id >= 0) {
77 /* If the link has a preference for a particular subnet id try to allocate that */
78 if ((uint64_t)subnet_id >= n_prefixes)
79 return log_link_debug_errno(link,
80 SYNTHETIC_ERRNO(ERANGE),
81 "subnet id %" PRIi64 " is out of range. Only have %" PRIu64 " subnets.",
82 subnet_id,
83 n_prefixes);
84
85 r = in_addr_prefix_nth(AF_INET6, &prefix, 64, subnet_id);
86 if (r < 0)
87 return log_link_debug_errno(link,
88 r,
89 "subnet id %" PRIi64 " is out of range. Only have %" PRIu64 " subnets.",
90 subnet_id,
91 n_prefixes);
92
93 /* Verify that the prefix we did calculate fits in the pd prefix.
94 * This should not fail as we checked the prefix size beforehand */
95 assert_se(in_addr_prefix_covers(AF_INET6, &pd_prefix_union, pd_prefix_len, &prefix) > 0);
96
97 Link* assigned_link = dhcp6_prefix_get(manager, &prefix.in6);
98
99 (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf);
100
101 if (assigned_link && assigned_link != link)
102 return log_link_error_errno(link, SYNTHETIC_ERRNO(EAGAIN),
103 "The requested prefix %s is already assigned to another link: %s",
104 strnull(assigned_buf),
105 strnull(assigned_link->ifname));
106
107 *ret_addr = prefix.in6;
108
109 log_link_debug(link, "The requested prefix %s is available. Using it.",
110 strnull(assigned_buf));
111 return 0;
112 } else {
113 for (uint64_t n = 0; n < n_prefixes; n++) {
114 /* if we do not have an allocation preference just iterate
115 * through the address space and return the first free prefix. */
116 Link* assigned_link = dhcp6_prefix_get(manager, &prefix.in6);
117
118 if (!assigned_link || assigned_link == link) {
119 *ret_addr = prefix.in6;
120 return 0;
121 }
122
123 r = in_addr_prefix_next(AF_INET6, &prefix, 64);
124 if (r < 0)
125 return log_link_error_errno(link,
126 r,
127 "Can't allocate another prefix. Out of address space?");
128 }
129
130 log_link_warning(link, "Couldn't find a suitable prefix. Ran out of address space.");
131 }
132
133 return -ERANGE;
134 }
135
136 static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) {
137 Manager *manager;
138 Link *l;
139 Iterator i;
140
141 assert(dhcp6_link);
142
143 manager = dhcp6_link->manager;
144 assert(manager);
145
146 HASHMAP_FOREACH(l, manager->links, i) {
147 if (l == dhcp6_link)
148 continue;
149
150 if (!dhcp6_get_prefix_delegation(l))
151 continue;
152
153 return true;
154 }
155
156 return false;
157 }
158
159 static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
160 Link *link) {
161 return 0;
162 }
163
164 static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix,
165 uint8_t prefix_len,
166 uint32_t lifetime_preferred,
167 uint32_t lifetime_valid) {
168 sd_radv *radv = link->radv;
169 int r;
170 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
171
172 r = sd_radv_prefix_new(&p);
173 if (r < 0)
174 return r;
175
176 r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
177 if (r < 0)
178 return r;
179
180 r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred);
181 if (r < 0)
182 return r;
183
184 r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid);
185 if (r < 0)
186 return r;
187
188 r = sd_radv_stop(radv);
189 if (r < 0)
190 return r;
191
192 r = sd_radv_add_prefix(radv, p, true);
193 if (r < 0 && r != -EEXIST)
194 return r;
195
196 r = dhcp6_prefix_add(link->manager, prefix, link);
197 if (r < 0)
198 return r;
199
200 if (link->network->dhcp6_pd_assign_prefix) {
201 r = dhcp6_assign_delegated_prefix(link, prefix, prefix_len, lifetime_preferred, lifetime_valid);
202 if (r < 0)
203 return r;
204 }
205
206 return sd_radv_start(radv);
207 }
208
209 static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
210 int r;
211
212 assert(link);
213
214 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
215 return 1;
216
217 r = sd_netlink_message_get_errno(m);
218 if (r < 0)
219 log_link_message_warning_errno(link, m, r, "Received error on unreachable route removal for DHCPv6 delegated subnet");
220
221 return 1;
222 }
223
224 int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) {
225 int r;
226 sd_dhcp6_lease *lease;
227 union in_addr_union pd_prefix;
228 uint8_t pd_prefix_len;
229 uint32_t lifetime_preferred, lifetime_valid;
230
231 r = sd_dhcp6_client_get_lease(client, &lease);
232 if (r < 0)
233 return r;
234
235 sd_dhcp6_lease_reset_pd_prefix_iter(lease);
236
237 while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
238 &lifetime_preferred,
239 &lifetime_valid) >= 0) {
240 _cleanup_free_ char *buf = NULL;
241 _cleanup_(route_freep) Route *route = NULL;
242
243 if (pd_prefix_len >= 64)
244 continue;
245
246 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
247
248 r = route_new(&route);
249 if (r < 0)
250 return r;
251
252 route->family = AF_INET6;
253 route->dst = pd_prefix;
254 route->dst_prefixlen = pd_prefix_len;
255 route->type = RTN_UNREACHABLE;
256
257 r = route_remove(route, link, dhcp6_route_remove_handler);
258 if (r < 0) {
259 log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m",
260 strnull(buf),
261 pd_prefix_len);
262 continue;
263 }
264
265 log_link_debug(link, "Removing unreachable route %s/%u",
266 strnull(buf), pd_prefix_len);
267 }
268
269 return 0;
270 }
271
272 static int dhcp6_pd_prefix_distribute(Link *dhcp6_link,
273 struct in6_addr *pd_prefix,
274 uint8_t pd_prefix_len,
275 uint32_t lifetime_preferred,
276 uint32_t lifetime_valid,
277 bool assign_preferred_subnet_id) {
278 Iterator i;
279 Link *link;
280 Manager *manager = dhcp6_link->manager;
281 union in_addr_union prefix = {
282 .in6 = *pd_prefix,
283 };
284 uint64_t n_prefixes;
285 _cleanup_free_ char *buf = NULL;
286 _cleanup_free_ char *assigned_buf = NULL;
287 int r;
288 bool pool_depleted = false;
289
290 assert(manager);
291 assert(pd_prefix_len <= 64);
292
293 r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len);
294 if (r < 0)
295 return r;
296
297 n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
298
299 (void) in_addr_to_string(AF_INET6, &prefix, &buf);
300 log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u",
301 n_prefixes, strnull(buf), pd_prefix_len);
302
303 HASHMAP_FOREACH(link, manager->links, i) {
304 union in_addr_union assigned_prefix;
305
306 if (link == dhcp6_link)
307 continue;
308
309 if (!dhcp6_get_prefix_delegation(link))
310 continue;
311
312 if (dhcp6_link_has_dhcpv6_prefix(link))
313 continue;
314
315 if (assign_preferred_subnet_id != dhcp6_has_preferred_subnet_id(link))
316 continue;
317
318 r = dhcp6_get_preferred_delegated_prefix(manager, link, &prefix.in6, pd_prefix_len,
319 &assigned_prefix.in6);
320
321 if (assign_preferred_subnet_id && r == -EAGAIN) {
322 /* A link has a preferred subnet_id but that one is
323 * already taken by another link. Now all the remaining
324 * links will also not obtain a prefix. */
325 pool_depleted = true;
326 continue;
327 } else if (r < 0)
328 return r;
329
330 (void) in_addr_to_string(AF_INET6, &assigned_prefix, &assigned_buf);
331 r = dhcp6_pd_prefix_assign(link, &assigned_prefix.in6, 64,
332 lifetime_preferred, lifetime_valid);
333 if (r < 0) {
334 log_link_error_errno(link, r, "Unable to assign/update prefix %s/64 from %s/%u for link: %m",
335 strnull(assigned_buf),
336 strnull(buf), pd_prefix_len);
337 } else
338 log_link_debug(link, "Assigned prefix %s/64 from %s/%u to link",
339 strnull(assigned_buf),
340 strnull(buf), pd_prefix_len);
341 }
342
343 /* If one of the link requests couldn't be fulfilled, signal that we
344 should try again with another prefix. */
345 if (pool_depleted)
346 return -EAGAIN;
347
348 return 0;
349 }
350
351 static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
352 int r;
353
354 assert(link);
355
356 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
357 return 1;
358
359 r = sd_netlink_message_get_errno(m);
360 if (r < 0 && r != -EEXIST)
361 log_link_message_warning_errno(link, m, r, "Received error when adding unreachable route for DHCPv6 delegated subnet");
362
363 return 1;
364 }
365
366 static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
367 int r;
368 sd_dhcp6_lease *lease;
369 union in_addr_union pd_prefix;
370 uint8_t pd_prefix_len;
371 uint32_t lifetime_preferred, lifetime_valid;
372
373 r = sd_dhcp6_client_get_lease(client, &lease);
374 if (r < 0)
375 return r;
376
377 sd_dhcp6_lease_reset_pd_prefix_iter(lease);
378
379 while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
380 &lifetime_preferred,
381 &lifetime_valid) >= 0) {
382
383 _cleanup_free_ char *buf = NULL;
384
385 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
386
387 if (pd_prefix_len > 64) {
388 log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
389 strnull(buf), pd_prefix_len);
390 continue;
391 }
392
393 if (pd_prefix_len < 48)
394 log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u",
395 strnull(buf), pd_prefix_len);
396
397 if (pd_prefix_len < 64) {
398 _cleanup_(route_freep) Route *route = NULL;
399
400 r = route_new(&route);
401 if (r < 0)
402 return r;
403
404 route->family = AF_INET6;
405 route->dst = pd_prefix;
406 route->dst_prefixlen = pd_prefix_len;
407 route->table = link_get_dhcp_route_table(link);
408 route->type = RTN_UNREACHABLE;
409
410 r = route_configure(route, link, dhcp6_route_handler);
411 if (r < 0) {
412 log_link_warning_errno(link, r, "Cannot configure unreachable route for delegated subnet %s/%u: %m",
413 strnull(buf),
414 pd_prefix_len);
415 continue;
416 }
417
418 log_link_debug(link, "Configuring unreachable route for %s/%u",
419 strnull(buf), pd_prefix_len);
420 } else
421 log_link_debug(link, "Not adding a blocking route since distributed prefix is /64");
422
423 /* We are doing prefix allocation in two steps:
424 * 1. all those links that have a preferred subnet id will be assigned their subnet
425 * 2. all those links that remain will receive prefixes in sequential
426 * order. Prefixes that were previously already allocated to another
427 * link will be skipped.
428
429 * If a subnet id request couldn't be fulfilled the failure will be logged (as error)
430 * and no further attempts at obtaining a prefix will be made.
431
432 * The assignment has to be split in two phases since subnet id
433 * preferences should be honored. Meaning that any subnet id should be
434 * handed out to the requesting link and not to some link that didn't
435 * specify any preference. */
436
437 r = dhcp6_pd_prefix_distribute(link, &pd_prefix.in6,
438 pd_prefix_len,
439 lifetime_preferred,
440 lifetime_valid,
441 true);
442 if (r < 0 && r != -EAGAIN)
443 return r;
444
445 /* if r == -EAGAIN then the allocation failed because we ran
446 * out of addresses for the preferred subnet id's. This doesn't
447 * mean we can't fulfill other prefix requests.
448 *
449 * Since we do not have dedicated lists of links that request
450 * specific subnet id's and those that accept any prefix we
451 * *must* reset the iterator to the start as otherwise some
452 * links might not get their requested prefix. */
453
454 r = dhcp6_pd_prefix_distribute(link, &pd_prefix.in6,
455 pd_prefix_len,
456 lifetime_preferred,
457 lifetime_valid,
458 false);
459 if (r < 0 && r != -EAGAIN)
460 return r;
461
462 /* If the prefix distribution did return -EAGAIN we will try to
463 * fulfill those with the next available pd delegated prefix. */
464 }
465
466 return 0;
467 }
468
469 int dhcp6_request_prefix_delegation(Link *link) {
470 Link *l;
471 Iterator i;
472
473 assert_return(link, -EINVAL);
474 assert_return(link->manager, -EOPNOTSUPP);
475
476 if (dhcp6_get_prefix_delegation(link) <= 0)
477 return 0;
478
479 log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link");
480
481 HASHMAP_FOREACH(l, link->manager->links, i) {
482 int r, enabled;
483
484 if (l == link)
485 continue;
486
487 if (!l->dhcp6_client)
488 continue;
489
490 r = sd_dhcp6_client_get_prefix_delegation(l->dhcp6_client, &enabled);
491 if (r < 0) {
492 log_link_warning_errno(l, r, "Cannot get prefix delegation when adding new link");
493 continue;
494 }
495
496 if (enabled == 0) {
497 r = sd_dhcp6_client_set_prefix_delegation(l->dhcp6_client, 1);
498 if (r < 0) {
499 log_link_warning_errno(l, r, "Cannot enable prefix delegation when adding new link");
500 continue;
501 }
502 }
503
504 r = sd_dhcp6_client_is_running(l->dhcp6_client);
505 if (r <= 0)
506 continue;
507
508 if (enabled != 0) {
509 log_link_debug(l, "Requesting re-assignment of delegated prefixes after adding new link");
510 (void) dhcp6_lease_pd_prefix_acquired(l->dhcp6_client, l);
511
512 continue;
513 }
514
515 r = sd_dhcp6_client_stop(l->dhcp6_client);
516 if (r < 0) {
517 log_link_warning_errno(l, r, "Cannot stop DHCPv6 prefix delegation client after adding new link");
518 continue;
519 }
520
521 r = sd_dhcp6_client_start(l->dhcp6_client);
522 if (r < 0) {
523 log_link_warning_errno(l, r, "Cannot restart DHCPv6 prefix delegation client after adding new link");
524 continue;
525 }
526
527 log_link_debug(l, "Restarted DHCPv6 client to acquire prefix delegations after adding new link");
528 }
529
530 return 0;
531 }
532
533 static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
534 int r;
535
536 assert(link);
537
538 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
539 return 1;
540
541 r = sd_netlink_message_get_errno(m);
542 if (r < 0 && r != -EEXIST) {
543 log_link_message_warning_errno(link, m, r, "Could not set DHCPv6 address");
544 link_enter_failed(link);
545 return 1;
546 } else if (r >= 0)
547 (void) manager_rtnl_process_address(rtnl, m, link->manager);
548
549 r = link_request_set_routes(link);
550 if (r < 0) {
551 link_enter_failed(link);
552 return 1;
553 }
554
555 return 1;
556 }
557
558 static int dhcp6_address_change(
559 Link *link,
560 struct in6_addr *ip6_addr,
561 uint32_t lifetime_preferred,
562 uint32_t lifetime_valid) {
563
564 _cleanup_(address_freep) Address *addr = NULL;
565 _cleanup_free_ char *buffer = NULL;
566 int r;
567
568 r = address_new(&addr);
569 if (r < 0)
570 return r;
571
572 addr->family = AF_INET6;
573 addr->in_addr.in6 = *ip6_addr;
574 addr->flags = IFA_F_NOPREFIXROUTE;
575 addr->prefixlen = 128;
576 addr->cinfo.ifa_prefered = lifetime_preferred;
577 addr->cinfo.ifa_valid = lifetime_valid;
578
579 (void) in_addr_to_string(addr->family, &addr->in_addr, &buffer);
580 log_link_info(link,
581 "DHCPv6 address %s/%d timeout preferred %d valid %d",
582 strnull(buffer), addr->prefixlen, lifetime_preferred, lifetime_valid);
583
584 r = address_configure(addr, link, dhcp6_address_handler, true);
585 if (r < 0)
586 return log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
587
588 return 0;
589 }
590
591 static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
592 int r;
593 sd_dhcp6_lease *lease;
594 struct in6_addr ip6_addr;
595 uint32_t lifetime_preferred, lifetime_valid;
596
597 r = sd_dhcp6_client_get_lease(client, &lease);
598 if (r < 0)
599 return r;
600
601 sd_dhcp6_lease_reset_address_iter(lease);
602
603 while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
604 &lifetime_preferred,
605 &lifetime_valid) >= 0) {
606
607 r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid);
608 if (r < 0)
609 return r;
610 }
611
612 return 0;
613 }
614
615 static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
616 int r;
617 Link *link = userdata;
618
619 assert(link);
620 assert(link->network);
621
622 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
623 return;
624
625 switch(event) {
626 case SD_DHCP6_CLIENT_EVENT_STOP:
627 case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
628 case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
629 if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
630 log_link_warning(link, "DHCPv6 lease lost");
631
632 (void) dhcp6_lease_pd_prefix_lost(client, link);
633 (void) dhcp6_prefix_remove_all(link->manager, link);
634
635 link_dirty(link);
636 link->dhcp6_configured = false;
637 break;
638
639 case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
640 r = dhcp6_lease_address_acquired(client, link);
641 if (r < 0) {
642 link_enter_failed(link);
643 return;
644 }
645
646 r = dhcp6_lease_pd_prefix_acquired(client, link);
647 if (r < 0)
648 log_link_debug(link, "DHCPv6 did not receive prefixes to delegate");
649
650 _fallthrough_;
651 case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
652 r = dhcp6_lease_information_acquired(client, link);
653 if (r < 0) {
654 link_enter_failed(link);
655 return;
656 }
657
658 link_dirty(link);
659 link->dhcp6_configured = true;
660 break;
661
662 default:
663 if (event < 0)
664 log_link_warning_errno(link, event, "DHCPv6 error: %m");
665 else
666 log_link_warning(link, "DHCPv6 unknown event: %d", event);
667 return;
668 }
669
670 link_check_ready(link);
671 }
672
673 int dhcp6_request_address(Link *link, int ir) {
674 int r, inf_req, pd;
675 bool running;
676
677 assert(link);
678 assert(link->dhcp6_client);
679 assert(link->network);
680 assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
681
682 r = sd_dhcp6_client_is_running(link->dhcp6_client);
683 if (r < 0)
684 return r;
685 running = r;
686
687 r = sd_dhcp6_client_get_prefix_delegation(link->dhcp6_client, &pd);
688 if (r < 0)
689 return r;
690
691 if (pd && ir && link->network->dhcp6_force_pd_other_information) {
692 log_link_debug(link, "Enabling managed mode to request DHCPv6 PD with 'Other Information' set");
693
694 r = sd_dhcp6_client_set_address_request(link->dhcp6_client,
695 false);
696 if (r < 0 )
697 return r;
698
699 ir = false;
700 }
701
702 if (running) {
703 r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
704 if (r < 0)
705 return r;
706
707 if (inf_req == ir)
708 return 0;
709
710 r = sd_dhcp6_client_stop(link->dhcp6_client);
711 if (r < 0)
712 return r;
713 } else {
714 r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
715 if (r < 0)
716 return r;
717 }
718
719 r = sd_dhcp6_client_set_information_request(link->dhcp6_client, ir);
720 if (r < 0)
721 return r;
722
723 r = sd_dhcp6_client_start(link->dhcp6_client);
724 if (r < 0)
725 return r;
726
727 return 0;
728 }
729
730 static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
731 _cleanup_free_ char *hostname = NULL;
732 const char *hn;
733 int r;
734
735 assert(link);
736
737 if (!link->network->dhcp_send_hostname)
738 hn = NULL;
739 else if (link->network->dhcp_hostname)
740 hn = link->network->dhcp_hostname;
741 else {
742 r = gethostname_strict(&hostname);
743 if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
744 return r;
745
746 hn = hostname;
747 }
748
749 r = sd_dhcp6_client_set_fqdn(client, hn);
750 if (r == -EINVAL && hostname)
751 /* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */
752 log_link_warning_errno(link, r, "DHCP6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
753 else if (r < 0)
754 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set hostname: %m");
755
756 return 0;
757 }
758
759 int dhcp6_configure(Link *link) {
760 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
761 sd_dhcp6_option *vendor_option;
762 sd_dhcp6_option *send_option;
763 void *request_options;
764 const DUID *duid;
765 Iterator i;
766 int r;
767
768 assert(link);
769 assert(link->network);
770
771 if (link->dhcp6_client)
772 return 0;
773
774 r = sd_dhcp6_client_new(&client);
775 if (r == -ENOMEM)
776 return log_oom();
777 if (r < 0)
778 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m");
779
780 r = sd_dhcp6_client_attach_event(client, NULL, 0);
781 if (r < 0)
782 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m");
783
784 r = sd_dhcp6_client_set_mac(client,
785 (const uint8_t *) &link->mac,
786 sizeof (link->mac), ARPHRD_ETHER);
787 if (r < 0)
788 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MAC address: %m");
789
790 if (link->network->iaid_set) {
791 r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
792 if (r < 0)
793 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m");
794 }
795
796 duid = link_get_duid(link);
797 if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
798 r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
799 else
800 r = sd_dhcp6_client_set_duid(client,
801 duid->type,
802 duid->raw_data_len > 0 ? duid->raw_data : NULL,
803 duid->raw_data_len);
804 if (r < 0)
805 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m");
806
807 ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp6_client_send_options, i) {
808 r = sd_dhcp6_client_add_option(client, send_option);
809 if (r == -EEXIST)
810 continue;
811 if (r < 0)
812 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set option: %m");
813 }
814
815 r = dhcp6_set_hostname(client, link);
816 if (r < 0)
817 return r;
818
819 r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
820 if (r < 0)
821 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set ifindex: %m");
822
823 if (link->network->rapid_commit) {
824 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT);
825 if (r < 0)
826 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m");
827 }
828
829 if (link->network->dhcp6_mudurl) {
830 r = sd_dhcp6_client_set_request_mud_url(client, link->network->dhcp6_mudurl);
831 if (r < 0)
832 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MUD URL: %m");
833 }
834
835 SET_FOREACH(request_options, link->network->dhcp6_request_options, i) {
836 uint32_t option = PTR_TO_UINT32(request_options);
837
838 r = sd_dhcp6_client_set_request_option(client, option);
839 if (r == -EEXIST) {
840 log_link_debug(link, "DHCP6 CLIENT: Failed to set request flag for '%u' already exists, ignoring.", option);
841 continue;
842 }
843
844 if (r < 0)
845 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for '%u': %m", option);
846 }
847
848 if (link->network->dhcp6_user_class) {
849 r = sd_dhcp6_client_set_request_user_class(client, link->network->dhcp6_user_class);
850 if (r < 0)
851 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set user class: %m");
852 }
853
854 if (link->network->dhcp6_vendor_class) {
855 r = sd_dhcp6_client_set_request_vendor_class(client, link->network->dhcp6_vendor_class);
856 if (r < 0)
857 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set vendor class: %m");
858 }
859
860 ORDERED_HASHMAP_FOREACH(vendor_option, link->network->dhcp6_client_send_vendor_options, i) {
861 r = sd_dhcp6_client_add_vendor_option(client, vendor_option);
862 if (r == -EEXIST)
863 continue;
864 if (r < 0)
865 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set vendor option: %m");
866 }
867
868 r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
869 if (r < 0)
870 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m");
871
872 if (dhcp6_enable_prefix_delegation(link)) {
873 r = sd_dhcp6_client_set_prefix_delegation(client, true);
874 if (r < 0)
875 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix delegation: %m");
876 }
877
878 if (link->network->dhcp6_pd_length > 0) {
879 r = sd_dhcp6_client_set_prefix_delegation_hint(client, link->network->dhcp6_pd_length, &link->network->dhcp6_pd_address);
880 if (r < 0)
881 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix hint: %m");
882 }
883
884 link->dhcp6_client = TAKE_PTR(client);
885
886 return 0;
887 }
888
889 static Link *dhcp6_prefix_get(Manager *m, struct in6_addr *addr) {
890 assert_return(m, NULL);
891 assert_return(addr, NULL);
892
893 return hashmap_get(m->dhcp6_prefixes, addr);
894 }
895
896 static int dhcp6_route_add_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
897 int r;
898
899 assert(link);
900
901 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
902 return 1;
903
904 r = sd_netlink_message_get_errno(m);
905 if (r < 0 && r != -EEXIST) {
906 log_link_message_warning_errno(link, m, r, "Received error adding DHCPv6 Prefix Delegation route");
907 link_enter_failed(link);
908 return 1;
909 }
910
911 return 1;
912 }
913
914 static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
915 _cleanup_(route_freep) Route *route = NULL;
916 _cleanup_free_ struct in6_addr *a = NULL;
917 _cleanup_free_ char *buf = NULL;
918 Link *assigned_link;
919 int r;
920
921 assert_return(m, -EINVAL);
922 assert_return(addr, -EINVAL);
923
924 r = route_new(&route);
925 if (r < 0)
926 return r;
927
928 route->family = AF_INET6;
929 route->dst.in6 = *addr;
930 route->dst_prefixlen = 64;
931
932 r = route_configure(route, link, dhcp6_route_add_handler);
933 if (r < 0)
934 return r;
935
936 (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
937 log_link_debug(link, "Adding prefix route %s/64", strnull(buf));
938
939 assigned_link = hashmap_get(m->dhcp6_prefixes, addr);
940 if (assigned_link) {
941 assert(assigned_link == link);
942 return 0;
943 }
944
945 a = newdup(struct in6_addr, addr, 1);
946 if (!a)
947 return -ENOMEM;
948
949 r = hashmap_ensure_allocated(&m->dhcp6_prefixes, &in6_addr_hash_ops);
950 if (r < 0)
951 return r;
952
953 r = hashmap_put(m->dhcp6_prefixes, a, link);
954 if (r < 0)
955 return r;
956
957 TAKE_PTR(a);
958 link_ref(link);
959 return 0;
960 }
961
962 static int dhcp6_prefix_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
963 int r;
964
965 assert(link);
966
967 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
968 return 1;
969
970 r = sd_netlink_message_get_errno(m);
971 if (r < 0) {
972 log_link_message_warning_errno(link, m, r, "Received error on DHCPv6 Prefix Delegation route removal");
973 link_enter_failed(link);
974 return 1;
975 }
976
977 return 1;
978 }
979
980 int dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
981 _cleanup_free_ struct in6_addr *a = NULL;
982 _cleanup_(link_unrefp) Link *l = NULL;
983 _cleanup_(route_freep) Route *route = NULL;
984 _cleanup_free_ char *buf = NULL;
985 int r;
986
987 assert_return(m, -EINVAL);
988 assert_return(addr, -EINVAL);
989
990 l = hashmap_remove2(m->dhcp6_prefixes, addr, (void **) &a);
991 if (!l)
992 return -EINVAL;
993
994 (void) sd_radv_remove_prefix(l->radv, addr, 64);
995
996 r = route_new(&route);
997 if (r < 0)
998 return r;
999
1000 route->family = AF_INET6;
1001 route->dst.in6 = *addr;
1002 route->dst_prefixlen = 64;
1003
1004 r = route_remove(route, l, dhcp6_prefix_remove_handler);
1005 if (r < 0)
1006 return r;
1007
1008 (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
1009 log_link_debug(l, "Removing prefix route %s/64", strnull(buf));
1010
1011 return 0;
1012 }
1013
1014 static int dhcp6_prefix_remove_all(Manager *m, Link *link) {
1015 struct in6_addr *addr;
1016 Iterator i;
1017 Link *l;
1018
1019 assert_return(m, -EINVAL);
1020 assert_return(link, -EINVAL);
1021
1022 HASHMAP_FOREACH_KEY(l, addr, m->dhcp6_prefixes, i)
1023 if (l == link)
1024 (void) dhcp6_prefix_remove(m, addr);
1025
1026 return 0;
1027 }
1028
1029 static bool dhcp6_link_has_dhcpv6_prefix(Link *link) {
1030 Iterator i;
1031 Link *l;
1032
1033 assert(link);
1034 assert(link->manager);
1035
1036 HASHMAP_FOREACH(l, link->manager->dhcp6_prefixes, i)
1037 if (link == l)
1038 return true;
1039
1040 return false;
1041 }
1042
1043 static int dhcp6_assign_delegated_prefix(Link *link,
1044 const struct in6_addr *prefix,
1045 uint8_t prefix_len,
1046 uint32_t lifetime_preferred,
1047 uint32_t lifetime_valid) {
1048 _cleanup_(address_freep) Address *address = NULL;
1049 int r;
1050
1051 assert(link);
1052 assert(link->network);
1053 assert(prefix);
1054
1055 if (!link->network->dhcp6_pd_assign_prefix)
1056 return 0;
1057
1058 r = address_new(&address);
1059 if (r < 0)
1060 return log_link_error_errno(link, r, "Could not allocate address: %m");
1061
1062 address->in_addr.in6 = *prefix;
1063 r = generate_ipv6_eui_64_address(link, &address->in_addr.in6);
1064 if (r < 0)
1065 return log_link_warning_errno(link, r, "Failed to generate EUI64 address for DHCPv6 acquired delegated prefix: %m");
1066
1067 address->prefixlen = prefix_len;
1068 address->family = AF_INET6;
1069 address->cinfo.ifa_prefered = lifetime_preferred;
1070 address->cinfo.ifa_valid = lifetime_valid;
1071
1072 link_set_state(link, LINK_STATE_CONFIGURING);
1073
1074 r = address_configure(address, link, address_handler, true);
1075 if (r < 0)
1076 return log_link_warning_errno(link, r, "Could not set addresses: %m");
1077 if (r > 0)
1078 link->address_messages++;
1079
1080 return 0;
1081 }
1082
1083 int config_parse_dhcp6_pd_hint(
1084 const char* unit,
1085 const char *filename,
1086 unsigned line,
1087 const char *section,
1088 unsigned section_line,
1089 const char *lvalue,
1090 int ltype,
1091 const char *rvalue,
1092 void *data,
1093 void *userdata) {
1094
1095 Network *network = data;
1096 int r;
1097
1098 assert(filename);
1099 assert(lvalue);
1100 assert(rvalue);
1101 assert(data);
1102
1103 r = in_addr_prefix_from_string(rvalue, AF_INET6, (union in_addr_union *) &network->dhcp6_pd_address, &network->dhcp6_pd_length);
1104 if (r < 0) {
1105 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse PrefixDelegationHint=%s, ignoring assignment", rvalue);
1106 return 0;
1107 }
1108
1109 if (network->dhcp6_pd_length < 1 || network->dhcp6_pd_length > 128) {
1110 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid prefix length='%d', ignoring assignment", network->dhcp6_pd_length);
1111 network->dhcp6_pd_length = 0;
1112 return 0;
1113 }
1114
1115 return 0;
1116 }
1117
1118 int config_parse_dhcp6_mud_url(
1119 const char *unit,
1120 const char *filename,
1121 unsigned line,
1122 const char *section,
1123 unsigned section_line,
1124 const char *lvalue,
1125 int ltype,
1126 const char *rvalue,
1127 void *data,
1128 void *userdata) {
1129 _cleanup_free_ char *unescaped = NULL;
1130 Network *network = data;
1131 int r;
1132
1133 assert(filename);
1134 assert(lvalue);
1135 assert(rvalue);
1136
1137 if (isempty(rvalue)) {
1138 network->dhcp6_mudurl = mfree(network->dhcp6_mudurl);
1139 return 0;
1140 }
1141
1142 r = cunescape(rvalue, 0, &unescaped);
1143 if (r < 0) {
1144 log_syntax(unit, LOG_ERR, filename, line, r,
1145 "Failed to Failed to unescape MUD URL, ignoring: %s", rvalue);
1146 return 0;
1147 }
1148
1149 if (!http_url_is_valid(unescaped) || strlen(unescaped) > UINT8_MAX) {
1150 log_syntax(unit, LOG_ERR, filename, line, 0,
1151 "Failed to parse MUD URL '%s', ignoring: %m", rvalue);
1152
1153 return 0;
1154 }
1155
1156 return free_and_replace(network->dhcp6_mudurl, unescaped);
1157 }