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