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, &timestamp_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