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