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