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");
45 link_enter_failed(link
);
48 if (link
->ndisc_messages
== 0) {
49 link
->ndisc_configured
= true;
50 link_check_ready(link
);
56 static void ndisc_router_process_default(Link
*link
, sd_ndisc_router
*rt
) {
57 _cleanup_route_free_ Route
*route
= NULL
;
58 struct in6_addr gateway
;
70 r
= sd_ndisc_router_get_lifetime(rt
, &lifetime
);
72 log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
75 if (lifetime
== 0) /* not a default router */
78 r
= sd_ndisc_router_get_address(rt
, &gateway
);
80 log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
84 SET_FOREACH(address
, link
->addresses
, i
) {
85 if (!memcmp(&gateway
, &address
->in_addr
.in6
,
86 sizeof(address
->in_addr
.in6
))) {
87 char buffer
[INET6_ADDRSTRLEN
];
89 log_link_debug(link
, "No NDisc route added, gateway %s matches local address",
91 &address
->in_addr
.in6
,
92 buffer
, sizeof(buffer
)));
97 SET_FOREACH(address
, link
->addresses_foreign
, i
) {
98 if (!memcmp(&gateway
, &address
->in_addr
.in6
,
99 sizeof(address
->in_addr
.in6
))) {
100 char buffer
[INET6_ADDRSTRLEN
];
102 log_link_debug(link
, "No NDisc route added, gateway %s matches local address",
104 &address
->in_addr
.in6
,
105 buffer
, sizeof(buffer
)));
110 r
= sd_ndisc_router_get_preference(rt
, &preference
);
112 log_link_warning_errno(link
, r
, "Failed to get default router preference from RA: %m");
116 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
118 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
122 r
= sd_ndisc_router_get_mtu(rt
, &mtu
);
126 log_link_warning_errno(link
, r
, "Failed to get default router MTU from RA: %m");
130 r
= route_new(&route
);
132 log_link_error_errno(link
, r
, "Could not allocate route: %m");
136 route
->family
= AF_INET6
;
137 route
->table
= link
->network
->ipv6_accept_ra_route_table
;
138 route
->priority
= link
->network
->dhcp_route_metric
;
139 route
->protocol
= RTPROT_RA
;
140 route
->pref
= preference
;
141 route
->gw
.in6
= gateway
;
142 route
->lifetime
= time_now
+ lifetime
* USEC_PER_SEC
;
145 r
= route_configure(route
, link
, ndisc_netlink_handler
);
147 log_link_warning_errno(link
, r
, "Could not set default route: %m");
148 link_enter_failed(link
);
152 link
->ndisc_messages
++;
155 static void ndisc_router_process_autonomous_prefix(Link
*link
, sd_ndisc_router
*rt
) {
156 _cleanup_address_free_ Address
*address
= NULL
;
157 Address
*existing_address
;
158 uint32_t lifetime_valid
, lifetime_preferred
, lifetime_remaining
;
166 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
168 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
172 r
= sd_ndisc_router_prefix_get_prefixlen(rt
, &prefixlen
);
174 log_link_error_errno(link
, r
, "Failed to get prefix length: %m");
178 r
= sd_ndisc_router_prefix_get_valid_lifetime(rt
, &lifetime_valid
);
180 log_link_error_errno(link
, r
, "Failed to get prefix valid lifetime: %m");
184 r
= sd_ndisc_router_prefix_get_preferred_lifetime(rt
, &lifetime_preferred
);
186 log_link_error_errno(link
, r
, "Failed to get prefix preferred lifetime: %m");
190 r
= address_new(&address
);
192 log_link_error_errno(link
, r
, "Could not allocate address: %m");
196 address
->family
= AF_INET6
;
197 r
= sd_ndisc_router_prefix_get_address(rt
, &address
->in_addr
.in6
);
199 log_link_error_errno(link
, r
, "Failed to get prefix address: %m");
203 if (in_addr_is_null(AF_INET6
, (const union in_addr_union
*) &link
->network
->ipv6_token
) == 0)
204 memcpy(((char *)&address
->in_addr
.in6
) + 8, ((char *)&link
->network
->ipv6_token
) + 8, 8);
206 /* see RFC4291 section 2.5.1 */
207 address
->in_addr
.in6
.s6_addr
[8] = link
->mac
.ether_addr_octet
[0];
208 address
->in_addr
.in6
.s6_addr
[8] ^= 1 << 1;
209 address
->in_addr
.in6
.s6_addr
[9] = link
->mac
.ether_addr_octet
[1];
210 address
->in_addr
.in6
.s6_addr
[10] = link
->mac
.ether_addr_octet
[2];
211 address
->in_addr
.in6
.s6_addr
[11] = 0xff;
212 address
->in_addr
.in6
.s6_addr
[12] = 0xfe;
213 address
->in_addr
.in6
.s6_addr
[13] = link
->mac
.ether_addr_octet
[3];
214 address
->in_addr
.in6
.s6_addr
[14] = link
->mac
.ether_addr_octet
[4];
215 address
->in_addr
.in6
.s6_addr
[15] = link
->mac
.ether_addr_octet
[5];
217 address
->prefixlen
= prefixlen
;
218 address
->flags
= IFA_F_NOPREFIXROUTE
|IFA_F_MANAGETEMPADDR
;
219 address
->cinfo
.ifa_prefered
= lifetime_preferred
;
221 /* see RFC4862 section 5.5.3.e */
222 r
= address_get(link
, address
->family
, &address
->in_addr
, address
->prefixlen
, &existing_address
);
224 lifetime_remaining
= existing_address
->cinfo
.tstamp
/ 100 + existing_address
->cinfo
.ifa_valid
- time_now
/ USEC_PER_SEC
;
225 if (lifetime_valid
> NDISC_PREFIX_LFT_MIN
|| lifetime_valid
> lifetime_remaining
)
226 address
->cinfo
.ifa_valid
= lifetime_valid
;
227 else if (lifetime_remaining
<= NDISC_PREFIX_LFT_MIN
)
228 address
->cinfo
.ifa_valid
= lifetime_remaining
;
230 address
->cinfo
.ifa_valid
= NDISC_PREFIX_LFT_MIN
;
231 } else if (lifetime_valid
> 0)
232 address
->cinfo
.ifa_valid
= lifetime_valid
;
234 return; /* see RFC4862 section 5.5.3.d */
236 if (address
->cinfo
.ifa_valid
== 0)
239 r
= address_configure(address
, link
, ndisc_netlink_handler
, true);
241 log_link_warning_errno(link
, r
, "Could not set SLAAC address: %m");
242 link_enter_failed(link
);
246 link
->ndisc_messages
++;
249 static void ndisc_router_process_onlink_prefix(Link
*link
, sd_ndisc_router
*rt
) {
250 _cleanup_route_free_ Route
*route
= NULL
;
259 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
261 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
265 r
= sd_ndisc_router_prefix_get_prefixlen(rt
, &prefixlen
);
267 log_link_error_errno(link
, r
, "Failed to get prefix length: %m");
271 r
= sd_ndisc_router_prefix_get_valid_lifetime(rt
, &lifetime
);
273 log_link_error_errno(link
, r
, "Failed to get prefix lifetime: %m");
277 r
= route_new(&route
);
279 log_link_error_errno(link
, r
, "Could not allocate route: %m");
283 route
->family
= AF_INET6
;
284 route
->table
= link
->network
->ipv6_accept_ra_route_table
;
285 route
->priority
= link
->network
->dhcp_route_metric
;
286 route
->protocol
= RTPROT_RA
;
287 route
->flags
= RTM_F_PREFIX
;
288 route
->dst_prefixlen
= prefixlen
;
289 route
->lifetime
= time_now
+ lifetime
* USEC_PER_SEC
;
291 r
= sd_ndisc_router_prefix_get_address(rt
, &route
->dst
.in6
);
293 log_link_error_errno(link
, r
, "Failed to get prefix address: %m");
297 r
= route_configure(route
, link
, ndisc_netlink_handler
);
299 log_link_warning_errno(link
, r
, "Could not set prefix route: %m");
300 link_enter_failed(link
);
304 link
->ndisc_messages
++;
307 static void ndisc_router_process_route(Link
*link
, sd_ndisc_router
*rt
) {
308 _cleanup_route_free_ Route
*route
= NULL
;
309 struct in6_addr gateway
;
311 unsigned preference
, prefixlen
;
317 r
= sd_ndisc_router_route_get_lifetime(rt
, &lifetime
);
319 log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
325 r
= sd_ndisc_router_get_address(rt
, &gateway
);
327 log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
331 r
= sd_ndisc_router_route_get_prefixlen(rt
, &prefixlen
);
333 log_link_warning_errno(link
, r
, "Failed to get route prefix length: %m");
337 r
= sd_ndisc_router_route_get_preference(rt
, &preference
);
339 log_link_warning_errno(link
, r
, "Failed to get default router preference from RA: %m");
343 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
345 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
349 r
= route_new(&route
);
351 log_link_error_errno(link
, r
, "Could not allocate route: %m");
355 route
->family
= AF_INET6
;
356 route
->table
= link
->network
->ipv6_accept_ra_route_table
;
357 route
->protocol
= RTPROT_RA
;
358 route
->pref
= preference
;
359 route
->gw
.in6
= gateway
;
360 route
->dst_prefixlen
= prefixlen
;
361 route
->lifetime
= time_now
+ lifetime
* USEC_PER_SEC
;
363 r
= sd_ndisc_router_route_get_address(rt
, &route
->dst
.in6
);
365 log_link_error_errno(link
, r
, "Failed to get route address: %m");
369 r
= route_configure(route
, link
, ndisc_netlink_handler
);
371 log_link_warning_errno(link
, r
, "Could not set additional route: %m");
372 link_enter_failed(link
);
376 link
->ndisc_messages
++;
379 static void ndisc_rdnss_hash_func(const void *p
, struct siphash
*state
) {
380 const NDiscRDNSS
*x
= p
;
382 siphash24_compress(&x
->address
, sizeof(x
->address
), state
);
385 static int ndisc_rdnss_compare_func(const void *_a
, const void *_b
) {
386 const NDiscRDNSS
*a
= _a
, *b
= _b
;
388 return memcmp(&a
->address
, &b
->address
, sizeof(a
->address
));
391 static const struct hash_ops ndisc_rdnss_hash_ops
= {
392 .hash
= ndisc_rdnss_hash_func
,
393 .compare
= ndisc_rdnss_compare_func
396 static void ndisc_router_process_rdnss(Link
*link
, sd_ndisc_router
*rt
) {
398 const struct in6_addr
*a
;
405 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
407 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
411 r
= sd_ndisc_router_rdnss_get_lifetime(rt
, &lifetime
);
413 log_link_warning_errno(link
, r
, "Failed to get RDNSS lifetime: %m");
417 n
= sd_ndisc_router_rdnss_get_addresses(rt
, &a
);
419 log_link_warning_errno(link
, n
, "Failed to get RDNSS addresses: %m");
423 for (i
= 0; i
< n
; i
++) {
429 (void) set_remove(link
->ndisc_rdnss
, &d
);
434 x
= set_get(link
->ndisc_rdnss
, &d
);
436 x
->valid_until
= time_now
+ lifetime
* USEC_PER_SEC
;
442 if (set_size(link
->ndisc_rdnss
) >= NDISC_RDNSS_MAX
) {
443 log_link_warning(link
, "Too many RDNSS records per link, ignoring.");
447 r
= set_ensure_allocated(&link
->ndisc_rdnss
, &ndisc_rdnss_hash_ops
);
453 x
= new0(NDiscRDNSS
, 1);
460 x
->valid_until
= time_now
+ lifetime
* USEC_PER_SEC
;
462 r
= set_put(link
->ndisc_rdnss
, x
);
474 static void ndisc_dnssl_hash_func(const void *p
, struct siphash
*state
) {
475 const NDiscDNSSL
*x
= p
;
477 siphash24_compress(NDISC_DNSSL_DOMAIN(x
), strlen(NDISC_DNSSL_DOMAIN(x
)), state
);
480 static int ndisc_dnssl_compare_func(const void *_a
, const void *_b
) {
481 const NDiscDNSSL
*a
= _a
, *b
= _b
;
483 return strcmp(NDISC_DNSSL_DOMAIN(a
), NDISC_DNSSL_DOMAIN(b
));
486 static const struct hash_ops ndisc_dnssl_hash_ops
= {
487 .hash
= ndisc_dnssl_hash_func
,
488 .compare
= ndisc_dnssl_compare_func
491 static void ndisc_router_process_dnssl(Link
*link
, sd_ndisc_router
*rt
) {
492 _cleanup_strv_free_
char **l
= NULL
;
501 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
503 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
507 r
= sd_ndisc_router_dnssl_get_lifetime(rt
, &lifetime
);
509 log_link_warning_errno(link
, r
, "Failed to get RDNSS lifetime: %m");
513 r
= sd_ndisc_router_dnssl_get_domains(rt
, &l
);
515 log_link_warning_errno(link
, r
, "Failed to get RDNSS addresses: %m");
520 _cleanup_free_ NDiscDNSSL
*s
;
523 s
= malloc0(ALIGN(sizeof(NDiscDNSSL
)) + strlen(*i
) + 1);
529 strcpy(NDISC_DNSSL_DOMAIN(s
), *i
);
532 (void) set_remove(link
->ndisc_dnssl
, s
);
537 x
= set_get(link
->ndisc_dnssl
, s
);
539 x
->valid_until
= time_now
+ lifetime
* USEC_PER_SEC
;
545 if (set_size(link
->ndisc_dnssl
) >= NDISC_DNSSL_MAX
) {
546 log_link_warning(link
, "Too many DNSSL records per link, ignoring.");
550 r
= set_ensure_allocated(&link
->ndisc_dnssl
, &ndisc_dnssl_hash_ops
);
556 s
->valid_until
= time_now
+ lifetime
* USEC_PER_SEC
;
558 r
= set_put(link
->ndisc_dnssl
, s
);
570 static void ndisc_router_process_options(Link
*link
, sd_ndisc_router
*rt
) {
576 r
= sd_ndisc_router_option_rewind(rt
);
581 log_link_warning_errno(link
, r
, "Failed to iterate through options: %m");
584 if (r
== 0) /* EOF */
587 r
= sd_ndisc_router_option_get_type(rt
, &type
);
589 log_link_warning_errno(link
, r
, "Failed to get RA option type: %m");
595 case SD_NDISC_OPTION_PREFIX_INFORMATION
: {
598 r
= sd_ndisc_router_prefix_get_flags(rt
, &flags
);
600 log_link_warning_errno(link
, r
, "Failed to get RA prefix flags: %m");
604 if (flags
& ND_OPT_PI_FLAG_ONLINK
)
605 ndisc_router_process_onlink_prefix(link
, rt
);
606 if (flags
& ND_OPT_PI_FLAG_AUTO
)
607 ndisc_router_process_autonomous_prefix(link
, rt
);
612 case SD_NDISC_OPTION_ROUTE_INFORMATION
:
613 ndisc_router_process_route(link
, rt
);
616 case SD_NDISC_OPTION_RDNSS
:
617 if (link
->network
->ipv6_accept_ra_use_dns
)
618 ndisc_router_process_rdnss(link
, rt
);
621 case SD_NDISC_OPTION_DNSSL
:
622 if (link
->network
->ipv6_accept_ra_use_dns
)
623 ndisc_router_process_dnssl(link
, rt
);
627 r
= sd_ndisc_router_option_next(rt
);
631 static void ndisc_router_handler(Link
*link
, sd_ndisc_router
*rt
) {
636 assert(link
->network
);
637 assert(link
->manager
);
640 r
= sd_ndisc_router_get_flags(rt
, &flags
);
642 log_link_warning_errno(link
, r
, "Failed to get RA flags: %m");
646 if (flags
& (ND_RA_FLAG_MANAGED
| ND_RA_FLAG_OTHER
)) {
647 /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
648 r
= dhcp6_request_address(link
, !(flags
& ND_RA_FLAG_MANAGED
));
649 if (r
< 0 && r
!= -EBUSY
)
650 log_link_warning_errno(link
, r
, "Could not acquire DHCPv6 lease on NDisc request: %m");
652 log_link_debug(link
, "Acquiring DHCPv6 lease on NDisc request");
655 ndisc_router_process_default(link
, rt
);
656 ndisc_router_process_options(link
, rt
);
659 static void ndisc_handler(sd_ndisc
*nd
, sd_ndisc_event event
, sd_ndisc_router
*rt
, void *userdata
) {
660 Link
*link
= userdata
;
664 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
669 case SD_NDISC_EVENT_ROUTER
:
670 ndisc_router_handler(link
, rt
);
673 case SD_NDISC_EVENT_TIMEOUT
:
674 link
->ndisc_configured
= true;
675 link_check_ready(link
);
679 log_link_warning(link
, "IPv6 Neighbor Discovery unknown event: %d", event
);
683 int ndisc_configure(Link
*link
) {
688 r
= sd_ndisc_new(&link
->ndisc
);
692 r
= sd_ndisc_attach_event(link
->ndisc
, NULL
, 0);
696 r
= sd_ndisc_set_mac(link
->ndisc
, &link
->mac
);
700 r
= sd_ndisc_set_ifindex(link
->ndisc
, link
->ifindex
);
704 r
= sd_ndisc_set_callback(link
->ndisc
, ndisc_handler
, link
);
711 void ndisc_vacuum(Link
*link
) {
719 /* Removes all RDNSS and DNSSL entries whose validity time has passed */
721 time_now
= now(clock_boottime_or_monotonic());
723 SET_FOREACH(r
, link
->ndisc_rdnss
, i
)
724 if (r
->valid_until
< time_now
) {
725 free(set_remove(link
->ndisc_rdnss
, r
));
729 SET_FOREACH(d
, link
->ndisc_dnssl
, i
)
730 if (d
->valid_until
< time_now
) {
731 free(set_remove(link
->ndisc_dnssl
, d
));
736 void ndisc_flush(Link
*link
) {
739 /* Removes all RDNSS and DNSSL entries, without exception */
741 link
->ndisc_rdnss
= set_free_free(link
->ndisc_rdnss
);
742 link
->ndisc_dnssl
= set_free_free(link
->ndisc_dnssl
);