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