1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2014 Intel Corporation. All rights reserved.
6 #include <netinet/in.h>
8 #include <linux/if_arp.h>
11 #include "sd-dhcp6-client.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"
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
,
33 uint32_t lifetime_preferred
,
34 uint32_t lifetime_valid
);
36 static bool dhcp6_get_prefix_delegation(Link
*link
) {
40 return IN_SET(link
->network
->router_prefix_delegation
,
41 RADV_PREFIX_DELEGATION_DHCP6
,
42 RADV_PREFIX_DELEGATION_BOTH
);
45 static bool dhcp6_has_preferred_subnet_id(Link
*link
) {
49 return link
->network
->router_prefix_subnet_id
>= 0;
52 static int dhcp6_get_preferred_delegated_prefix(
55 const struct in6_addr
*pd_prefix
,
56 uint8_t pd_prefix_len
,
57 struct in6_addr
*ret_addr
) {
59 union in_addr_union pd_prefix_union
= {
62 int64_t subnet_id
= link
->network
->router_prefix_subnet_id
;
64 assert(pd_prefix_len
<= 64);
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
;
70 /* We start off with the original PD prefix we have been assigned and
71 * iterate from there */
72 union in_addr_union prefix
= {
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.",
85 r
= in_addr_prefix_nth(AF_INET6
, &prefix
, 64, subnet_id
);
87 return log_link_debug_errno(link
,
89 "subnet id %" PRIi64
" is out of range. Only have %" PRIu64
" subnets.",
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);
97 Link
* assigned_link
= dhcp6_prefix_get(manager
, &prefix
.in6
);
99 (void) in_addr_to_string(AF_INET6
, &prefix
, &assigned_buf
);
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
));
107 *ret_addr
= prefix
.in6
;
109 log_link_debug(link
, "The requested prefix %s is available. Using it.",
110 strnull(assigned_buf
));
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
);
118 if (!assigned_link
|| assigned_link
== link
) {
119 *ret_addr
= prefix
.in6
;
123 r
= in_addr_prefix_next(AF_INET6
, &prefix
, 64);
125 return log_link_error_errno(link
,
127 "Can't allocate another prefix. Out of address space?");
130 log_link_warning(link
, "Couldn't find a suitable prefix. Ran out of address space.");
136 static bool dhcp6_enable_prefix_delegation(Link
*dhcp6_link
) {
143 manager
= dhcp6_link
->manager
;
146 HASHMAP_FOREACH(l
, manager
->links
, i
) {
150 if (!dhcp6_get_prefix_delegation(l
))
159 static int dhcp6_lease_information_acquired(sd_dhcp6_client
*client
,
164 static int dhcp6_pd_prefix_assign(Link
*link
, struct in6_addr
*prefix
,
166 uint32_t lifetime_preferred
,
167 uint32_t lifetime_valid
) {
168 sd_radv
*radv
= link
->radv
;
170 _cleanup_(sd_radv_prefix_unrefp
) sd_radv_prefix
*p
= NULL
;
172 r
= sd_radv_prefix_new(&p
);
176 r
= sd_radv_prefix_set_prefix(p
, prefix
, prefix_len
);
180 r
= sd_radv_prefix_set_preferred_lifetime(p
, lifetime_preferred
);
184 r
= sd_radv_prefix_set_valid_lifetime(p
, lifetime_valid
);
188 r
= sd_radv_stop(radv
);
192 r
= sd_radv_add_prefix(radv
, p
, true);
193 if (r
< 0 && r
!= -EEXIST
)
196 r
= dhcp6_prefix_add(link
->manager
, prefix
, link
);
200 if (link
->network
->dhcp6_pd_assign_prefix
) {
201 r
= dhcp6_assign_delegated_prefix(link
, prefix
, prefix_len
, lifetime_preferred
, lifetime_valid
);
206 return sd_radv_start(radv
);
209 static int dhcp6_route_remove_handler(sd_netlink
*nl
, sd_netlink_message
*m
, Link
*link
) {
214 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
217 r
= sd_netlink_message_get_errno(m
);
219 log_link_message_warning_errno(link
, m
, r
, "Received error on unreachable route removal for DHCPv6 delegated subnet");
224 int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client
*client
, Link
* link
) {
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
;
231 r
= sd_dhcp6_client_get_lease(client
, &lease
);
235 sd_dhcp6_lease_reset_pd_prefix_iter(lease
);
237 while (sd_dhcp6_lease_get_pd(lease
, &pd_prefix
.in6
, &pd_prefix_len
,
239 &lifetime_valid
) >= 0) {
240 _cleanup_free_
char *buf
= NULL
;
241 _cleanup_(route_freep
) Route
*route
= NULL
;
243 if (pd_prefix_len
>= 64)
246 (void) in_addr_to_string(AF_INET6
, &pd_prefix
, &buf
);
248 r
= route_new(&route
);
252 route
->family
= AF_INET6
;
253 route
->dst
= pd_prefix
;
254 route
->dst_prefixlen
= pd_prefix_len
;
255 route
->type
= RTN_UNREACHABLE
;
257 r
= route_remove(route
, link
, dhcp6_route_remove_handler
);
259 log_link_warning_errno(link
, r
, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m",
265 log_link_debug(link
, "Removing unreachable route %s/%u",
266 strnull(buf
), pd_prefix_len
);
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
) {
280 Manager
*manager
= dhcp6_link
->manager
;
281 union in_addr_union prefix
= {
285 _cleanup_free_
char *buf
= NULL
;
286 _cleanup_free_
char *assigned_buf
= NULL
;
288 bool pool_depleted
= false;
291 assert(pd_prefix_len
<= 64);
293 r
= in_addr_mask(AF_INET6
, &prefix
, pd_prefix_len
);
297 n_prefixes
= UINT64_C(1) << (64 - pd_prefix_len
);
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
);
303 HASHMAP_FOREACH(link
, manager
->links
, i
) {
304 union in_addr_union assigned_prefix
;
306 if (link
== dhcp6_link
)
309 if (!dhcp6_get_prefix_delegation(link
))
312 if (dhcp6_link_has_dhcpv6_prefix(link
))
315 if (assign_preferred_subnet_id
!= dhcp6_has_preferred_subnet_id(link
))
318 r
= dhcp6_get_preferred_delegated_prefix(manager
, link
, &prefix
.in6
, pd_prefix_len
,
319 &assigned_prefix
.in6
);
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;
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
);
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
);
338 log_link_debug(link
, "Assigned prefix %s/64 from %s/%u to link",
339 strnull(assigned_buf
),
340 strnull(buf
), pd_prefix_len
);
343 /* If one of the link requests couldn't be fulfilled, signal that we
344 should try again with another prefix. */
351 static int dhcp6_route_handler(sd_netlink
*nl
, sd_netlink_message
*m
, Link
*link
) {
356 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
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");
366 static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client
*client
, Link
*link
) {
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
;
373 r
= sd_dhcp6_client_get_lease(client
, &lease
);
377 sd_dhcp6_lease_reset_pd_prefix_iter(lease
);
379 while (sd_dhcp6_lease_get_pd(lease
, &pd_prefix
.in6
, &pd_prefix_len
,
381 &lifetime_valid
) >= 0) {
383 _cleanup_free_
char *buf
= NULL
;
385 (void) in_addr_to_string(AF_INET6
, &pd_prefix
, &buf
);
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
);
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
);
397 if (pd_prefix_len
< 64) {
398 _cleanup_(route_freep
) Route
*route
= NULL
;
400 r
= route_new(&route
);
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
;
410 r
= route_configure(route
, link
, dhcp6_route_handler
);
412 log_link_warning_errno(link
, r
, "Cannot configure unreachable route for delegated subnet %s/%u: %m",
418 log_link_debug(link
, "Configuring unreachable route for %s/%u",
419 strnull(buf
), pd_prefix_len
);
421 log_link_debug(link
, "Not adding a blocking route since distributed prefix is /64");
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.
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.
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. */
437 r
= dhcp6_pd_prefix_distribute(link
, &pd_prefix
.in6
,
442 if (r
< 0 && r
!= -EAGAIN
)
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.
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. */
454 r
= dhcp6_pd_prefix_distribute(link
, &pd_prefix
.in6
,
459 if (r
< 0 && r
!= -EAGAIN
)
462 /* If the prefix distribution did return -EAGAIN we will try to
463 * fulfill those with the next available pd delegated prefix. */
469 int dhcp6_request_prefix_delegation(Link
*link
) {
473 assert_return(link
, -EINVAL
);
474 assert_return(link
->manager
, -EOPNOTSUPP
);
476 if (dhcp6_get_prefix_delegation(link
) <= 0)
479 log_link_debug(link
, "Requesting DHCPv6 prefixes to be delegated for new link");
481 HASHMAP_FOREACH(l
, link
->manager
->links
, i
) {
487 if (!l
->dhcp6_client
)
490 r
= sd_dhcp6_client_get_prefix_delegation(l
->dhcp6_client
, &enabled
);
492 log_link_warning_errno(l
, r
, "Cannot get prefix delegation when adding new link");
497 r
= sd_dhcp6_client_set_prefix_delegation(l
->dhcp6_client
, 1);
499 log_link_warning_errno(l
, r
, "Cannot enable prefix delegation when adding new link");
504 r
= sd_dhcp6_client_is_running(l
->dhcp6_client
);
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
);
515 r
= sd_dhcp6_client_stop(l
->dhcp6_client
);
517 log_link_warning_errno(l
, r
, "Cannot stop DHCPv6 prefix delegation client after adding new link");
521 r
= sd_dhcp6_client_start(l
->dhcp6_client
);
523 log_link_warning_errno(l
, r
, "Cannot restart DHCPv6 prefix delegation client after adding new link");
527 log_link_debug(l
, "Restarted DHCPv6 client to acquire prefix delegations after adding new link");
533 static int dhcp6_address_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Link
*link
) {
538 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
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
);
547 (void) manager_rtnl_process_address(rtnl
, m
, link
->manager
);
549 r
= link_request_set_routes(link
);
551 link_enter_failed(link
);
558 static int dhcp6_address_change(
560 struct in6_addr
*ip6_addr
,
561 uint32_t lifetime_preferred
,
562 uint32_t lifetime_valid
) {
564 _cleanup_(address_freep
) Address
*addr
= NULL
;
565 _cleanup_free_
char *buffer
= NULL
;
568 r
= address_new(&addr
);
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
;
579 (void) in_addr_to_string(addr
->family
, &addr
->in_addr
, &buffer
);
581 "DHCPv6 address %s/%d timeout preferred %d valid %d",
582 strnull(buffer
), addr
->prefixlen
, lifetime_preferred
, lifetime_valid
);
584 r
= address_configure(addr
, link
, dhcp6_address_handler
, true);
586 return log_link_warning_errno(link
, r
, "Could not assign DHCPv6 address: %m");
591 static int dhcp6_lease_address_acquired(sd_dhcp6_client
*client
, Link
*link
) {
593 sd_dhcp6_lease
*lease
;
594 struct in6_addr ip6_addr
;
595 uint32_t lifetime_preferred
, lifetime_valid
;
597 r
= sd_dhcp6_client_get_lease(client
, &lease
);
601 sd_dhcp6_lease_reset_address_iter(lease
);
603 while (sd_dhcp6_lease_get_address(lease
, &ip6_addr
,
605 &lifetime_valid
) >= 0) {
607 r
= dhcp6_address_change(link
, &ip6_addr
, lifetime_preferred
, lifetime_valid
);
615 static void dhcp6_handler(sd_dhcp6_client
*client
, int event
, void *userdata
) {
617 Link
*link
= userdata
;
620 assert(link
->network
);
622 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
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");
632 (void) dhcp6_lease_pd_prefix_lost(client
, link
);
633 (void) dhcp6_prefix_remove_all(link
->manager
, link
);
636 link
->dhcp6_configured
= false;
639 case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
:
640 r
= dhcp6_lease_address_acquired(client
, link
);
642 link_enter_failed(link
);
646 r
= dhcp6_lease_pd_prefix_acquired(client
, link
);
648 log_link_debug(link
, "DHCPv6 did not receive prefixes to delegate");
651 case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
:
652 r
= dhcp6_lease_information_acquired(client
, link
);
654 link_enter_failed(link
);
659 link
->dhcp6_configured
= true;
664 log_link_warning_errno(link
, event
, "DHCPv6 error: %m");
666 log_link_warning(link
, "DHCPv6 unknown event: %d", event
);
670 link_check_ready(link
);
673 int dhcp6_request_address(Link
*link
, int ir
) {
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);
682 r
= sd_dhcp6_client_is_running(link
->dhcp6_client
);
687 r
= sd_dhcp6_client_get_prefix_delegation(link
->dhcp6_client
, &pd
);
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");
694 r
= sd_dhcp6_client_set_address_request(link
->dhcp6_client
,
703 r
= sd_dhcp6_client_get_information_request(link
->dhcp6_client
, &inf_req
);
710 r
= sd_dhcp6_client_stop(link
->dhcp6_client
);
714 r
= sd_dhcp6_client_set_local_address(link
->dhcp6_client
, &link
->ipv6ll_address
);
719 r
= sd_dhcp6_client_set_information_request(link
->dhcp6_client
, ir
);
723 r
= sd_dhcp6_client_start(link
->dhcp6_client
);
730 static int dhcp6_set_hostname(sd_dhcp6_client
*client
, Link
*link
) {
731 _cleanup_free_
char *hostname
= NULL
;
737 if (!link
->network
->dhcp_send_hostname
)
739 else if (link
->network
->dhcp_hostname
)
740 hn
= link
->network
->dhcp_hostname
;
742 r
= gethostname_strict(&hostname
);
743 if (r
< 0 && r
!= -ENXIO
) /* ENXIO: no hostname set or hostname is "localhost" */
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");
754 return log_link_error_errno(link
, r
, "DHCP6 CLIENT: Failed to set hostname: %m");
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
;
769 assert(link
->network
);
771 if (link
->dhcp6_client
)
774 r
= sd_dhcp6_client_new(&client
);
778 return log_link_error_errno(link
, r
, "DHCP6 CLIENT: Failed to create DHCP6 client: %m");
780 r
= sd_dhcp6_client_attach_event(client
, NULL
, 0);
782 return log_link_error_errno(link
, r
, "DHCP6 CLIENT: Failed to attach event: %m");
784 r
= sd_dhcp6_client_set_mac(client
,
785 (const uint8_t *) &link
->mac
,
786 sizeof (link
->mac
), ARPHRD_ETHER
);
788 return log_link_error_errno(link
, r
, "DHCP6 CLIENT: Failed to set MAC address: %m");
790 if (link
->network
->iaid_set
) {
791 r
= sd_dhcp6_client_set_iaid(client
, link
->network
->iaid
);
793 return log_link_error_errno(link
, r
, "DHCP6 CLIENT: Failed to set IAID: %m");
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
);
800 r
= sd_dhcp6_client_set_duid(client
,
802 duid
->raw_data_len
> 0 ? duid
->raw_data
: NULL
,
805 return log_link_error_errno(link
, r
, "DHCP6 CLIENT: Failed to set DUID: %m");
807 ORDERED_HASHMAP_FOREACH(send_option
, link
->network
->dhcp6_client_send_options
, i
) {
808 r
= sd_dhcp6_client_add_option(client
, send_option
);
812 return log_link_error_errno(link
, r
, "DHCP6 CLIENT: Failed to set option: %m");
815 r
= dhcp6_set_hostname(client
, link
);
819 r
= sd_dhcp6_client_set_ifindex(client
, link
->ifindex
);
821 return log_link_error_errno(link
, r
, "DHCP6 CLIENT: Failed to set ifindex: %m");
823 if (link
->network
->rapid_commit
) {
824 r
= sd_dhcp6_client_set_request_option(client
, SD_DHCP6_OPTION_RAPID_COMMIT
);
826 return log_link_error_errno(link
, r
, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m");
829 if (link
->network
->dhcp6_mudurl
) {
830 r
= sd_dhcp6_client_set_request_mud_url(client
, link
->network
->dhcp6_mudurl
);
832 return log_link_error_errno(link
, r
, "DHCP6 CLIENT: Failed to set MUD URL: %m");
835 SET_FOREACH(request_options
, link
->network
->dhcp6_request_options
, i
) {
836 uint32_t option
= PTR_TO_UINT32(request_options
);
838 r
= sd_dhcp6_client_set_request_option(client
, option
);
840 log_link_debug(link
, "DHCP6 CLIENT: Failed to set request flag for '%u' already exists, ignoring.", option
);
845 return log_link_error_errno(link
, r
, "DHCP6 CLIENT: Failed to set request flag for '%u': %m", option
);
848 if (link
->network
->dhcp6_user_class
) {
849 r
= sd_dhcp6_client_set_request_user_class(client
, link
->network
->dhcp6_user_class
);
851 return log_link_error_errno(link
, r
, "DHCP6 CLIENT: Failed to set user class: %m");
854 if (link
->network
->dhcp6_vendor_class
) {
855 r
= sd_dhcp6_client_set_request_vendor_class(client
, link
->network
->dhcp6_vendor_class
);
857 return log_link_error_errno(link
, r
, "DHCP6 CLIENT: Failed to set vendor class: %m");
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
);
865 return log_link_error_errno(link
, r
, "DHCP6 CLIENT: Failed to set vendor option: %m");
868 r
= sd_dhcp6_client_set_callback(client
, dhcp6_handler
, link
);
870 return log_link_error_errno(link
, r
, "DHCP6 CLIENT: Failed to set callback: %m");
872 if (dhcp6_enable_prefix_delegation(link
)) {
873 r
= sd_dhcp6_client_set_prefix_delegation(client
, true);
875 return log_link_error_errno(link
, r
, "DHCP6 CLIENT: Failed to set prefix delegation: %m");
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
);
881 return log_link_error_errno(link
, r
, "DHCP6 CLIENT: Failed to set prefix hint: %m");
884 link
->dhcp6_client
= TAKE_PTR(client
);
889 static Link
*dhcp6_prefix_get(Manager
*m
, struct in6_addr
*addr
) {
890 assert_return(m
, NULL
);
891 assert_return(addr
, NULL
);
893 return hashmap_get(m
->dhcp6_prefixes
, addr
);
896 static int dhcp6_route_add_handler(sd_netlink
*nl
, sd_netlink_message
*m
, Link
*link
) {
901 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
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
);
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
;
921 assert_return(m
, -EINVAL
);
922 assert_return(addr
, -EINVAL
);
924 r
= route_new(&route
);
928 route
->family
= AF_INET6
;
929 route
->dst
.in6
= *addr
;
930 route
->dst_prefixlen
= 64;
932 r
= route_configure(route
, link
, dhcp6_route_add_handler
);
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
));
939 assigned_link
= hashmap_get(m
->dhcp6_prefixes
, addr
);
941 assert(assigned_link
== link
);
945 a
= newdup(struct in6_addr
, addr
, 1);
949 r
= hashmap_ensure_allocated(&m
->dhcp6_prefixes
, &in6_addr_hash_ops
);
953 r
= hashmap_put(m
->dhcp6_prefixes
, a
, link
);
962 static int dhcp6_prefix_remove_handler(sd_netlink
*nl
, sd_netlink_message
*m
, Link
*link
) {
967 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
970 r
= sd_netlink_message_get_errno(m
);
972 log_link_message_warning_errno(link
, m
, r
, "Received error on DHCPv6 Prefix Delegation route removal");
973 link_enter_failed(link
);
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
;
987 assert_return(m
, -EINVAL
);
988 assert_return(addr
, -EINVAL
);
990 l
= hashmap_remove2(m
->dhcp6_prefixes
, addr
, (void **) &a
);
994 (void) sd_radv_remove_prefix(l
->radv
, addr
, 64);
996 r
= route_new(&route
);
1000 route
->family
= AF_INET6
;
1001 route
->dst
.in6
= *addr
;
1002 route
->dst_prefixlen
= 64;
1004 r
= route_remove(route
, l
, dhcp6_prefix_remove_handler
);
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
));
1014 static int dhcp6_prefix_remove_all(Manager
*m
, Link
*link
) {
1015 struct in6_addr
*addr
;
1019 assert_return(m
, -EINVAL
);
1020 assert_return(link
, -EINVAL
);
1022 HASHMAP_FOREACH_KEY(l
, addr
, m
->dhcp6_prefixes
, i
)
1024 (void) dhcp6_prefix_remove(m
, addr
);
1029 static bool dhcp6_link_has_dhcpv6_prefix(Link
*link
) {
1034 assert(link
->manager
);
1036 HASHMAP_FOREACH(l
, link
->manager
->dhcp6_prefixes
, i
)
1043 static int dhcp6_assign_delegated_prefix(Link
*link
,
1044 const struct in6_addr
*prefix
,
1046 uint32_t lifetime_preferred
,
1047 uint32_t lifetime_valid
) {
1048 _cleanup_(address_freep
) Address
*address
= NULL
;
1052 assert(link
->network
);
1055 if (!link
->network
->dhcp6_pd_assign_prefix
)
1058 r
= address_new(&address
);
1060 return log_link_error_errno(link
, r
, "Could not allocate address: %m");
1062 address
->in_addr
.in6
= *prefix
;
1063 r
= generate_ipv6_eui_64_address(link
, &address
->in_addr
.in6
);
1065 return log_link_warning_errno(link
, r
, "Failed to generate EUI64 address for DHCPv6 acquired delegated prefix: %m");
1067 address
->prefixlen
= prefix_len
;
1068 address
->family
= AF_INET6
;
1069 address
->cinfo
.ifa_prefered
= lifetime_preferred
;
1070 address
->cinfo
.ifa_valid
= lifetime_valid
;
1072 link_set_state(link
, LINK_STATE_CONFIGURING
);
1074 r
= address_configure(address
, link
, address_handler
, true);
1076 return log_link_warning_errno(link
, r
, "Could not set addresses: %m");
1078 link
->address_messages
++;
1083 int config_parse_dhcp6_pd_hint(
1085 const char *filename
,
1087 const char *section
,
1088 unsigned section_line
,
1095 Network
*network
= data
;
1103 r
= in_addr_prefix_from_string(rvalue
, AF_INET6
, (union in_addr_union
*) &network
->dhcp6_pd_address
, &network
->dhcp6_pd_length
);
1105 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse PrefixDelegationHint=%s, ignoring assignment", rvalue
);
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;
1118 int config_parse_dhcp6_mud_url(
1120 const char *filename
,
1122 const char *section
,
1123 unsigned section_line
,
1129 _cleanup_free_
char *unescaped
= NULL
;
1130 Network
*network
= data
;
1137 if (isempty(rvalue
)) {
1138 network
->dhcp6_mudurl
= mfree(network
->dhcp6_mudurl
);
1142 r
= cunescape(rvalue
, 0, &unescaped
);
1144 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1145 "Failed to Failed to unescape MUD URL, ignoring: %s", rvalue
);
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
);
1156 return free_and_replace(network
->dhcp6_mudurl
, unescaped
);