1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright (C) 2014 Intel Corporation. All rights reserved.
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include <netinet/icmp6.h>
22 #include <arpa/inet.h>
26 #include "networkd-ndisc.h"
27 #include "networkd-route.h"
29 #define NDISC_DNSSL_MAX 64U
30 #define NDISC_RDNSS_MAX 64U
31 #define NDISC_PREFIX_LFT_MIN 7200U
33 static int ndisc_netlink_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, void *userdata
) {
34 _cleanup_link_unref_ Link
*link
= userdata
;
38 assert(link
->ndisc_messages
> 0);
40 link
->ndisc_messages
--;
42 r
= sd_netlink_message_get_errno(m
);
43 if (r
< 0 && r
!= -EEXIST
)
44 log_link_error_errno(link
, r
, "Could not set NDisc route or address: %m");
46 if (link
->ndisc_messages
== 0) {
47 link
->ndisc_configured
= true;
48 link_check_ready(link
);
54 static void ndisc_router_process_default(Link
*link
, sd_ndisc_router
*rt
) {
55 _cleanup_route_free_ Route
*route
= NULL
;
56 struct in6_addr gateway
;
68 r
= sd_ndisc_router_get_lifetime(rt
, &lifetime
);
70 log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
73 if (lifetime
== 0) /* not a default router */
76 r
= sd_ndisc_router_get_address(rt
, &gateway
);
78 log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
82 SET_FOREACH(address
, link
->addresses
, i
) {
83 if (!memcmp(&gateway
, &address
->in_addr
.in6
,
84 sizeof(address
->in_addr
.in6
))) {
85 char buffer
[INET6_ADDRSTRLEN
];
87 log_link_debug(link
, "No NDisc route added, gateway %s matches local address",
89 &address
->in_addr
.in6
,
90 buffer
, sizeof(buffer
)));
95 SET_FOREACH(address
, link
->addresses_foreign
, i
) {
96 if (!memcmp(&gateway
, &address
->in_addr
.in6
,
97 sizeof(address
->in_addr
.in6
))) {
98 char buffer
[INET6_ADDRSTRLEN
];
100 log_link_debug(link
, "No NDisc route added, gateway %s matches local address",
102 &address
->in_addr
.in6
,
103 buffer
, sizeof(buffer
)));
108 r
= sd_ndisc_router_get_preference(rt
, &preference
);
110 log_link_warning_errno(link
, r
, "Failed to get default router preference from RA: %m");
114 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
116 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
120 r
= sd_ndisc_router_get_mtu(rt
, &mtu
);
124 log_link_warning_errno(link
, r
, "Failed to get default router MTU from RA: %m");
128 r
= route_new(&route
);
130 log_link_error_errno(link
, r
, "Could not allocate route: %m");
134 route
->family
= AF_INET6
;
135 route
->table
= link
->network
->ipv6_accept_ra_route_table
;
136 route
->priority
= link
->network
->dhcp_route_metric
;
137 route
->protocol
= RTPROT_RA
;
138 route
->pref
= preference
;
139 route
->gw
.in6
= gateway
;
140 route
->lifetime
= time_now
+ lifetime
* USEC_PER_SEC
;
143 r
= route_configure(route
, link
, ndisc_netlink_handler
);
145 log_link_warning_errno(link
, r
, "Could not set default route: %m");
146 link_enter_failed(link
);
150 link
->ndisc_messages
++;
153 static void ndisc_router_process_autonomous_prefix(Link
*link
, sd_ndisc_router
*rt
) {
154 _cleanup_address_free_ Address
*address
= NULL
;
155 Address
*existing_address
;
156 uint32_t lifetime_valid
, lifetime_preferred
, lifetime_remaining
;
164 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
166 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
170 r
= sd_ndisc_router_prefix_get_prefixlen(rt
, &prefixlen
);
172 log_link_error_errno(link
, r
, "Failed to get prefix length: %m");
176 r
= sd_ndisc_router_prefix_get_valid_lifetime(rt
, &lifetime_valid
);
178 log_link_error_errno(link
, r
, "Failed to get prefix valid lifetime: %m");
182 r
= sd_ndisc_router_prefix_get_preferred_lifetime(rt
, &lifetime_preferred
);
184 log_link_error_errno(link
, r
, "Failed to get prefix preferred lifetime: %m");
188 r
= address_new(&address
);
190 log_link_error_errno(link
, r
, "Could not allocate address: %m");
194 address
->family
= AF_INET6
;
195 r
= sd_ndisc_router_prefix_get_address(rt
, &address
->in_addr
.in6
);
197 log_link_error_errno(link
, r
, "Failed to get prefix address: %m");
201 if (in_addr_is_null(AF_INET6
, (const union in_addr_union
*) &link
->network
->ipv6_token
) == 0)
202 memcpy(((char *)&address
->in_addr
.in6
) + 8, ((char *)&link
->network
->ipv6_token
) + 8, 8);
204 /* see RFC4291 section 2.5.1 */
205 address
->in_addr
.in6
.s6_addr
[8] = link
->mac
.ether_addr_octet
[0];
206 address
->in_addr
.in6
.s6_addr
[8] ^= 1 << 1;
207 address
->in_addr
.in6
.s6_addr
[9] = link
->mac
.ether_addr_octet
[1];
208 address
->in_addr
.in6
.s6_addr
[10] = link
->mac
.ether_addr_octet
[2];
209 address
->in_addr
.in6
.s6_addr
[11] = 0xff;
210 address
->in_addr
.in6
.s6_addr
[12] = 0xfe;
211 address
->in_addr
.in6
.s6_addr
[13] = link
->mac
.ether_addr_octet
[3];
212 address
->in_addr
.in6
.s6_addr
[14] = link
->mac
.ether_addr_octet
[4];
213 address
->in_addr
.in6
.s6_addr
[15] = link
->mac
.ether_addr_octet
[5];
215 address
->prefixlen
= prefixlen
;
216 address
->flags
= IFA_F_NOPREFIXROUTE
|IFA_F_MANAGETEMPADDR
;
217 address
->cinfo
.ifa_prefered
= lifetime_preferred
;
219 /* see RFC4862 section 5.5.3.e */
220 r
= address_get(link
, address
->family
, &address
->in_addr
, address
->prefixlen
, &existing_address
);
222 lifetime_remaining
= existing_address
->cinfo
.tstamp
/ 100 + existing_address
->cinfo
.ifa_valid
- time_now
/ USEC_PER_SEC
;
223 if (lifetime_valid
> NDISC_PREFIX_LFT_MIN
|| lifetime_valid
> lifetime_remaining
)
224 address
->cinfo
.ifa_valid
= lifetime_valid
;
225 else if (lifetime_remaining
<= NDISC_PREFIX_LFT_MIN
)
226 address
->cinfo
.ifa_valid
= lifetime_remaining
;
228 address
->cinfo
.ifa_valid
= NDISC_PREFIX_LFT_MIN
;
229 } else if (lifetime_valid
> 0)
230 address
->cinfo
.ifa_valid
= lifetime_valid
;
232 return; /* see RFC4862 section 5.5.3.d */
234 if (address
->cinfo
.ifa_valid
== 0)
237 r
= address_configure(address
, link
, ndisc_netlink_handler
, true);
239 log_link_warning_errno(link
, r
, "Could not set SLAAC address: %m");
240 link_enter_failed(link
);
244 link
->ndisc_messages
++;
247 static void ndisc_router_process_onlink_prefix(Link
*link
, sd_ndisc_router
*rt
) {
248 _cleanup_route_free_ Route
*route
= NULL
;
257 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
259 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
263 r
= sd_ndisc_router_prefix_get_prefixlen(rt
, &prefixlen
);
265 log_link_error_errno(link
, r
, "Failed to get prefix length: %m");
269 r
= sd_ndisc_router_prefix_get_valid_lifetime(rt
, &lifetime
);
271 log_link_error_errno(link
, r
, "Failed to get prefix lifetime: %m");
275 r
= route_new(&route
);
277 log_link_error_errno(link
, r
, "Could not allocate route: %m");
281 route
->family
= AF_INET6
;
282 route
->table
= link
->network
->ipv6_accept_ra_route_table
;
283 route
->priority
= link
->network
->dhcp_route_metric
;
284 route
->protocol
= RTPROT_RA
;
285 route
->flags
= RTM_F_PREFIX
;
286 route
->dst_prefixlen
= prefixlen
;
287 route
->lifetime
= time_now
+ lifetime
* USEC_PER_SEC
;
289 r
= sd_ndisc_router_prefix_get_address(rt
, &route
->dst
.in6
);
291 log_link_error_errno(link
, r
, "Failed to get prefix address: %m");
295 r
= route_configure(route
, link
, ndisc_netlink_handler
);
297 log_link_warning_errno(link
, r
, "Could not set prefix route: %m");
298 link_enter_failed(link
);
302 link
->ndisc_messages
++;
305 static void ndisc_router_process_route(Link
*link
, sd_ndisc_router
*rt
) {
306 _cleanup_route_free_ Route
*route
= NULL
;
307 struct in6_addr gateway
;
309 unsigned preference
, prefixlen
;
315 r
= sd_ndisc_router_route_get_lifetime(rt
, &lifetime
);
317 log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
323 r
= sd_ndisc_router_get_address(rt
, &gateway
);
325 log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
329 r
= sd_ndisc_router_route_get_prefixlen(rt
, &prefixlen
);
331 log_link_warning_errno(link
, r
, "Failed to get route prefix length: %m");
335 r
= sd_ndisc_router_route_get_preference(rt
, &preference
);
337 log_link_warning_errno(link
, r
, "Failed to get default router preference from RA: %m");
341 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
343 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
347 r
= route_new(&route
);
349 log_link_error_errno(link
, r
, "Could not allocate route: %m");
353 route
->family
= AF_INET6
;
354 route
->table
= link
->network
->ipv6_accept_ra_route_table
;
355 route
->protocol
= RTPROT_RA
;
356 route
->pref
= preference
;
357 route
->gw
.in6
= gateway
;
358 route
->dst_prefixlen
= prefixlen
;
359 route
->lifetime
= time_now
+ lifetime
* USEC_PER_SEC
;
361 r
= sd_ndisc_router_route_get_address(rt
, &route
->dst
.in6
);
363 log_link_error_errno(link
, r
, "Failed to get route address: %m");
367 r
= route_configure(route
, link
, ndisc_netlink_handler
);
369 log_link_warning_errno(link
, r
, "Could not set additional route: %m");
370 link_enter_failed(link
);
374 link
->ndisc_messages
++;
377 static void ndisc_rdnss_hash_func(const void *p
, struct siphash
*state
) {
378 const NDiscRDNSS
*x
= p
;
380 siphash24_compress(&x
->address
, sizeof(x
->address
), state
);
383 static int ndisc_rdnss_compare_func(const void *_a
, const void *_b
) {
384 const NDiscRDNSS
*a
= _a
, *b
= _b
;
386 return memcmp(&a
->address
, &b
->address
, sizeof(a
->address
));
389 static const struct hash_ops ndisc_rdnss_hash_ops
= {
390 .hash
= ndisc_rdnss_hash_func
,
391 .compare
= ndisc_rdnss_compare_func
394 static void ndisc_router_process_rdnss(Link
*link
, sd_ndisc_router
*rt
) {
396 const struct in6_addr
*a
;
403 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
405 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
409 r
= sd_ndisc_router_rdnss_get_lifetime(rt
, &lifetime
);
411 log_link_warning_errno(link
, r
, "Failed to get RDNSS lifetime: %m");
415 n
= sd_ndisc_router_rdnss_get_addresses(rt
, &a
);
417 log_link_warning_errno(link
, n
, "Failed to get RDNSS addresses: %m");
421 for (i
= 0; i
< n
; i
++) {
427 (void) set_remove(link
->ndisc_rdnss
, &d
);
432 x
= set_get(link
->ndisc_rdnss
, &d
);
434 x
->valid_until
= time_now
+ lifetime
* USEC_PER_SEC
;
440 if (set_size(link
->ndisc_rdnss
) >= NDISC_RDNSS_MAX
) {
441 log_link_warning(link
, "Too many RDNSS records per link, ignoring.");
445 r
= set_ensure_allocated(&link
->ndisc_rdnss
, &ndisc_rdnss_hash_ops
);
451 x
= new0(NDiscRDNSS
, 1);
458 x
->valid_until
= time_now
+ lifetime
* USEC_PER_SEC
;
460 r
= set_put(link
->ndisc_rdnss
, x
);
472 static void ndisc_dnssl_hash_func(const void *p
, struct siphash
*state
) {
473 const NDiscDNSSL
*x
= p
;
475 siphash24_compress(NDISC_DNSSL_DOMAIN(x
), strlen(NDISC_DNSSL_DOMAIN(x
)), state
);
478 static int ndisc_dnssl_compare_func(const void *_a
, const void *_b
) {
479 const NDiscDNSSL
*a
= _a
, *b
= _b
;
481 return strcmp(NDISC_DNSSL_DOMAIN(a
), NDISC_DNSSL_DOMAIN(b
));
484 static const struct hash_ops ndisc_dnssl_hash_ops
= {
485 .hash
= ndisc_dnssl_hash_func
,
486 .compare
= ndisc_dnssl_compare_func
489 static void ndisc_router_process_dnssl(Link
*link
, sd_ndisc_router
*rt
) {
490 _cleanup_strv_free_
char **l
= NULL
;
499 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
501 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
505 r
= sd_ndisc_router_dnssl_get_lifetime(rt
, &lifetime
);
507 log_link_warning_errno(link
, r
, "Failed to get RDNSS lifetime: %m");
511 r
= sd_ndisc_router_dnssl_get_domains(rt
, &l
);
513 log_link_warning_errno(link
, r
, "Failed to get RDNSS addresses: %m");
518 _cleanup_free_ NDiscDNSSL
*s
;
521 s
= malloc0(ALIGN(sizeof(NDiscDNSSL
)) + strlen(*i
) + 1);
527 strcpy(NDISC_DNSSL_DOMAIN(s
), *i
);
530 (void) set_remove(link
->ndisc_dnssl
, s
);
535 x
= set_get(link
->ndisc_dnssl
, s
);
537 x
->valid_until
= time_now
+ lifetime
* USEC_PER_SEC
;
543 if (set_size(link
->ndisc_dnssl
) >= NDISC_DNSSL_MAX
) {
544 log_link_warning(link
, "Too many DNSSL records per link, ignoring.");
548 r
= set_ensure_allocated(&link
->ndisc_dnssl
, &ndisc_dnssl_hash_ops
);
554 s
->valid_until
= time_now
+ lifetime
* USEC_PER_SEC
;
556 r
= set_put(link
->ndisc_dnssl
, s
);
568 static void ndisc_router_process_options(Link
*link
, sd_ndisc_router
*rt
) {
574 r
= sd_ndisc_router_option_rewind(rt
);
579 log_link_warning_errno(link
, r
, "Failed to iterate through options: %m");
582 if (r
== 0) /* EOF */
585 r
= sd_ndisc_router_option_get_type(rt
, &type
);
587 log_link_warning_errno(link
, r
, "Failed to get RA option type: %m");
593 case SD_NDISC_OPTION_PREFIX_INFORMATION
: {
596 r
= sd_ndisc_router_prefix_get_flags(rt
, &flags
);
598 log_link_warning_errno(link
, r
, "Failed to get RA prefix flags: %m");
602 if (flags
& ND_OPT_PI_FLAG_ONLINK
)
603 ndisc_router_process_onlink_prefix(link
, rt
);
604 if (flags
& ND_OPT_PI_FLAG_AUTO
)
605 ndisc_router_process_autonomous_prefix(link
, rt
);
610 case SD_NDISC_OPTION_ROUTE_INFORMATION
:
611 ndisc_router_process_route(link
, rt
);
614 case SD_NDISC_OPTION_RDNSS
:
615 if (link
->network
->ipv6_accept_ra_use_dns
)
616 ndisc_router_process_rdnss(link
, rt
);
619 case SD_NDISC_OPTION_DNSSL
:
620 if (link
->network
->ipv6_accept_ra_use_dns
)
621 ndisc_router_process_dnssl(link
, rt
);
625 r
= sd_ndisc_router_option_next(rt
);
629 static void ndisc_router_handler(Link
*link
, sd_ndisc_router
*rt
) {
634 assert(link
->network
);
635 assert(link
->manager
);
638 r
= sd_ndisc_router_get_flags(rt
, &flags
);
640 log_link_warning_errno(link
, r
, "Failed to get RA flags: %m");
644 if (flags
& (ND_RA_FLAG_MANAGED
| ND_RA_FLAG_OTHER
)) {
645 /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
646 r
= dhcp6_request_address(link
, !(flags
& ND_RA_FLAG_MANAGED
));
647 if (r
< 0 && r
!= -EBUSY
)
648 log_link_warning_errno(link
, r
, "Could not acquire DHCPv6 lease on NDisc request: %m");
650 log_link_debug(link
, "Acquiring DHCPv6 lease on NDisc request");
653 ndisc_router_process_default(link
, rt
);
654 ndisc_router_process_options(link
, rt
);
657 static void ndisc_handler(sd_ndisc
*nd
, sd_ndisc_event event
, sd_ndisc_router
*rt
, void *userdata
) {
658 Link
*link
= userdata
;
662 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
667 case SD_NDISC_EVENT_ROUTER
:
668 ndisc_router_handler(link
, rt
);
671 case SD_NDISC_EVENT_TIMEOUT
:
672 link
->ndisc_configured
= true;
673 link_check_ready(link
);
677 log_link_warning(link
, "IPv6 Neighbor Discovery unknown event: %d", event
);
681 int ndisc_configure(Link
*link
) {
686 r
= sd_ndisc_new(&link
->ndisc
);
690 r
= sd_ndisc_attach_event(link
->ndisc
, NULL
, 0);
694 r
= sd_ndisc_set_mac(link
->ndisc
, &link
->mac
);
698 r
= sd_ndisc_set_ifindex(link
->ndisc
, link
->ifindex
);
702 r
= sd_ndisc_set_callback(link
->ndisc
, ndisc_handler
, link
);
709 void ndisc_vacuum(Link
*link
) {
717 /* Removes all RDNSS and DNSSL entries whose validity time has passed */
719 time_now
= now(clock_boottime_or_monotonic());
721 SET_FOREACH(r
, link
->ndisc_rdnss
, i
)
722 if (r
->valid_until
< time_now
) {
723 free(set_remove(link
->ndisc_rdnss
, r
));
727 SET_FOREACH(d
, link
->ndisc_dnssl
, i
)
728 if (d
->valid_until
< time_now
) {
729 free(set_remove(link
->ndisc_dnssl
, d
));
734 void ndisc_flush(Link
*link
) {
737 /* Removes all RDNSS and DNSSL entries, without exception */
739 link
->ndisc_rdnss
= set_free_free(link
->ndisc_rdnss
);
740 link
->ndisc_dnssl
= set_free_free(link
->ndisc_dnssl
);