]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-dhcp6.c
network: make link enter failed state when a configuration fails
[thirdparty/systemd.git] / src / network / networkd-dhcp6.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
5c79bd79 2/***
810adae9 3 Copyright © 2014 Intel Corporation. All rights reserved.
5c79bd79
PF
4***/
5
9aa5d8ba 6#include <netinet/in.h>
5c79bd79 7#include <linux/if.h>
76c3246d 8#include "sd-radv.h"
5c79bd79 9
5c79bd79
PF
10#include "sd-dhcp6-client.h"
11
76c3246d 12#include "hashmap.h"
8006aa32 13#include "hostname-util.h"
aeed8332 14#include "missing_network.h"
07630cea 15#include "network-internal.h"
ca5ad760 16#include "networkd-dhcp6.h"
23f53b99
TG
17#include "networkd-link.h"
18#include "networkd-manager.h"
76c3246d
PF
19#include "siphash24.h"
20#include "string-util.h"
21#include "radv-internal.h"
07630cea 22
be3a09b7 23static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
04ed9949
YW
24static Link *dhcp6_prefix_get(Manager *m, struct in6_addr *addr);
25static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link);
26static int dhcp6_prefix_remove_all(Manager *m, Link *link);
be3a09b7 27
03d4fc2e
PF
28static bool dhcp6_get_prefix_delegation(Link *link) {
29 if (!link->network)
103b81ee 30 return false;
103b81ee 31
a9eea606
YW
32 return IN_SET(link->network->router_prefix_delegation,
33 RADV_PREFIX_DELEGATION_DHCP6,
34 RADV_PREFIX_DELEGATION_BOTH);
103b81ee
PF
35}
36
37static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) {
38 Manager *manager;
39 Link *l;
40 Iterator i;
41
42 assert(dhcp6_link);
43
44 manager = dhcp6_link->manager;
45 assert(manager);
46
47 HASHMAP_FOREACH(l, manager->links, i) {
48 if (l == dhcp6_link)
49 continue;
50
03d4fc2e 51 if (!dhcp6_get_prefix_delegation(l))
103b81ee
PF
52 continue;
53
54 return true;
55 }
56
57 return false;
58}
59
c62c4628
PF
60static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
61 Link *link) {
62 return 0;
63}
64
76c3246d
PF
65static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix,
66 uint8_t prefix_len,
67 uint32_t lifetime_preferred,
68 uint32_t lifetime_valid) {
69 sd_radv *radv = link->radv;
70 int r;
71 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
72
73 r = sd_radv_prefix_new(&p);
74 if (r < 0)
75 return r;
76
77 r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
78 if (r < 0)
79 return r;
80
81 r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred);
82 if (r < 0)
83 return r;
84
85 r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid);
86 if (r < 0)
87 return r;
88
89 r = sd_radv_stop(radv);
90 if (r < 0)
91 return r;
92
93 r = sd_radv_add_prefix(radv, p, true);
94 if (r < 0 && r != -EEXIST)
95 return r;
96
04ed9949 97 r = dhcp6_prefix_add(link->manager, prefix, link);
76c3246d
PF
98 if (r < 0)
99 return r;
100
101 return sd_radv_start(radv);
102}
103
302a796f 104static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
494c868d
PF
105 int r;
106
1046bf9b
YW
107 assert(link);
108
4ff296b0
YW
109 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
110 return 1;
111
494c868d
PF
112 r = sd_netlink_message_get_errno(m);
113 if (r < 0)
04ed9949 114 log_link_debug_errno(link, r, "Received error on unreachable route removal for DHCPv6 delegated subnet: %m");
494c868d 115
0ae286e6 116 return 1;
494c868d
PF
117}
118
65dd5e31 119int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) {
494c868d
PF
120 int r;
121 sd_dhcp6_lease *lease;
122 union in_addr_union pd_prefix;
123 uint8_t pd_prefix_len;
124 uint32_t lifetime_preferred, lifetime_valid;
125
126 r = sd_dhcp6_client_get_lease(client, &lease);
127 if (r < 0)
128 return r;
129
494c868d
PF
130 sd_dhcp6_lease_reset_pd_prefix_iter(lease);
131
132 while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
133 &lifetime_preferred,
134 &lifetime_valid) >= 0) {
135 _cleanup_free_ char *buf = NULL;
ca7c792b 136 Route *route;
494c868d 137
ca7c792b 138 if (pd_prefix_len >= 64)
494c868d
PF
139 continue;
140
141 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
142
ca7c792b
YW
143 r = route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, 0, 0, 0, &route);
144 if (r < 0) {
145 log_link_warning_errno(link, r, "Failed to add unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m",
146 strnull(buf),
147 pd_prefix_len);
148 continue;
149 }
dd5ab7d9 150
ca7c792b 151 route_update(route, NULL, 0, NULL, NULL, 0, 0, RTN_UNREACHABLE);
494c868d 152
ca7c792b
YW
153 r = route_remove(route, link, dhcp6_route_remove_handler);
154 if (r < 0) {
155 log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m",
156 strnull(buf),
157 pd_prefix_len);
158 continue;
494c868d 159 }
ca7c792b
YW
160
161 log_link_debug(link, "Removing unreachable route %s/%u",
162 strnull(buf), pd_prefix_len);
494c868d
PF
163 }
164
165 return 0;
166}
167
76c3246d
PF
168static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
169 struct in6_addr *pd_prefix,
170 uint8_t pd_prefix_len,
171 uint32_t lifetime_preferred,
172 uint32_t lifetime_valid) {
173 Link *link;
174 Manager *manager = dhcp6_link->manager;
175 union in_addr_union prefix;
9fb96abd 176 uint64_t n_prefixes, n_used = 0;
76c3246d 177 _cleanup_free_ char *buf = NULL;
eb75b919 178 _cleanup_free_ char *assigned_buf = NULL;
76c3246d
PF
179 int r;
180
181 assert(manager);
182 assert(pd_prefix_len <= 64);
183
184 prefix.in6 = *pd_prefix;
185
186 r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len);
187 if (r < 0)
188 return r;
189
9fb96abd 190 n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
76c3246d
PF
191
192 (void) in_addr_to_string(AF_INET6, &prefix, &buf);
9fb96abd 193 log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u",
76c3246d
PF
194 n_prefixes, strnull(buf), pd_prefix_len);
195
196 while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) {
197 Link *assigned_link;
198
199 if (n_used == n_prefixes) {
9fb96abd 200 log_link_debug(dhcp6_link, "Assigned %" PRIu64 "/%" PRIu64 " prefixes from %s/%u",
76c3246d
PF
201 n_used, n_prefixes, strnull(buf), pd_prefix_len);
202
203 return -EAGAIN;
204 }
205
206 if (link == dhcp6_link)
207 continue;
208
03d4fc2e 209 if (!dhcp6_get_prefix_delegation(link))
76c3246d
PF
210 continue;
211
04ed9949 212 assigned_link = dhcp6_prefix_get(manager, &prefix.in6);
2c448c8a 213 if (assigned_link && assigned_link != link)
76c3246d
PF
214 continue;
215
eb75b919 216 (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf);
76c3246d
PF
217 r = dhcp6_pd_prefix_assign(link, &prefix.in6, 64,
218 lifetime_preferred, lifetime_valid);
219 if (r < 0) {
eb75b919 220 log_link_error_errno(link, r, "Unable to %s prefix %s/64 from %s/%u for link: %m",
76c3246d 221 assigned_link ? "update": "assign",
eb75b919 222 strnull(assigned_buf),
76c3246d
PF
223 strnull(buf), pd_prefix_len);
224
4e361acc 225 if (!assigned_link)
76c3246d
PF
226 continue;
227
228 } else
eb75b919
PF
229 log_link_debug(link, "Assigned prefix %" PRIu64 "/%" PRIu64 " %s/64 from %s/%u to link",
230 n_used + 1, n_prefixes,
231 strnull(assigned_buf),
232 strnull(buf), pd_prefix_len);
76c3246d
PF
233
234 n_used++;
235
37f52406 236 r = in_addr_prefix_next(AF_INET6, &prefix, 64);
76c3246d
PF
237 if (r < 0 && n_used < n_prefixes)
238 return r;
239 }
240
494c868d
PF
241 return 0;
242}
26db55f3 243
302a796f 244static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
494c868d 245 int r;
73922903 246
1046bf9b
YW
247 assert(link);
248
4ff296b0
YW
249 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
250 return 1;
251
494c868d 252 r = sd_netlink_message_get_errno(m);
4ff296b0 253 if (r < 0 && r != -EEXIST)
1046bf9b 254 log_link_debug_errno(link, r, "Received error when adding unreachable route for DHCPv6 delegated subnet: %m");
73922903 255
0ae286e6 256 return 1;
76c3246d
PF
257}
258
259static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
260 int r;
261 sd_dhcp6_lease *lease;
494c868d 262 union in_addr_union pd_prefix;
76c3246d
PF
263 uint8_t pd_prefix_len;
264 uint32_t lifetime_preferred, lifetime_valid;
76c3246d
PF
265 Iterator i = ITERATOR_FIRST;
266
267 r = sd_dhcp6_client_get_lease(client, &lease);
268 if (r < 0)
269 return r;
270
76c3246d
PF
271 sd_dhcp6_lease_reset_pd_prefix_iter(lease);
272
494c868d 273 while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
76c3246d
PF
274 &lifetime_preferred,
275 &lifetime_valid) >= 0) {
276
ca7c792b
YW
277 _cleanup_free_ char *buf = NULL;
278
279 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
280
76c3246d
PF
281 if (pd_prefix_len > 64) {
282 log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
283 strnull(buf), pd_prefix_len);
284 continue;
285 }
286
ca7c792b 287 if (pd_prefix_len < 48)
3ec8303f
PF
288 log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u",
289 strnull(buf), pd_prefix_len);
3ec8303f 290
494c868d 291 if (pd_prefix_len < 64) {
bdb9f580 292 uint32_t table;
ca7c792b 293 Route *route;
494c868d 294
bdb9f580
YW
295 table = link_get_dhcp_route_table(link);
296
57445b53
YW
297 r = route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, 0, 0, table, &route);
298 if (r < 0) {
299 log_link_warning_errno(link, r, "Failed to add unreachable route for DHCPv6 delegated subnet %s/%u: %m",
300 strnull(buf),
301 pd_prefix_len);
302 continue;
303 }
304
305 route_update(route, NULL, 0, NULL, NULL, 0, 0, RTN_UNREACHABLE);
494c868d 306
c8ee637e 307 r = route_configure(route, link, dhcp6_route_handler);
494c868d
PF
308 if (r < 0) {
309 log_link_warning_errno(link, r, "Cannot configure unreachable route for delegated subnet %s/%u: %m",
310 strnull(buf),
311 pd_prefix_len);
494c868d
PF
312 continue;
313 }
494c868d 314
494c868d
PF
315 log_link_debug(link, "Configuring unreachable route for %s/%u",
316 strnull(buf), pd_prefix_len);
494c868d
PF
317 } else
318 log_link_debug(link, "Not adding a blocking route since distributed prefix is /64");
319
320 r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix.in6,
76c3246d
PF
321 pd_prefix_len,
322 lifetime_preferred,
323 lifetime_valid);
324 if (r < 0 && r != -EAGAIN)
325 return r;
326
327 if (r >= 0)
328 i = ITERATOR_FIRST;
329 }
330
331 return 0;
332}
333
10752343
PF
334int dhcp6_request_prefix_delegation(Link *link) {
335 Link *l;
336 Iterator i;
337
338 assert_return(link, -EINVAL);
339 assert_return(link->manager, -EOPNOTSUPP);
340
341 if (dhcp6_get_prefix_delegation(link) <= 0)
342 return 0;
343
344 log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link");
345
346 HASHMAP_FOREACH(l, link->manager->links, i) {
347 int r, enabled;
348
349 if (l == link)
350 continue;
351
352 if (!l->dhcp6_client)
353 continue;
354
355 r = sd_dhcp6_client_get_prefix_delegation(l->dhcp6_client, &enabled);
356 if (r < 0) {
357 log_link_warning_errno(l, r, "Cannot get prefix delegation when adding new link");
358 continue;
359 }
360
361 if (enabled == 0) {
362 r = sd_dhcp6_client_set_prefix_delegation(l->dhcp6_client, 1);
363 if (r < 0) {
364 log_link_warning_errno(l, r, "Cannot enable prefix delegation when adding new link");
365 continue;
366 }
367 }
368
369 r = sd_dhcp6_client_is_running(l->dhcp6_client);
370 if (r <= 0)
371 continue;
372
373 if (enabled != 0) {
374 log_link_debug(l, "Requesting re-assignment of delegated prefixes after adding new link");
375 (void) dhcp6_lease_pd_prefix_acquired(l->dhcp6_client, l);
376
377 continue;
378 }
379
380 r = sd_dhcp6_client_stop(l->dhcp6_client);
381 if (r < 0) {
382 log_link_warning_errno(l, r, "Cannot stop DHCPv6 prefix delegation client after adding new link");
383 continue;
384 }
385
386 r = sd_dhcp6_client_start(l->dhcp6_client);
387 if (r < 0) {
388 log_link_warning_errno(l, r, "Cannot restart DHCPv6 prefix delegation client after adding new link");
389 continue;
390 }
391
392 log_link_debug(l, "Restarted DHCPv6 client to acquire prefix delegations after adding new link");
393 }
394
395 return 0;
396}
397
302a796f 398static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
c62c4628
PF
399 int r;
400
401 assert(link);
402
4ff296b0
YW
403 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
404 return 1;
405
1c4baffc 406 r = sd_netlink_message_get_errno(m);
c62c4628 407 if (r < 0 && r != -EEXIST) {
be3a09b7
PF
408 if (link->rtnl_extended_attrs) {
409 log_link_warning(link, "Could not set extended netlink attributes, reverting to fallback mechanism");
410
411 link->rtnl_extended_attrs = false;
412 dhcp6_lease_address_acquired(link->dhcp6_client, link);
413
414 return 1;
415 }
416
e53fc357 417 log_link_error_errno(link, r, "Could not set DHCPv6 address: %m");
c62c4628
PF
418
419 link_enter_failed(link);
73854ba1
YW
420 return 1;
421 }
422 if (r >= 0)
4ff296b0 423 (void) manager_rtnl_process_address(rtnl, m, link->manager);
c62c4628 424
4ff296b0
YW
425 r = link_request_set_routes(link);
426 if (r < 0) {
427 link_enter_failed(link);
428 return 1;
429 }
6545067a 430
c62c4628
PF
431 return 1;
432}
433
1e7a0e21
LP
434static int dhcp6_address_change(
435 Link *link,
436 struct in6_addr *ip6_addr,
437 uint32_t lifetime_preferred,
438 uint32_t lifetime_valid) {
439
8e766630 440 _cleanup_(address_freep) Address *addr = NULL;
57e44707 441 _cleanup_free_ char *buffer = NULL;
1e7a0e21 442 int r;
c62c4628 443
f0213e37 444 r = address_new(&addr);
c62c4628
PF
445 if (r < 0)
446 return r;
447
448 addr->family = AF_INET6;
57e44707 449 addr->in_addr.in6 = *ip6_addr;
851c9f82
PF
450
451 addr->flags = IFA_F_NOPREFIXROUTE;
6d8f6b0b 452 addr->prefixlen = 128;
c62c4628
PF
453
454 addr->cinfo.ifa_prefered = lifetime_preferred;
455 addr->cinfo.ifa_valid = lifetime_valid;
456
57e44707 457 (void) in_addr_to_string(addr->family, &addr->in_addr, &buffer);
f2341e0a 458 log_link_info(link,
1e7a0e21 459 "DHCPv6 address %s/%d timeout preferred %d valid %d",
57e44707 460 strnull(buffer), addr->prefixlen, lifetime_preferred, lifetime_valid);
c62c4628 461
66669078 462 r = address_configure(addr, link, dhcp6_address_handler, true);
c62c4628 463 if (r < 0)
4ff296b0 464 return log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
c62c4628 465
4ff296b0 466 return 0;
c62c4628
PF
467}
468
c62c4628
PF
469static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
470 int r;
471 sd_dhcp6_lease *lease;
472 struct in6_addr ip6_addr;
473 uint32_t lifetime_preferred, lifetime_valid;
c62c4628
PF
474
475 r = sd_dhcp6_client_get_lease(client, &lease);
476 if (r < 0)
477 return r;
478
479 sd_dhcp6_lease_reset_address_iter(lease);
480
481 while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
483d099e
ZJS
482 &lifetime_preferred,
483 &lifetime_valid) >= 0) {
c62c4628 484
6d8f6b0b 485 r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid);
c62c4628
PF
486 if (r < 0)
487 return r;
488 }
489
490 return 0;
491}
492
5c79bd79 493static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
c62c4628 494 int r;
5c79bd79
PF
495 Link *link = userdata;
496
497 assert(link);
498 assert(link->network);
5c79bd79
PF
499
500 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
501 return;
502
503 switch(event) {
10c9ce61
DH
504 case SD_DHCP6_CLIENT_EVENT_STOP:
505 case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
506 case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
3098562c
TG
507 if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
508 log_link_warning(link, "DHCPv6 lease lost");
18d29550 509
494c868d 510 (void) dhcp6_lease_pd_prefix_lost(client, link);
04ed9949 511 (void) dhcp6_prefix_remove_all(link->manager, link);
76c3246d 512
18d29550 513 link->dhcp6_configured = false;
c62c4628
PF
514 break;
515
10c9ce61 516 case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
c62c4628
PF
517 r = dhcp6_lease_address_acquired(client, link);
518 if (r < 0) {
519 link_enter_failed(link);
520 return;
521 }
522
76c3246d
PF
523 r = dhcp6_lease_pd_prefix_acquired(client, link);
524 if (r < 0)
525 log_link_debug(link, "DHCPv6 did not receive prefixes to delegate");
526
4831981d 527 _fallthrough_;
10c9ce61 528 case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
c62c4628
PF
529 r = dhcp6_lease_information_acquired(client, link);
530 if (r < 0) {
531 link_enter_failed(link);
532 return;
533 }
5c79bd79 534
18d29550 535 link->dhcp6_configured = true;
5c79bd79
PF
536 break;
537
538 default:
539 if (event < 0)
e53fc357 540 log_link_warning_errno(link, event, "DHCPv6 error: %m");
5c79bd79 541 else
e53fc357 542 log_link_warning(link, "DHCPv6 unknown event: %d", event);
5c79bd79
PF
543 return;
544 }
18d29550 545
8012cd39 546 link_check_ready(link);
5c79bd79
PF
547}
548
720bec40 549int dhcp6_request_address(Link *link, int ir) {
125f20b4 550 int r, inf_req, pd;
7a695d8e 551 bool running;
5c79bd79 552
7a695d8e
TG
553 assert(link);
554 assert(link->dhcp6_client);
125f20b4 555 assert(link->network);
720bec40 556 assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
85bd849f 557
7a695d8e
TG
558 r = sd_dhcp6_client_is_running(link->dhcp6_client);
559 if (r < 0)
560 return r;
561 else
5d904a6a 562 running = r;
e6604041 563
125f20b4
PF
564 r = sd_dhcp6_client_get_prefix_delegation(link->dhcp6_client, &pd);
565 if (r < 0)
566 return r;
567
568 if (pd && ir && link->network->dhcp6_force_pd_other_information) {
569 log_link_debug(link, "Enabling managed mode to request DHCPv6 PD with 'Other Information' set");
570
571 r = sd_dhcp6_client_set_address_request(link->dhcp6_client,
572 false);
573 if (r < 0 )
574 return r;
575
576 ir = false;
577 }
578
7a695d8e 579 if (running) {
720bec40
TY
580 r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
581 if (r < 0)
582 return r;
583
584 if (inf_req == ir)
585 return 0;
586
7a695d8e
TG
587 r = sd_dhcp6_client_stop(link->dhcp6_client);
588 if (r < 0)
589 return r;
720bec40
TY
590 } else {
591 r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
592 if (r < 0)
593 return r;
7a695d8e 594 }
85bd849f 595
720bec40
TY
596 r = sd_dhcp6_client_set_information_request(link->dhcp6_client, ir);
597 if (r < 0)
598 return r;
599
600 r = sd_dhcp6_client_start(link->dhcp6_client);
7a695d8e
TG
601 if (r < 0)
602 return r;
85bd849f 603
7a695d8e
TG
604 return 0;
605}
18d29550 606
8006aa32
SA
607static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
608 _cleanup_free_ char *hostname = NULL;
609 const char *hn;
610 int r;
611
612 assert(link);
613
614 if (!link->network->dhcp_send_hostname)
615 hn = NULL;
616 else if (link->network->dhcp_hostname)
617 hn = link->network->dhcp_hostname;
618 else {
619 r = gethostname_strict(&hostname);
620 if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
621 return r;
622
623 hn = hostname;
624 }
625
a8494759
YW
626 r = sd_dhcp6_client_set_fqdn(client, hn);
627 if (r == -EINVAL && hostname)
628 /* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */
629 log_link_warning_errno(link, r, "DHCP6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
630 else if (r < 0)
631 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set hostname: %m");
632
633 return 0;
8006aa32
SA
634}
635
7a695d8e 636int dhcp6_configure(Link *link) {
5bad7ebd 637 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
8341a5c3 638 const DUID *duid;
fb5c8216 639 int r;
7a695d8e
TG
640
641 assert(link);
fb5c8216 642 assert(link->network);
7a695d8e 643
62379e88
TG
644 if (link->dhcp6_client)
645 return 0;
646
7a695d8e 647 r = sd_dhcp6_client_new(&client);
5bad7ebd
YW
648 if (r == -ENOMEM)
649 return log_oom();
7a695d8e 650 if (r < 0)
5bad7ebd 651 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m");
5c79bd79 652
7a695d8e 653 r = sd_dhcp6_client_attach_event(client, NULL, 0);
5c79bd79 654 if (r < 0)
5bad7ebd 655 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m");
5c79bd79 656
7a695d8e 657 r = sd_dhcp6_client_set_mac(client,
5c79bd79
PF
658 (const uint8_t *) &link->mac,
659 sizeof (link->mac), ARPHRD_ETHER);
e6604041 660 if (r < 0)
5bad7ebd 661 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MAC address: %m");
5c79bd79 662
8217ed5e
TH
663 if (link->network->iaid_set) {
664 r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
665 if (r < 0)
666 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m");
667 }
413708d1 668
f24648a6 669 duid = link_get_duid(link);
0cf7c3fd
YW
670 if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
671 r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
672 else
673 r = sd_dhcp6_client_set_duid(client,
674 duid->type,
675 duid->raw_data_len > 0 ? duid->raw_data : NULL,
676 duid->raw_data_len);
413708d1 677 if (r < 0)
5bad7ebd 678 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m");
413708d1 679
8006aa32 680 r = dhcp6_set_hostname(client, link);
8006aa32 681 if (r < 0)
a8494759 682 return r;
8006aa32 683
2f8e7633 684 r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
e6604041 685 if (r < 0)
5bad7ebd 686 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set ifindex: %m");
5c79bd79 687
fb5c8216
SS
688 if (link->network->rapid_commit) {
689 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT);
690 if (r < 0)
5bad7ebd 691 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m");
fb5c8216
SS
692 }
693
7a695d8e 694 r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
e6604041 695 if (r < 0)
5bad7ebd 696 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m");
5c79bd79 697
103b81ee
PF
698 if (dhcp6_enable_prefix_delegation(link)) {
699 r = sd_dhcp6_client_set_prefix_delegation(client, true);
700 if (r < 0)
5bad7ebd 701 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix delegation: %m");
103b81ee
PF
702 }
703
5bad7ebd 704 link->dhcp6_client = TAKE_PTR(client);
e6604041 705
7a695d8e 706 return 0;
5c79bd79 707}
04ed9949
YW
708
709static Link *dhcp6_prefix_get(Manager *m, struct in6_addr *addr) {
710 assert_return(m, NULL);
711 assert_return(addr, NULL);
712
713 return hashmap_get(m->dhcp6_prefixes, addr);
714}
715
716static int dhcp6_route_add_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
717 int r;
718
719 assert(link);
720
4ff296b0
YW
721 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
722 return 1;
723
04ed9949 724 r = sd_netlink_message_get_errno(m);
4ff296b0 725 if (r < 0 && r != -EEXIST) {
04ed9949 726 log_link_debug_errno(link, r, "Received error adding DHCPv6 Prefix Delegation route: %m");
4ff296b0
YW
727 link_enter_failed(link);
728 return 1;
729 }
04ed9949 730
4ff296b0 731 return 1;
04ed9949
YW
732}
733
734static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
735 _cleanup_free_ struct in6_addr *a = NULL;
736 _cleanup_free_ char *buf = NULL;
737 Link *assigned_link;
738 Route *route;
739 int r;
740
741 assert_return(m, -EINVAL);
742 assert_return(addr, -EINVAL);
743
744 r = route_add(link, AF_INET6, (union in_addr_union *) addr, 64,
745 0, 0, 0, &route);
746 if (r < 0)
747 return r;
748
749 r = route_configure(route, link, dhcp6_route_add_handler);
750 if (r < 0)
751 return r;
752
753 (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
754 log_link_debug(link, "Adding prefix route %s/64", strnull(buf));
755
756 assigned_link = hashmap_get(m->dhcp6_prefixes, addr);
757 if (assigned_link) {
758 assert(assigned_link == link);
759 return 0;
760 }
761
762 a = newdup(struct in6_addr, addr, 1);
763 if (!a)
764 return -ENOMEM;
765
766 r = hashmap_ensure_allocated(&m->dhcp6_prefixes, &in6_addr_hash_ops);
767 if (r < 0)
768 return r;
769
770 r = hashmap_put(m->dhcp6_prefixes, a, link);
771 if (r < 0)
772 return r;
773
774 TAKE_PTR(a);
775 link_ref(link);
776 return 0;
777}
778
779static int dhcp6_prefix_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
780 int r;
781
782 assert(link);
783
4ff296b0
YW
784 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
785 return 1;
786
04ed9949 787 r = sd_netlink_message_get_errno(m);
4ff296b0 788 if (r < 0) {
04ed9949 789 log_link_debug_errno(link, r, "Received error on DHCPv6 Prefix Delegation route removal: %m");
4ff296b0
YW
790 link_enter_failed(link);
791 return 1;
792 }
04ed9949
YW
793
794 return 1;
795}
796
797int dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
798 _cleanup_free_ struct in6_addr *a = NULL;
799 _cleanup_(link_unrefp) Link *l = NULL;
800 _cleanup_free_ char *buf = NULL;
801 Route *route;
802 int r;
803
804 assert_return(m, -EINVAL);
805 assert_return(addr, -EINVAL);
806
807 l = hashmap_remove2(m->dhcp6_prefixes, addr, (void **) &a);
808 if (!l)
809 return -EINVAL;
810
811 (void) sd_radv_remove_prefix(l->radv, addr, 64);
812 r = route_get(l, AF_INET6, (union in_addr_union *) addr, 64, 0, 0, 0, &route);
813 if (r < 0)
814 return r;
815
816 r = route_remove(route, l, dhcp6_prefix_remove_handler);
817 if (r < 0)
818 return r;
819
820 (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
821 log_link_debug(l, "Removing prefix route %s/64", strnull(buf));
822
823 return 0;
824}
825
826static int dhcp6_prefix_remove_all(Manager *m, Link *link) {
827 struct in6_addr *addr;
828 Iterator i;
829 Link *l;
830
831 assert_return(m, -EINVAL);
832 assert_return(link, -EINVAL);
833
834 HASHMAP_FOREACH_KEY(l, addr, m->dhcp6_prefixes, i)
835 if (l == link)
836 (void) dhcp6_prefix_remove(m, addr);
837
838 return 0;
839}