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