]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp6.c
tree-wide: use ASSERT_PTR more
[thirdparty/systemd.git] / src / network / networkd-dhcp6.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3 Copyright © 2014 Intel Corporation. All rights reserved.
4 ***/
5
6 #include "sd-dhcp6-client.h"
7
8 #include "hashmap.h"
9 #include "hostname-setup.h"
10 #include "hostname-util.h"
11 #include "networkd-address.h"
12 #include "networkd-dhcp-prefix-delegation.h"
13 #include "networkd-dhcp6.h"
14 #include "networkd-link.h"
15 #include "networkd-manager.h"
16 #include "networkd-queue.h"
17 #include "networkd-route.h"
18 #include "string-table.h"
19 #include "string-util.h"
20
21 bool link_dhcp6_with_address_enabled(Link *link) {
22 if (!link_dhcp6_enabled(link))
23 return false;
24
25 return link->network->dhcp6_use_address;
26 }
27
28 static DHCP6ClientStartMode link_get_dhcp6_client_start_mode(Link *link) {
29 assert(link);
30
31 if (!link->network)
32 return DHCP6_CLIENT_START_MODE_NO;
33
34 /* When WithoutRA= is explicitly specified, then honor it. */
35 if (link->network->dhcp6_client_start_mode >= 0)
36 return link->network->dhcp6_client_start_mode;
37
38 /* When this interface itself is an uplink interface, then start dhcp6 client in solicit mode. */
39 if (dhcp_pd_is_uplink(link, link, /* accept_auto = */ false))
40 return DHCP6_CLIENT_START_MODE_SOLICIT;
41
42 /* Otherwise, start dhcp6 client when RA is received. */
43 return DHCP6_CLIENT_START_MODE_NO;
44 }
45
46 static int dhcp6_remove(Link *link, bool only_marked) {
47 Address *address;
48 Route *route;
49 int k, r = 0;
50
51 assert(link);
52
53 if (!only_marked)
54 link->dhcp6_configured = false;
55
56 SET_FOREACH(route, link->routes) {
57 if (route->source != NETWORK_CONFIG_SOURCE_DHCP6)
58 continue;
59 if (only_marked && !route_is_marked(route))
60 continue;
61
62 k = route_remove(route);
63 if (k < 0)
64 r = k;
65
66 route_cancel_request(route, link);
67 }
68
69 SET_FOREACH(address, link->addresses) {
70 if (address->source != NETWORK_CONFIG_SOURCE_DHCP6)
71 continue;
72 if (only_marked && !address_is_marked(address))
73 continue;
74
75 k = address_remove(address);
76 if (k < 0)
77 r = k;
78
79 address_cancel_request(address);
80 }
81
82 return r;
83 }
84
85 static int dhcp6_address_ready_callback(Address *address) {
86 Address *a;
87
88 assert(address);
89 assert(address->link);
90
91 SET_FOREACH(a, address->link->addresses)
92 if (a->source == NETWORK_CONFIG_SOURCE_DHCP6)
93 a->callback = NULL;
94
95 return dhcp6_check_ready(address->link);
96 }
97
98 int dhcp6_check_ready(Link *link) {
99 bool has_ready = false;
100 Address *address;
101 int r;
102
103 assert(link);
104
105 if (link->dhcp6_messages > 0) {
106 log_link_debug(link, "%s(): DHCPv6 addresses and routes are not set.", __func__);
107 return 0;
108 }
109
110 SET_FOREACH(address, link->addresses) {
111 if (address->source != NETWORK_CONFIG_SOURCE_DHCP6)
112 continue;
113 if (address_is_ready(address)) {
114 has_ready = true;
115 break;
116 }
117 }
118
119 if (!has_ready) {
120 SET_FOREACH(address, link->addresses)
121 if (address->source == NETWORK_CONFIG_SOURCE_DHCP6)
122 address->callback = dhcp6_address_ready_callback;
123
124 log_link_debug(link, "%s(): no DHCPv6 address is ready.", __func__);
125 return 0;
126 }
127
128 link->dhcp6_configured = true;
129 log_link_debug(link, "DHCPv6 addresses and routes set.");
130
131 r = dhcp6_remove(link, /* only_marked = */ true);
132 if (r < 0)
133 return r;
134
135 link_check_ready(link);
136 return 0;
137 }
138
139 static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) {
140 int r;
141
142 assert(link);
143
144 r = address_configure_handler_internal(rtnl, m, link, "Could not set DHCPv6 address");
145 if (r <= 0)
146 return r;
147
148 r = dhcp6_check_ready(link);
149 if (r < 0)
150 link_enter_failed(link);
151
152 return 1;
153 }
154
155 static int verify_dhcp6_address(Link *link, const Address *address) {
156 bool by_ndisc = false;
157 Address *existing;
158 int log_level;
159
160 assert(link);
161 assert(address);
162 assert(address->family == AF_INET6);
163
164 const char *pretty = IN6_ADDR_TO_STRING(&address->in_addr.in6);
165
166 if (address_get(link, address, &existing) < 0 &&
167 link_get_address(link, AF_INET6, &address->in_addr, 0, &existing) < 0) {
168 /* New address. */
169 log_level = LOG_INFO;
170 goto simple_log;
171 } else
172 log_level = LOG_DEBUG;
173
174 if (address->prefixlen == existing->prefixlen)
175 /* Currently, only conflict in prefix length is reported. */
176 goto simple_log;
177
178 if (existing->source == NETWORK_CONFIG_SOURCE_NDISC)
179 by_ndisc = true;
180
181 log_link_warning(link, "Ignoring DHCPv6 address %s/%u (valid %s, preferred %s) which conflicts with %s/%u%s.",
182 pretty, address->prefixlen,
183 FORMAT_LIFETIME(address->lifetime_valid_usec),
184 FORMAT_LIFETIME(address->lifetime_preferred_usec),
185 pretty, existing->prefixlen,
186 by_ndisc ? " assigned by NDisc" : "");
187 if (by_ndisc)
188 log_link_warning(link, "Hint: use IPv6Token= setting to change the address generated by NDisc or set UseAutonomousPrefix=no.");
189
190 return -EEXIST;
191
192 simple_log:
193 log_link_full(link, log_level, "DHCPv6 address %s/%u (valid %s, preferred %s)",
194 pretty, address->prefixlen,
195 FORMAT_LIFETIME(address->lifetime_valid_usec),
196 FORMAT_LIFETIME(address->lifetime_preferred_usec));
197 return 0;
198 }
199
200 static int dhcp6_request_address(
201 Link *link,
202 const struct in6_addr *server_address,
203 const struct in6_addr *ip6_addr,
204 usec_t lifetime_preferred_usec,
205 usec_t lifetime_valid_usec) {
206
207 _cleanup_(address_freep) Address *addr = NULL;
208 Address *existing;
209 int r;
210
211 r = address_new(&addr);
212 if (r < 0)
213 return log_oom();
214
215 addr->source = NETWORK_CONFIG_SOURCE_DHCP6;
216 addr->provider.in6 = *server_address;
217 addr->family = AF_INET6;
218 addr->in_addr.in6 = *ip6_addr;
219 addr->flags = IFA_F_NOPREFIXROUTE;
220 addr->prefixlen = 128;
221 addr->lifetime_preferred_usec = lifetime_preferred_usec;
222 addr->lifetime_valid_usec = lifetime_valid_usec;
223
224 if (verify_dhcp6_address(link, addr) < 0)
225 return 0;
226
227 r = free_and_strdup_warn(&addr->netlabel, link->network->dhcp6_netlabel);
228 if (r < 0)
229 return r;
230
231 if (address_get(link, addr, &existing) < 0)
232 link->dhcp6_configured = false;
233 else
234 address_unmark(existing);
235
236 r = link_request_address(link, TAKE_PTR(addr), true, &link->dhcp6_messages,
237 dhcp6_address_handler, NULL);
238 if (r < 0)
239 return log_link_error_errno(link, r, "Failed to request DHCPv6 address %s/128: %m",
240 IN6_ADDR_TO_STRING(ip6_addr));
241 return 0;
242 }
243
244 static int dhcp6_address_acquired(Link *link) {
245 struct in6_addr server_address;
246 usec_t timestamp_usec;
247 int r;
248
249 assert(link);
250 assert(link->network);
251 assert(link->dhcp6_lease);
252
253 if (!link->network->dhcp6_use_address)
254 return 0;
255
256 r = sd_dhcp6_lease_get_server_address(link->dhcp6_lease, &server_address);
257 if (r < 0)
258 return log_link_warning_errno(link, r, "Failed to get server address of DHCPv6 lease: %m");
259
260 r = sd_dhcp6_lease_get_timestamp(link->dhcp6_lease, CLOCK_BOOTTIME, &timestamp_usec);
261 if (r < 0)
262 return log_link_warning_errno(link, r, "Failed to get timestamp of DHCPv6 lease: %m");
263
264 for (sd_dhcp6_lease_reset_address_iter(link->dhcp6_lease);;) {
265 uint32_t lifetime_preferred_sec, lifetime_valid_sec;
266 struct in6_addr ip6_addr;
267
268 r = sd_dhcp6_lease_get_address(link->dhcp6_lease, &ip6_addr, &lifetime_preferred_sec, &lifetime_valid_sec);
269 if (r < 0)
270 break;
271
272 r = dhcp6_request_address(link, &server_address, &ip6_addr,
273 sec_to_usec(lifetime_preferred_sec, timestamp_usec),
274 sec_to_usec(lifetime_valid_sec, timestamp_usec));
275 if (r < 0)
276 return r;
277 }
278
279 if (link->network->dhcp6_use_hostname) {
280 const char *dhcpname = NULL;
281 _cleanup_free_ char *hostname = NULL;
282
283 (void) sd_dhcp6_lease_get_fqdn(link->dhcp6_lease, &dhcpname);
284
285 if (dhcpname) {
286 r = shorten_overlong(dhcpname, &hostname);
287 if (r < 0)
288 log_link_warning_errno(link, r, "Unable to shorten overlong DHCP hostname '%s', ignoring: %m", dhcpname);
289 if (r == 1)
290 log_link_notice(link, "Overlong DHCP hostname received, shortened from '%s' to '%s'", dhcpname, hostname);
291 }
292 if (hostname) {
293 r = manager_set_hostname(link->manager, hostname);
294 if (r < 0)
295 log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname);
296 }
297 }
298
299 return 0;
300 }
301
302 static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) {
303 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease_old = NULL;
304 sd_dhcp6_lease *lease;
305 int r;
306
307 link_mark_addresses(link, NETWORK_CONFIG_SOURCE_DHCP6, NULL);
308 link_mark_routes(link, NETWORK_CONFIG_SOURCE_DHCP6, NULL);
309
310 r = sd_dhcp6_client_get_lease(client, &lease);
311 if (r < 0)
312 return log_link_error_errno(link, r, "Failed to get DHCPv6 lease: %m");
313
314 lease_old = TAKE_PTR(link->dhcp6_lease);
315 link->dhcp6_lease = sd_dhcp6_lease_ref(lease);
316
317 r = dhcp6_address_acquired(link);
318 if (r < 0)
319 return r;
320
321 if (dhcp6_lease_has_pd_prefix(lease)) {
322 r = dhcp6_pd_prefix_acquired(link);
323 if (r < 0)
324 return r;
325 } else if (dhcp6_lease_has_pd_prefix(lease_old))
326 /* When we had PD prefixes but not now, we need to remove them. */
327 dhcp_pd_prefix_lost(link);
328
329 if (link->dhcp6_messages == 0) {
330 link->dhcp6_configured = true;
331
332 r = dhcp6_remove(link, /* only_marked = */ true);
333 if (r < 0)
334 return r;
335 } else
336 log_link_debug(link, "Setting DHCPv6 addresses and routes");
337
338 if (!link->dhcp6_configured)
339 link_set_state(link, LINK_STATE_CONFIGURING);
340
341 link_check_ready(link);
342 return 0;
343 }
344
345 static int dhcp6_lease_information_acquired(sd_dhcp6_client *client, Link *link) {
346 return 0;
347 }
348
349 static int dhcp6_lease_lost(Link *link) {
350 int r;
351
352 assert(link);
353 assert(link->manager);
354
355 log_link_info(link, "DHCPv6 lease lost");
356
357 if (dhcp6_lease_has_pd_prefix(link->dhcp6_lease))
358 dhcp_pd_prefix_lost(link);
359
360 link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease);
361
362 r = dhcp6_remove(link, /* only_marked = */ false);
363 if (r < 0)
364 return r;
365
366 return 0;
367 }
368
369 static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
370 Link *link = ASSERT_PTR(userdata);
371 int r;
372
373 assert(link->network);
374
375 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
376 return;
377
378 switch (event) {
379 case SD_DHCP6_CLIENT_EVENT_STOP:
380 case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
381 case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
382 r = dhcp6_lease_lost(link);
383 if (r < 0)
384 link_enter_failed(link);
385 break;
386
387 case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
388 r = dhcp6_lease_ip_acquired(client, link);
389 if (r < 0) {
390 link_enter_failed(link);
391 return;
392 }
393
394 _fallthrough_;
395 case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
396 r = dhcp6_lease_information_acquired(client, link);
397 if (r < 0)
398 link_enter_failed(link);
399 break;
400
401 default:
402 if (event < 0)
403 log_link_warning_errno(link, event, "DHCPv6 error: %m");
404 else
405 log_link_warning(link, "DHCPv6 unknown event: %d", event);
406 return;
407 }
408 }
409
410 int dhcp6_start_on_ra(Link *link, bool information_request) {
411 int r;
412
413 assert(link);
414 assert(link->dhcp6_client);
415 assert(link->network);
416 assert(in6_addr_is_link_local(&link->ipv6ll_address));
417
418 if (link_get_dhcp6_client_start_mode(link) != DHCP6_CLIENT_START_MODE_NO)
419 /* When WithoutRA= is specified, then the DHCPv6 client should be already running in
420 * the requested mode. Hence, ignore the requests by RA. */
421 return 0;
422
423 r = sd_dhcp6_client_is_running(link->dhcp6_client);
424 if (r < 0)
425 return r;
426
427 if (r > 0) {
428 int inf_req;
429
430 r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
431 if (r < 0)
432 return r;
433
434 if (inf_req == information_request)
435 /* The client is already running in the requested mode. */
436 return 0;
437
438 if (!inf_req) {
439 log_link_debug(link,
440 "The DHCPv6 client is already running in the managed mode, "
441 "refusing to start the client in the information requesting mode.");
442 return 0;
443 }
444
445 log_link_debug(link,
446 "The DHCPv6 client is running in the information requesting mode. "
447 "Restarting the client in the managed mode.");
448
449 r = sd_dhcp6_client_stop(link->dhcp6_client);
450 if (r < 0)
451 return r;
452 } else {
453 r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
454 if (r < 0)
455 return r;
456 }
457
458 r = sd_dhcp6_client_set_information_request(link->dhcp6_client, information_request);
459 if (r < 0)
460 return r;
461
462 r = sd_dhcp6_client_start(link->dhcp6_client);
463 if (r < 0)
464 return r;
465
466 return 0;
467 }
468
469 int dhcp6_start(Link *link) {
470 DHCP6ClientStartMode start_mode;
471 int r;
472
473 assert(link);
474 assert(link->network);
475
476 if (!link->dhcp6_client)
477 return 0;
478
479 if (!link_dhcp6_enabled(link))
480 return 0;
481
482 if (!link_has_carrier(link))
483 return 0;
484
485 if (sd_dhcp6_client_is_running(link->dhcp6_client) > 0)
486 return 0;
487
488 if (!in6_addr_is_link_local(&link->ipv6ll_address)) {
489 log_link_debug(link, "IPv6 link-local address is not set, delaying to start DHCPv6 client.");
490 return 0;
491 }
492
493 r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
494 if (r < 0)
495 return r;
496
497 start_mode = link_get_dhcp6_client_start_mode(link);
498 if (start_mode == DHCP6_CLIENT_START_MODE_NO)
499 return 0;
500
501 r = sd_dhcp6_client_set_information_request(link->dhcp6_client,
502 start_mode == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST);
503 if (r < 0)
504 return r;
505
506 r = sd_dhcp6_client_start(link->dhcp6_client);
507 if (r < 0)
508 return r;
509
510 return 1;
511 }
512
513 static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
514 _cleanup_free_ char *hostname = NULL;
515 const char *hn;
516 int r;
517
518 assert(link);
519
520 if (!link->network->dhcp_send_hostname)
521 hn = NULL;
522 else if (link->network->dhcp_hostname)
523 hn = link->network->dhcp_hostname;
524 else {
525 r = gethostname_strict(&hostname);
526 if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
527 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to get hostname: %m");
528
529 hn = hostname;
530 }
531
532 r = sd_dhcp6_client_set_fqdn(client, hn);
533 if (r == -EINVAL && hostname)
534 /* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */
535 log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
536 else if (r < 0)
537 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set hostname: %m");
538
539 return 0;
540 }
541
542 static int dhcp6_set_identifier(Link *link, sd_dhcp6_client *client) {
543 const DUID *duid;
544 int r;
545
546 assert(link);
547 assert(link->network);
548 assert(client);
549
550 r = sd_dhcp6_client_set_mac(client, link->hw_addr.bytes, link->hw_addr.length, link->iftype);
551 if (r < 0)
552 return r;
553
554 if (link->network->dhcp6_iaid_set) {
555 r = sd_dhcp6_client_set_iaid(client, link->network->dhcp6_iaid);
556 if (r < 0)
557 return r;
558 }
559
560 duid = link_get_dhcp6_duid(link);
561 if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
562 r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
563 else
564 r = sd_dhcp6_client_set_duid(client,
565 duid->type,
566 duid->raw_data_len > 0 ? duid->raw_data : NULL,
567 duid->raw_data_len);
568 if (r < 0)
569 return r;
570
571 return 0;
572 }
573
574 static int dhcp6_configure(Link *link) {
575 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
576 sd_dhcp6_option *vendor_option;
577 sd_dhcp6_option *send_option;
578 void *request_options;
579 int r;
580
581 assert(link);
582 assert(link->network);
583
584 if (link->dhcp6_client)
585 return log_link_debug_errno(link, SYNTHETIC_ERRNO(EBUSY), "DHCPv6 client is already configured.");
586
587 r = sd_dhcp6_client_new(&client);
588 if (r == -ENOMEM)
589 return log_oom_debug();
590 if (r < 0)
591 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to create DHCPv6 client: %m");
592
593 r = sd_dhcp6_client_attach_event(client, link->manager->event, 0);
594 if (r < 0)
595 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to attach event: %m");
596
597 r = dhcp6_set_identifier(link, client);
598 if (r < 0)
599 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set identifier: %m");
600
601 ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp6_client_send_options) {
602 r = sd_dhcp6_client_add_option(client, send_option);
603 if (r == -EEXIST)
604 continue;
605 if (r < 0)
606 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set option: %m");
607 }
608
609 r = dhcp6_set_hostname(client, link);
610 if (r < 0)
611 return r;
612
613 r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
614 if (r < 0)
615 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set ifindex: %m");
616
617 if (link->network->dhcp6_mudurl) {
618 r = sd_dhcp6_client_set_request_mud_url(client, link->network->dhcp6_mudurl);
619 if (r < 0)
620 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set MUD URL: %m");
621 }
622
623 if (link->network->dhcp6_use_dns) {
624 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVER);
625 if (r < 0)
626 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to request DNS servers: %m");
627 }
628
629 if (link->network->dhcp6_use_domains > 0) {
630 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DOMAIN);
631 if (r < 0)
632 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to request domains: %m");
633 }
634
635 if (link->network->dhcp6_use_ntp) {
636 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_NTP_SERVER);
637 if (r < 0)
638 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to request NTP servers: %m");
639
640 /* If the server does not provide NTP servers, then we fallback to use SNTP servers. */
641 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_SNTP_SERVER);
642 if (r < 0)
643 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to request SNTP servers: %m");
644 }
645
646 SET_FOREACH(request_options, link->network->dhcp6_request_options) {
647 uint32_t option = PTR_TO_UINT32(request_options);
648
649 r = sd_dhcp6_client_set_request_option(client, option);
650 if (r == -EEXIST) {
651 log_link_debug(link, "DHCPv6 CLIENT: Failed to set request flag for '%u' already exists, ignoring.", option);
652 continue;
653 }
654 if (r < 0)
655 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set request flag for '%u': %m", option);
656 }
657
658 if (link->network->dhcp6_user_class) {
659 r = sd_dhcp6_client_set_request_user_class(client, link->network->dhcp6_user_class);
660 if (r < 0)
661 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set user class: %m");
662 }
663
664 if (link->network->dhcp6_vendor_class) {
665 r = sd_dhcp6_client_set_request_vendor_class(client, link->network->dhcp6_vendor_class);
666 if (r < 0)
667 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set vendor class: %m");
668 }
669
670 ORDERED_HASHMAP_FOREACH(vendor_option, link->network->dhcp6_client_send_vendor_options) {
671 r = sd_dhcp6_client_add_vendor_option(client, vendor_option);
672 if (r == -EEXIST)
673 continue;
674 if (r < 0)
675 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set vendor option: %m");
676 }
677
678 r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
679 if (r < 0)
680 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set callback: %m");
681
682 r = sd_dhcp6_client_set_prefix_delegation(client, link->network->dhcp6_use_pd_prefix);
683 if (r < 0)
684 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to %s requesting prefixes to be delegated: %m",
685 enable_disable(link->network->dhcp6_use_pd_prefix));
686
687 /* Even if UseAddress=no, we need to request IA_NA, as the dhcp6 client may be started in solicit mode. */
688 r = sd_dhcp6_client_set_address_request(client, link->network->dhcp6_use_pd_prefix ? link->network->dhcp6_use_address : true);
689 if (r < 0)
690 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to %s requesting address: %m",
691 enable_disable(link->network->dhcp6_use_address));
692
693 if (link->network->dhcp6_pd_prefix_length > 0) {
694 r = sd_dhcp6_client_set_prefix_delegation_hint(client,
695 link->network->dhcp6_pd_prefix_length,
696 &link->network->dhcp6_pd_prefix_hint);
697 if (r < 0)
698 return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set prefix delegation hint: %m");
699 }
700
701 r = sd_dhcp6_client_set_rapid_commit(client, link->network->dhcp6_use_rapid_commit);
702 if (r < 0)
703 return log_link_debug_errno(link, r,
704 "DHCPv6 CLIENT: Failed to %s rapid commit: %m",
705 enable_disable(link->network->dhcp6_use_rapid_commit));
706
707 link->dhcp6_client = TAKE_PTR(client);
708
709 return 0;
710 }
711
712 int dhcp6_update_mac(Link *link) {
713 bool restart;
714 int r;
715
716 assert(link);
717
718 if (!link->dhcp6_client)
719 return 0;
720
721 restart = sd_dhcp6_client_is_running(link->dhcp6_client) > 0;
722
723 if (restart) {
724 r = sd_dhcp6_client_stop(link->dhcp6_client);
725 if (r < 0)
726 return r;
727 }
728
729 r = dhcp6_set_identifier(link, link->dhcp6_client);
730 if (r < 0)
731 return r;
732
733 if (restart) {
734 r = sd_dhcp6_client_start(link->dhcp6_client);
735 if (r < 0)
736 return log_link_warning_errno(link, r, "Could not restart DHCPv6 client: %m");
737 }
738
739 return 0;
740 }
741
742 static int dhcp6_process_request(Request *req, Link *link, void *userdata) {
743 int r;
744
745 assert(link);
746
747 if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
748 return 0;
749
750 r = dhcp_configure_duid(link, link_get_dhcp6_duid(link));
751 if (r <= 0)
752 return r;
753
754 r = dhcp6_configure(link);
755 if (r < 0)
756 return log_link_warning_errno(link, r, "Failed to configure DHCPv6 client: %m");
757
758 r = ndisc_start(link);
759 if (r < 0)
760 return log_link_warning_errno(link, r, "Failed to start IPv6 Router Discovery: %m");
761
762 r = dhcp6_start(link);
763 if (r < 0)
764 return log_link_warning_errno(link, r, "Failed to start DHCPv6 client: %m");
765
766 log_link_debug(link, "DHCPv6 client is configured%s.",
767 r > 0 ? ", acquiring DHCPv6 lease" : "");
768 return 1;
769 }
770
771 int link_request_dhcp6_client(Link *link) {
772 int r;
773
774 assert(link);
775
776 if (!link_dhcp6_enabled(link) && !link_ipv6_accept_ra_enabled(link))
777 return 0;
778
779 if (link->dhcp6_client)
780 return 0;
781
782 r = link_queue_request(link, REQUEST_TYPE_DHCP6_CLIENT, dhcp6_process_request, NULL);
783 if (r < 0)
784 return log_link_warning_errno(link, r, "Failed to request configuring of the DHCPv6 client: %m");
785
786 log_link_debug(link, "Requested configuring of the DHCPv6 client.");
787 return 0;
788 }
789
790 int link_serialize_dhcp6_client(Link *link, FILE *f) {
791 _cleanup_free_ char *duid = NULL;
792 uint32_t iaid;
793 int r;
794
795 assert(link);
796
797 if (!link->dhcp6_client)
798 return 0;
799
800 r = sd_dhcp6_client_get_iaid(link->dhcp6_client, &iaid);
801 if (r >= 0)
802 fprintf(f, "DHCP6_CLIENT_IAID=0x%x\n", iaid);
803
804 r = sd_dhcp6_client_duid_as_string(link->dhcp6_client, &duid);
805 if (r >= 0)
806 fprintf(f, "DHCP6_CLIENT_DUID=%s\n", duid);
807
808 return 0;
809 }
810
811 int config_parse_dhcp6_pd_prefix_hint(
812 const char* unit,
813 const char *filename,
814 unsigned line,
815 const char *section,
816 unsigned section_line,
817 const char *lvalue,
818 int ltype,
819 const char *rvalue,
820 void *data,
821 void *userdata) {
822
823 Network *network = ASSERT_PTR(userdata);
824 union in_addr_union u;
825 unsigned char prefixlen;
826 int r;
827
828 assert(filename);
829 assert(lvalue);
830 assert(rvalue);
831
832 r = in_addr_prefix_from_string(rvalue, AF_INET6, &u, &prefixlen);
833 if (r < 0) {
834 log_syntax(unit, LOG_WARNING, filename, line, r,
835 "Failed to parse %s=%s, ignoring assignment.", lvalue, rvalue);
836 return 0;
837 }
838
839 if (prefixlen < 1 || prefixlen > 128) {
840 log_syntax(unit, LOG_WARNING, filename, line, 0,
841 "Invalid prefix length in %s=%s, ignoring assignment.", lvalue, rvalue);
842 return 0;
843 }
844
845 network->dhcp6_pd_prefix_hint = u.in6;
846 network->dhcp6_pd_prefix_length = prefixlen;
847
848 return 0;
849 }
850
851 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp6_client_start_mode, dhcp6_client_start_mode, DHCP6ClientStartMode,
852 "Failed to parse WithoutRA= setting");
853
854 static const char* const dhcp6_client_start_mode_table[_DHCP6_CLIENT_START_MODE_MAX] = {
855 [DHCP6_CLIENT_START_MODE_NO] = "no",
856 [DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST] = "information-request",
857 [DHCP6_CLIENT_START_MODE_SOLICIT] = "solicit",
858 };
859
860 DEFINE_STRING_TABLE_LOOKUP(dhcp6_client_start_mode, DHCP6ClientStartMode);