]> git.proxmox.com Git - systemd.git/blame - src/network/networkd-dhcp6.c
New upstream version 242
[systemd.git] / src / network / networkd-dhcp6.c
CommitLineData
52ad194e 1/* SPDX-License-Identifier: LGPL-2.1+ */
f47781d8 2/***
b012e921 3 Copyright © 2014 Intel Corporation. All rights reserved.
f47781d8
MP
4***/
5
6#include <netinet/ether.h>
7#include <linux/if.h>
1d42b86d 8#include "sd-radv.h"
f47781d8 9
f47781d8
MP
10#include "sd-dhcp6-client.h"
11
1d42b86d 12#include "hashmap.h"
52ad194e 13#include "hostname-util.h"
7c20daf6 14#include "missing_network.h"
db2df898 15#include "network-internal.h"
2897b343
MP
16#include "networkd-link.h"
17#include "networkd-manager.h"
1d42b86d
MB
18#include "siphash24.h"
19#include "string-util.h"
20#include "radv-internal.h"
db2df898 21
e3bff60a
MP
22static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
23
6e866b33
MB
24static bool dhcp6_get_prefix_delegation(Link *link) {
25 if (!link->network)
1d42b86d 26 return false;
1d42b86d 27
6e866b33
MB
28 return IN_SET(link->network->router_prefix_delegation,
29 RADV_PREFIX_DELEGATION_DHCP6,
30 RADV_PREFIX_DELEGATION_BOTH);
1d42b86d
MB
31}
32
33static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) {
34 Manager *manager;
35 Link *l;
36 Iterator i;
37
38 assert(dhcp6_link);
39
40 manager = dhcp6_link->manager;
41 assert(manager);
42
43 HASHMAP_FOREACH(l, manager->links, i) {
44 if (l == dhcp6_link)
45 continue;
46
6e866b33 47 if (!dhcp6_get_prefix_delegation(l))
1d42b86d
MB
48 continue;
49
50 return true;
51 }
52
53 return false;
54}
55
e735f4d4
MP
56static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
57 Link *link) {
58 return 0;
59}
60
1d42b86d
MB
61static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix,
62 uint8_t prefix_len,
63 uint32_t lifetime_preferred,
64 uint32_t lifetime_valid) {
65 sd_radv *radv = link->radv;
66 int r;
67 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
68
69 r = sd_radv_prefix_new(&p);
70 if (r < 0)
71 return r;
72
73 r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
74 if (r < 0)
75 return r;
76
77 r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred);
78 if (r < 0)
79 return r;
80
81 r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid);
82 if (r < 0)
83 return r;
84
85 r = sd_radv_stop(radv);
86 if (r < 0)
87 return r;
88
89 r = sd_radv_add_prefix(radv, p, true);
90 if (r < 0 && r != -EEXIST)
91 return r;
92
93 r = manager_dhcp6_prefix_add(link->manager, &p->opt.in6_addr, link);
94 if (r < 0)
95 return r;
96
97 return sd_radv_start(radv);
98}
99
6e866b33
MB
100static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
101 int r;
102
1d42b86d 103 assert(link);
1d42b86d 104
6e866b33
MB
105 r = sd_netlink_message_get_errno(m);
106 if (r < 0)
107 log_link_debug_errno(link, r, "Received error on unreachable route removal for DHCPv6 delegated subnetl: %m");
108
109 return 1;
110}
111
112int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) {
113 int r;
114 sd_dhcp6_lease *lease;
115 union in_addr_union pd_prefix;
116 uint8_t pd_prefix_len;
117 uint32_t lifetime_preferred, lifetime_valid;
118
119 r = sd_dhcp6_client_get_lease(client, &lease);
120 if (r < 0)
121 return r;
122
123 sd_dhcp6_lease_reset_pd_prefix_iter(lease);
124
125 while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
126 &lifetime_preferred,
127 &lifetime_valid) >= 0) {
128 _cleanup_free_ char *buf = NULL;
129 _cleanup_free_ Route *route = NULL;
130
131 if (pd_prefix_len > 64)
132 continue;
133
134 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
135
136 if (pd_prefix_len < 64) {
137 r = route_new(&route);
138 if (r < 0) {
139 log_link_warning_errno(link, r, "Cannot create unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m",
140 strnull(buf),
141 pd_prefix_len);
142 continue;
143 }
144
bb4f798a
MB
145 r = route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, 0, 0, 0, &route);
146 if (r < 0) {
147 log_link_warning_errno(link, r, "Failed to add unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m",
148 strnull(buf),
149 pd_prefix_len);
150 continue;
151 }
152
153 route_update(route, NULL, 0, NULL, NULL, 0, 0, RTN_UNREACHABLE);
6e866b33
MB
154
155 r = route_remove(route, link, dhcp6_route_remove_handler);
156 if (r < 0) {
157 (void) in_addr_to_string(AF_INET6,
158 &pd_prefix, &buf);
159
160 log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m",
161 strnull(buf),
162 pd_prefix_len);
163
164 continue;
165 }
166
167 log_link_debug(link, "Removing unreachable route %s/%u",
168 strnull(buf), pd_prefix_len);
169 }
170 }
171
172 return 0;
1d42b86d
MB
173}
174
175static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
176 struct in6_addr *pd_prefix,
177 uint8_t pd_prefix_len,
178 uint32_t lifetime_preferred,
179 uint32_t lifetime_valid) {
180 Link *link;
181 Manager *manager = dhcp6_link->manager;
182 union in_addr_union prefix;
6e866b33 183 uint64_t n_prefixes, n_used = 0;
1d42b86d 184 _cleanup_free_ char *buf = NULL;
6e866b33 185 _cleanup_free_ char *assigned_buf = NULL;
1d42b86d
MB
186 int r;
187
188 assert(manager);
189 assert(pd_prefix_len <= 64);
190
191 prefix.in6 = *pd_prefix;
192
193 r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len);
194 if (r < 0)
195 return r;
196
6e866b33 197 n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
1d42b86d
MB
198
199 (void) in_addr_to_string(AF_INET6, &prefix, &buf);
6e866b33 200 log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u",
1d42b86d
MB
201 n_prefixes, strnull(buf), pd_prefix_len);
202
203 while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) {
204 Link *assigned_link;
205
206 if (n_used == n_prefixes) {
6e866b33 207 log_link_debug(dhcp6_link, "Assigned %" PRIu64 "/%" PRIu64 " prefixes from %s/%u",
1d42b86d
MB
208 n_used, n_prefixes, strnull(buf), pd_prefix_len);
209
210 return -EAGAIN;
211 }
212
213 if (link == dhcp6_link)
214 continue;
215
6e866b33 216 if (!dhcp6_get_prefix_delegation(link))
1d42b86d
MB
217 continue;
218
219 assigned_link = manager_dhcp6_prefix_get(manager, &prefix.in6);
220 if (assigned_link != NULL && assigned_link != link)
221 continue;
222
6e866b33 223 (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf);
1d42b86d
MB
224 r = dhcp6_pd_prefix_assign(link, &prefix.in6, 64,
225 lifetime_preferred, lifetime_valid);
226 if (r < 0) {
6e866b33 227 log_link_error_errno(link, r, "Unable to %s prefix %s/64 from %s/%u for link: %m",
1d42b86d 228 assigned_link ? "update": "assign",
6e866b33 229 strnull(assigned_buf),
1d42b86d
MB
230 strnull(buf), pd_prefix_len);
231
232 if (assigned_link == NULL)
233 continue;
234
235 } else
6e866b33
MB
236 log_link_debug(link, "Assigned prefix %" PRIu64 "/%" PRIu64 " %s/64 from %s/%u to link",
237 n_used + 1, n_prefixes,
238 strnull(assigned_buf),
239 strnull(buf), pd_prefix_len);
1d42b86d
MB
240
241 n_used++;
242
6e866b33 243 r = in_addr_prefix_next(AF_INET6, &prefix, 64);
1d42b86d
MB
244 if (r < 0 && n_used < n_prefixes)
245 return r;
246 }
247
6e866b33
MB
248 return 0;
249}
b012e921 250
6e866b33
MB
251static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
252 int r;
1d42b86d 253
6e866b33 254 assert(link);
1d42b86d 255
6e866b33
MB
256 r = sd_netlink_message_get_errno(m);
257 if (r < 0 && r != -EEXIST)
258 log_link_debug_errno(link, r, "Received error when adding unreachable route for DHCPv6 delegated subnet: %m");
1d42b86d 259
6e866b33 260 return 1;
1d42b86d
MB
261}
262
263static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
264 int r;
265 sd_dhcp6_lease *lease;
6e866b33 266 union in_addr_union pd_prefix;
1d42b86d
MB
267 uint8_t pd_prefix_len;
268 uint32_t lifetime_preferred, lifetime_valid;
269 _cleanup_free_ char *buf = NULL;
270 Iterator i = ITERATOR_FIRST;
271
272 r = sd_dhcp6_client_get_lease(client, &lease);
273 if (r < 0)
274 return r;
275
1d42b86d
MB
276 sd_dhcp6_lease_reset_pd_prefix_iter(lease);
277
6e866b33 278 while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
1d42b86d
MB
279 &lifetime_preferred,
280 &lifetime_valid) >= 0) {
281
282 if (pd_prefix_len > 64) {
6e866b33 283 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
1d42b86d
MB
284 log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
285 strnull(buf), pd_prefix_len);
286 continue;
287 }
288
6e866b33
MB
289 if (pd_prefix_len < 48) {
290 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
291 log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u",
292 strnull(buf), pd_prefix_len);
293 }
294
295 if (pd_prefix_len < 64) {
bb4f798a
MB
296 _cleanup_(route_freep) Route *route = NULL;
297 uint32_t table;
6e866b33
MB
298
299 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
300
301 r = route_new(&route);
302 if (r < 0) {
303 log_link_warning_errno(link, r, "Cannot create unreachable route for DHCPv6 delegated subnet %s/%u: %m",
304 strnull(buf),
305 pd_prefix_len);
306 continue;
307 }
308
bb4f798a
MB
309 table = link_get_dhcp_route_table(link);
310
311 r = route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, 0, 0, table, &route);
312 if (r < 0) {
313 log_link_warning_errno(link, r, "Failed to add unreachable route for DHCPv6 delegated subnet %s/%u: %m",
314 strnull(buf),
315 pd_prefix_len);
316 continue;
317 }
318
319 route_update(route, NULL, 0, NULL, NULL, 0, 0, RTN_UNREACHABLE);
6e866b33
MB
320
321 r = route_configure(route, link, dhcp6_route_handler);
322 if (r < 0) {
323 log_link_warning_errno(link, r, "Cannot configure unreachable route for delegated subnet %s/%u: %m",
324 strnull(buf),
325 pd_prefix_len);
6e866b33
MB
326 continue;
327 }
328
6e866b33
MB
329 log_link_debug(link, "Configuring unreachable route for %s/%u",
330 strnull(buf), pd_prefix_len);
331
332 } else
333 log_link_debug(link, "Not adding a blocking route since distributed prefix is /64");
334
335 r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix.in6,
1d42b86d
MB
336 pd_prefix_len,
337 lifetime_preferred,
338 lifetime_valid);
339 if (r < 0 && r != -EAGAIN)
340 return r;
341
342 if (r >= 0)
343 i = ITERATOR_FIRST;
344 }
345
346 return 0;
347}
348
6e866b33
MB
349int dhcp6_request_prefix_delegation(Link *link) {
350 Link *l;
351 Iterator i;
352
353 assert_return(link, -EINVAL);
354 assert_return(link->manager, -EOPNOTSUPP);
355
356 if (dhcp6_get_prefix_delegation(link) <= 0)
357 return 0;
358
359 log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link");
360
361 HASHMAP_FOREACH(l, link->manager->links, i) {
362 int r, enabled;
363
364 if (l == link)
365 continue;
366
367 if (!l->dhcp6_client)
368 continue;
369
370 r = sd_dhcp6_client_get_prefix_delegation(l->dhcp6_client, &enabled);
371 if (r < 0) {
372 log_link_warning_errno(l, r, "Cannot get prefix delegation when adding new link");
373 continue;
374 }
375
376 if (enabled == 0) {
377 r = sd_dhcp6_client_set_prefix_delegation(l->dhcp6_client, 1);
378 if (r < 0) {
379 log_link_warning_errno(l, r, "Cannot enable prefix delegation when adding new link");
380 continue;
381 }
382 }
383
384 r = sd_dhcp6_client_is_running(l->dhcp6_client);
385 if (r <= 0)
386 continue;
387
388 if (enabled != 0) {
389 log_link_debug(l, "Requesting re-assignment of delegated prefixes after adding new link");
390 (void) dhcp6_lease_pd_prefix_acquired(l->dhcp6_client, l);
391
392 continue;
393 }
394
395 r = sd_dhcp6_client_stop(l->dhcp6_client);
396 if (r < 0) {
397 log_link_warning_errno(l, r, "Cannot stop DHCPv6 prefix delegation client after adding new link");
398 continue;
399 }
400
401 r = sd_dhcp6_client_start(l->dhcp6_client);
402 if (r < 0) {
403 log_link_warning_errno(l, r, "Cannot restart DHCPv6 prefix delegation client after adding new link");
404 continue;
405 }
406
407 log_link_debug(l, "Restarted DHCPv6 client to acquire prefix delegations after adding new link");
408 }
409
410 return 0;
411}
412
413static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
e735f4d4
MP
414 int r;
415
416 assert(link);
417
86f210e9 418 r = sd_netlink_message_get_errno(m);
e735f4d4 419 if (r < 0 && r != -EEXIST) {
e3bff60a
MP
420 if (link->rtnl_extended_attrs) {
421 log_link_warning(link, "Could not set extended netlink attributes, reverting to fallback mechanism");
422
423 link->rtnl_extended_attrs = false;
424 dhcp6_lease_address_acquired(link->dhcp6_client, link);
425
426 return 1;
427 }
428
6300502b 429 log_link_error_errno(link, r, "Could not set DHCPv6 address: %m");
e735f4d4
MP
430
431 link_enter_failed(link);
432
433 } else if (r >= 0)
db2df898 434 manager_rtnl_process_address(rtnl, m, link->manager);
e735f4d4
MP
435
436 return 1;
437}
438
5a920b42
MP
439static int dhcp6_address_change(
440 Link *link,
441 struct in6_addr *ip6_addr,
442 uint32_t lifetime_preferred,
443 uint32_t lifetime_valid) {
444
b012e921 445 _cleanup_(address_freep) Address *addr = NULL;
5a920b42
MP
446 char buffer[INET6_ADDRSTRLEN];
447 int r;
e735f4d4 448
db2df898 449 r = address_new(&addr);
e735f4d4
MP
450 if (r < 0)
451 return r;
452
453 addr->family = AF_INET6;
454 memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr));
e3bff60a
MP
455
456 addr->flags = IFA_F_NOPREFIXROUTE;
db2df898 457 addr->prefixlen = 128;
e735f4d4
MP
458
459 addr->cinfo.ifa_prefered = lifetime_preferred;
460 addr->cinfo.ifa_valid = lifetime_valid;
461
e3bff60a 462 log_link_info(link,
5a920b42
MP
463 "DHCPv6 address %s/%d timeout preferred %d valid %d",
464 inet_ntop(AF_INET6, &addr->in_addr.in6, buffer, sizeof(buffer)),
e3bff60a 465 addr->prefixlen, lifetime_preferred, lifetime_valid);
e735f4d4 466
db2df898 467 r = address_configure(addr, link, dhcp6_address_handler, true);
e735f4d4 468 if (r < 0)
e3bff60a 469 log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
e735f4d4
MP
470
471 return r;
472}
473
e735f4d4
MP
474static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
475 int r;
476 sd_dhcp6_lease *lease;
477 struct in6_addr ip6_addr;
478 uint32_t lifetime_preferred, lifetime_valid;
e735f4d4
MP
479
480 r = sd_dhcp6_client_get_lease(client, &lease);
481 if (r < 0)
482 return r;
483
484 sd_dhcp6_lease_reset_address_iter(lease);
485
486 while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
aa27b158
MP
487 &lifetime_preferred,
488 &lifetime_valid) >= 0) {
e735f4d4 489
db2df898 490 r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid);
e735f4d4
MP
491 if (r < 0)
492 return r;
493 }
494
495 return 0;
496}
497
f47781d8 498static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
e735f4d4 499 int r;
f47781d8
MP
500 Link *link = userdata;
501
502 assert(link);
503 assert(link->network);
f47781d8
MP
504
505 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
506 return;
507
508 switch(event) {
6300502b
MP
509 case SD_DHCP6_CLIENT_EVENT_STOP:
510 case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
511 case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
db2df898
MP
512 if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
513 log_link_warning(link, "DHCPv6 lease lost");
6300502b 514
6e866b33 515 (void) dhcp6_lease_pd_prefix_lost(client, link);
1d42b86d
MB
516 (void) manager_dhcp6_prefix_remove_all(link->manager, link);
517
6300502b 518 link->dhcp6_configured = false;
e735f4d4
MP
519 break;
520
6300502b 521 case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
e735f4d4
MP
522 r = dhcp6_lease_address_acquired(client, link);
523 if (r < 0) {
524 link_enter_failed(link);
525 return;
526 }
527
1d42b86d
MB
528 r = dhcp6_lease_pd_prefix_acquired(client, link);
529 if (r < 0)
530 log_link_debug(link, "DHCPv6 did not receive prefixes to delegate");
531
52ad194e 532 _fallthrough_;
6300502b 533 case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
e735f4d4
MP
534 r = dhcp6_lease_information_acquired(client, link);
535 if (r < 0) {
536 link_enter_failed(link);
537 return;
538 }
f47781d8 539
6300502b 540 link->dhcp6_configured = true;
f47781d8
MP
541 break;
542
543 default:
544 if (event < 0)
6300502b 545 log_link_warning_errno(link, event, "DHCPv6 error: %m");
f47781d8 546 else
6300502b 547 log_link_warning(link, "DHCPv6 unknown event: %d", event);
f47781d8
MP
548 return;
549 }
6300502b 550
db2df898 551 link_check_ready(link);
f47781d8
MP
552}
553
5a920b42 554int dhcp6_request_address(Link *link, int ir) {
6e866b33 555 int r, inf_req, pd;
db2df898 556 bool running;
f47781d8 557
db2df898
MP
558 assert(link);
559 assert(link->dhcp6_client);
6e866b33 560 assert(link->network);
5a920b42 561 assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
f47781d8 562
db2df898 563 r = sd_dhcp6_client_is_running(link->dhcp6_client);
6300502b 564 if (r < 0)
db2df898
MP
565 return r;
566 else
b012e921 567 running = r;
f47781d8 568
6e866b33
MB
569 r = sd_dhcp6_client_get_prefix_delegation(link->dhcp6_client, &pd);
570 if (r < 0)
571 return r;
572
573 if (pd && ir && link->network->dhcp6_force_pd_other_information) {
574 log_link_debug(link, "Enabling managed mode to request DHCPv6 PD with 'Other Information' set");
575
576 r = sd_dhcp6_client_set_address_request(link->dhcp6_client,
577 false);
578 if (r < 0 )
579 return r;
580
581 ir = false;
582 }
583
db2df898 584 if (running) {
5a920b42
MP
585 r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
586 if (r < 0)
587 return r;
588
589 if (inf_req == ir)
590 return 0;
591
db2df898 592 r = sd_dhcp6_client_stop(link->dhcp6_client);
6300502b 593 if (r < 0)
db2df898 594 return r;
5a920b42
MP
595 } else {
596 r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
597 if (r < 0)
598 return r;
f47781d8
MP
599 }
600
5a920b42
MP
601 r = sd_dhcp6_client_set_information_request(link->dhcp6_client, ir);
602 if (r < 0)
603 return r;
604
605 r = sd_dhcp6_client_start(link->dhcp6_client);
e3bff60a
MP
606 if (r < 0)
607 return r;
608
e3bff60a
MP
609 return 0;
610}
611
52ad194e
MB
612static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
613 _cleanup_free_ char *hostname = NULL;
614 const char *hn;
615 int r;
616
617 assert(link);
618
619 if (!link->network->dhcp_send_hostname)
620 hn = NULL;
621 else if (link->network->dhcp_hostname)
622 hn = link->network->dhcp_hostname;
623 else {
624 r = gethostname_strict(&hostname);
625 if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
626 return r;
627
628 hn = hostname;
629 }
630
6e866b33
MB
631 r = sd_dhcp6_client_set_fqdn(client, hn);
632 if (r == -EINVAL && hostname)
633 /* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */
634 log_link_warning_errno(link, r, "DHCP6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
635 else if (r < 0)
636 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set hostname: %m");
637
638 return 0;
52ad194e
MB
639}
640
db2df898 641int dhcp6_configure(Link *link) {
6e866b33 642 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
aa27b158 643 const DUID *duid;
1d42b86d 644 int r;
f47781d8
MP
645
646 assert(link);
1d42b86d 647 assert(link->network);
e735f4d4 648
db2df898
MP
649 if (link->dhcp6_client)
650 return 0;
f47781d8 651
db2df898 652 r = sd_dhcp6_client_new(&client);
6e866b33
MB
653 if (r == -ENOMEM)
654 return log_oom();
db2df898 655 if (r < 0)
6e866b33 656 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m");
f47781d8 657
db2df898
MP
658 r = sd_dhcp6_client_attach_event(client, NULL, 0);
659 if (r < 0)
6e866b33 660 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m");
f47781d8 661
db2df898
MP
662 r = sd_dhcp6_client_set_mac(client,
663 (const uint8_t *) &link->mac,
664 sizeof (link->mac), ARPHRD_ETHER);
f47781d8 665 if (r < 0)
6e866b33 666 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MAC address: %m");
f47781d8 667
6e866b33
MB
668 if (link->network->iaid_set) {
669 r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
670 if (r < 0)
671 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m");
672 }
aa27b158 673
6e866b33
MB
674 duid = link_get_duid(link);
675 if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
676 r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
677 else
678 r = sd_dhcp6_client_set_duid(client,
679 duid->type,
680 duid->raw_data_len > 0 ? duid->raw_data : NULL,
681 duid->raw_data_len);
aa27b158 682 if (r < 0)
6e866b33 683 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m");
aa27b158 684
52ad194e
MB
685 r = dhcp6_set_hostname(client, link);
686 if (r < 0)
6e866b33 687 return r;
52ad194e 688
5a920b42 689 r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
f47781d8 690 if (r < 0)
6e866b33 691 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set ifindex: %m");
f47781d8 692
1d42b86d
MB
693 if (link->network->rapid_commit) {
694 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT);
695 if (r < 0)
6e866b33 696 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m");
1d42b86d
MB
697 }
698
db2df898 699 r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
f47781d8 700 if (r < 0)
6e866b33 701 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m");
db2df898 702
1d42b86d
MB
703 if (dhcp6_enable_prefix_delegation(link)) {
704 r = sd_dhcp6_client_set_prefix_delegation(client, true);
705 if (r < 0)
6e866b33 706 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix delegation: %m");
1d42b86d
MB
707 }
708
6e866b33 709 link->dhcp6_client = TAKE_PTR(client);
f47781d8 710
db2df898 711 return 0;
f47781d8 712}