1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright (C) 2014 Intel Corporation. All rights reserved.
8 #include <netinet/icmp6.h>
13 #include "networkd-ndisc.h"
14 #include "networkd-route.h"
16 #define NDISC_DNSSL_MAX 64U
17 #define NDISC_RDNSS_MAX 64U
18 #define NDISC_PREFIX_LFT_MIN 7200U
20 static int ndisc_netlink_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, void *userdata
) {
21 _cleanup_(link_unrefp
) Link
*link
= userdata
;
25 assert(link
->ndisc_messages
> 0);
27 link
->ndisc_messages
--;
29 r
= sd_netlink_message_get_errno(m
);
30 if (r
< 0 && r
!= -EEXIST
)
31 log_link_error_errno(link
, r
, "Could not set NDisc route or address: %m");
33 if (link
->ndisc_messages
== 0) {
34 link
->ndisc_configured
= true;
35 link_check_ready(link
);
41 static void ndisc_router_process_default(Link
*link
, sd_ndisc_router
*rt
) {
42 _cleanup_(route_freep
) Route
*route
= NULL
;
43 struct in6_addr gateway
;
55 r
= sd_ndisc_router_get_lifetime(rt
, &lifetime
);
57 log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
60 if (lifetime
== 0) /* not a default router */
63 r
= sd_ndisc_router_get_address(rt
, &gateway
);
65 log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
69 SET_FOREACH(address
, link
->addresses
, i
) {
70 if (!memcmp(&gateway
, &address
->in_addr
.in6
,
71 sizeof(address
->in_addr
.in6
))) {
72 char buffer
[INET6_ADDRSTRLEN
];
74 log_link_debug(link
, "No NDisc route added, gateway %s matches local address",
76 &address
->in_addr
.in6
,
77 buffer
, sizeof(buffer
)));
82 SET_FOREACH(address
, link
->addresses_foreign
, 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 r
= sd_ndisc_router_get_preference(rt
, &preference
);
97 log_link_warning_errno(link
, r
, "Failed to get default router preference from RA: %m");
101 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
103 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
107 r
= sd_ndisc_router_get_mtu(rt
, &mtu
);
111 log_link_warning_errno(link
, r
, "Failed to get default router MTU from RA: %m");
115 r
= route_new(&route
);
117 log_link_error_errno(link
, r
, "Could not allocate route: %m");
121 route
->family
= AF_INET6
;
122 route
->table
= link
->network
->ipv6_accept_ra_route_table
;
123 route
->priority
= link
->network
->dhcp_route_metric
;
124 route
->protocol
= RTPROT_RA
;
125 route
->pref
= preference
;
126 route
->gw
.in6
= gateway
;
127 route
->lifetime
= time_now
+ lifetime
* USEC_PER_SEC
;
130 r
= route_configure(route
, link
, ndisc_netlink_handler
);
132 log_link_warning_errno(link
, r
, "Could not set default route: %m");
133 link_enter_failed(link
);
137 link
->ndisc_messages
++;
140 static void ndisc_router_process_autonomous_prefix(Link
*link
, sd_ndisc_router
*rt
) {
141 _cleanup_(address_freep
) Address
*address
= NULL
;
142 Address
*existing_address
;
143 uint32_t lifetime_valid
, lifetime_preferred
, lifetime_remaining
;
151 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
153 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
157 r
= sd_ndisc_router_prefix_get_prefixlen(rt
, &prefixlen
);
159 log_link_error_errno(link
, r
, "Failed to get prefix length: %m");
163 r
= sd_ndisc_router_prefix_get_valid_lifetime(rt
, &lifetime_valid
);
165 log_link_error_errno(link
, r
, "Failed to get prefix valid lifetime: %m");
169 r
= sd_ndisc_router_prefix_get_preferred_lifetime(rt
, &lifetime_preferred
);
171 log_link_error_errno(link
, r
, "Failed to get prefix preferred lifetime: %m");
175 /* The preferred lifetime is never greater than the valid lifetime */
176 if (lifetime_preferred
> lifetime_valid
)
179 r
= address_new(&address
);
181 log_link_error_errno(link
, r
, "Could not allocate address: %m");
185 address
->family
= AF_INET6
;
186 r
= sd_ndisc_router_prefix_get_address(rt
, &address
->in_addr
.in6
);
188 log_link_error_errno(link
, r
, "Failed to get prefix address: %m");
192 if (in_addr_is_null(AF_INET6
, (const union in_addr_union
*) &link
->network
->ipv6_token
) == 0)
193 memcpy(((char *)&address
->in_addr
.in6
) + 8, ((char *)&link
->network
->ipv6_token
) + 8, 8);
195 /* see RFC4291 section 2.5.1 */
196 address
->in_addr
.in6
.s6_addr
[8] = link
->mac
.ether_addr_octet
[0];
197 address
->in_addr
.in6
.s6_addr
[8] ^= 1 << 1;
198 address
->in_addr
.in6
.s6_addr
[9] = link
->mac
.ether_addr_octet
[1];
199 address
->in_addr
.in6
.s6_addr
[10] = link
->mac
.ether_addr_octet
[2];
200 address
->in_addr
.in6
.s6_addr
[11] = 0xff;
201 address
->in_addr
.in6
.s6_addr
[12] = 0xfe;
202 address
->in_addr
.in6
.s6_addr
[13] = link
->mac
.ether_addr_octet
[3];
203 address
->in_addr
.in6
.s6_addr
[14] = link
->mac
.ether_addr_octet
[4];
204 address
->in_addr
.in6
.s6_addr
[15] = link
->mac
.ether_addr_octet
[5];
206 address
->prefixlen
= prefixlen
;
207 address
->flags
= IFA_F_NOPREFIXROUTE
|IFA_F_MANAGETEMPADDR
;
208 address
->cinfo
.ifa_prefered
= lifetime_preferred
;
210 /* see RFC4862 section 5.5.3.e */
211 r
= address_get(link
, address
->family
, &address
->in_addr
, address
->prefixlen
, &existing_address
);
213 lifetime_remaining
= existing_address
->cinfo
.tstamp
/ 100 + existing_address
->cinfo
.ifa_valid
- time_now
/ USEC_PER_SEC
;
214 if (lifetime_valid
> NDISC_PREFIX_LFT_MIN
|| lifetime_valid
> lifetime_remaining
)
215 address
->cinfo
.ifa_valid
= lifetime_valid
;
216 else if (lifetime_remaining
<= NDISC_PREFIX_LFT_MIN
)
217 address
->cinfo
.ifa_valid
= lifetime_remaining
;
219 address
->cinfo
.ifa_valid
= NDISC_PREFIX_LFT_MIN
;
220 } else if (lifetime_valid
> 0)
221 address
->cinfo
.ifa_valid
= lifetime_valid
;
223 return; /* see RFC4862 section 5.5.3.d */
225 if (address
->cinfo
.ifa_valid
== 0)
228 r
= address_configure(address
, link
, ndisc_netlink_handler
, true);
230 log_link_warning_errno(link
, r
, "Could not set SLAAC address: %m");
231 link_enter_failed(link
);
235 link
->ndisc_messages
++;
238 static void ndisc_router_process_onlink_prefix(Link
*link
, sd_ndisc_router
*rt
) {
239 _cleanup_(route_freep
) Route
*route
= NULL
;
248 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
250 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
254 r
= sd_ndisc_router_prefix_get_prefixlen(rt
, &prefixlen
);
256 log_link_error_errno(link
, r
, "Failed to get prefix length: %m");
260 r
= sd_ndisc_router_prefix_get_valid_lifetime(rt
, &lifetime
);
262 log_link_error_errno(link
, r
, "Failed to get prefix lifetime: %m");
266 r
= route_new(&route
);
268 log_link_error_errno(link
, r
, "Could not allocate route: %m");
272 route
->family
= AF_INET6
;
273 route
->table
= link
->network
->ipv6_accept_ra_route_table
;
274 route
->priority
= link
->network
->dhcp_route_metric
;
275 route
->protocol
= RTPROT_RA
;
276 route
->flags
= RTM_F_PREFIX
;
277 route
->dst_prefixlen
= prefixlen
;
278 route
->lifetime
= time_now
+ lifetime
* USEC_PER_SEC
;
280 r
= sd_ndisc_router_prefix_get_address(rt
, &route
->dst
.in6
);
282 log_link_error_errno(link
, r
, "Failed to get prefix address: %m");
286 r
= route_configure(route
, link
, ndisc_netlink_handler
);
288 log_link_warning_errno(link
, r
, "Could not set prefix route: %m");
289 link_enter_failed(link
);
293 link
->ndisc_messages
++;
296 static void ndisc_router_process_route(Link
*link
, sd_ndisc_router
*rt
) {
297 _cleanup_(route_freep
) Route
*route
= NULL
;
298 struct in6_addr gateway
;
300 unsigned preference
, prefixlen
;
306 r
= sd_ndisc_router_route_get_lifetime(rt
, &lifetime
);
308 log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
314 r
= sd_ndisc_router_get_address(rt
, &gateway
);
316 log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
320 r
= sd_ndisc_router_route_get_prefixlen(rt
, &prefixlen
);
322 log_link_warning_errno(link
, r
, "Failed to get route prefix length: %m");
326 r
= sd_ndisc_router_route_get_preference(rt
, &preference
);
328 log_link_warning_errno(link
, r
, "Failed to get default router preference from RA: %m");
332 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
334 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
338 r
= route_new(&route
);
340 log_link_error_errno(link
, r
, "Could not allocate route: %m");
344 route
->family
= AF_INET6
;
345 route
->table
= link
->network
->ipv6_accept_ra_route_table
;
346 route
->protocol
= RTPROT_RA
;
347 route
->pref
= preference
;
348 route
->gw
.in6
= gateway
;
349 route
->dst_prefixlen
= prefixlen
;
350 route
->lifetime
= time_now
+ lifetime
* USEC_PER_SEC
;
352 r
= sd_ndisc_router_route_get_address(rt
, &route
->dst
.in6
);
354 log_link_error_errno(link
, r
, "Failed to get route address: %m");
358 r
= route_configure(route
, link
, ndisc_netlink_handler
);
360 log_link_warning_errno(link
, r
, "Could not set additional route: %m");
361 link_enter_failed(link
);
365 link
->ndisc_messages
++;
368 static void ndisc_rdnss_hash_func(const void *p
, struct siphash
*state
) {
369 const NDiscRDNSS
*x
= p
;
371 siphash24_compress(&x
->address
, sizeof(x
->address
), state
);
374 static int ndisc_rdnss_compare_func(const void *_a
, const void *_b
) {
375 const NDiscRDNSS
*a
= _a
, *b
= _b
;
377 return memcmp(&a
->address
, &b
->address
, sizeof(a
->address
));
380 static const struct hash_ops ndisc_rdnss_hash_ops
= {
381 .hash
= ndisc_rdnss_hash_func
,
382 .compare
= ndisc_rdnss_compare_func
385 static void ndisc_router_process_rdnss(Link
*link
, sd_ndisc_router
*rt
) {
387 const struct in6_addr
*a
;
394 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
396 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
400 r
= sd_ndisc_router_rdnss_get_lifetime(rt
, &lifetime
);
402 log_link_warning_errno(link
, r
, "Failed to get RDNSS lifetime: %m");
406 n
= sd_ndisc_router_rdnss_get_addresses(rt
, &a
);
408 log_link_warning_errno(link
, n
, "Failed to get RDNSS addresses: %m");
412 for (i
= 0; i
< n
; i
++) {
418 (void) set_remove(link
->ndisc_rdnss
, &d
);
423 x
= set_get(link
->ndisc_rdnss
, &d
);
425 x
->valid_until
= time_now
+ lifetime
* USEC_PER_SEC
;
431 if (set_size(link
->ndisc_rdnss
) >= NDISC_RDNSS_MAX
) {
432 log_link_warning(link
, "Too many RDNSS records per link, ignoring.");
436 r
= set_ensure_allocated(&link
->ndisc_rdnss
, &ndisc_rdnss_hash_ops
);
442 x
= new0(NDiscRDNSS
, 1);
449 x
->valid_until
= time_now
+ lifetime
* USEC_PER_SEC
;
451 r
= set_put(link
->ndisc_rdnss
, x
);
463 static void ndisc_dnssl_hash_func(const void *p
, struct siphash
*state
) {
464 const NDiscDNSSL
*x
= p
;
466 siphash24_compress(NDISC_DNSSL_DOMAIN(x
), strlen(NDISC_DNSSL_DOMAIN(x
)), state
);
469 static int ndisc_dnssl_compare_func(const void *_a
, const void *_b
) {
470 const NDiscDNSSL
*a
= _a
, *b
= _b
;
472 return strcmp(NDISC_DNSSL_DOMAIN(a
), NDISC_DNSSL_DOMAIN(b
));
475 static const struct hash_ops ndisc_dnssl_hash_ops
= {
476 .hash
= ndisc_dnssl_hash_func
,
477 .compare
= ndisc_dnssl_compare_func
480 static void ndisc_router_process_dnssl(Link
*link
, sd_ndisc_router
*rt
) {
481 _cleanup_strv_free_
char **l
= NULL
;
490 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
492 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
496 r
= sd_ndisc_router_dnssl_get_lifetime(rt
, &lifetime
);
498 log_link_warning_errno(link
, r
, "Failed to get RDNSS lifetime: %m");
502 r
= sd_ndisc_router_dnssl_get_domains(rt
, &l
);
504 log_link_warning_errno(link
, r
, "Failed to get RDNSS addresses: %m");
509 _cleanup_free_ NDiscDNSSL
*s
;
512 s
= malloc0(ALIGN(sizeof(NDiscDNSSL
)) + strlen(*i
) + 1);
518 strcpy(NDISC_DNSSL_DOMAIN(s
), *i
);
521 (void) set_remove(link
->ndisc_dnssl
, s
);
526 x
= set_get(link
->ndisc_dnssl
, s
);
528 x
->valid_until
= time_now
+ lifetime
* USEC_PER_SEC
;
534 if (set_size(link
->ndisc_dnssl
) >= NDISC_DNSSL_MAX
) {
535 log_link_warning(link
, "Too many DNSSL records per link, ignoring.");
539 r
= set_ensure_allocated(&link
->ndisc_dnssl
, &ndisc_dnssl_hash_ops
);
545 s
->valid_until
= time_now
+ lifetime
* USEC_PER_SEC
;
547 r
= set_put(link
->ndisc_dnssl
, s
);
559 static void ndisc_router_process_options(Link
*link
, sd_ndisc_router
*rt
) {
565 r
= sd_ndisc_router_option_rewind(rt
);
570 log_link_warning_errno(link
, r
, "Failed to iterate through options: %m");
573 if (r
== 0) /* EOF */
576 r
= sd_ndisc_router_option_get_type(rt
, &type
);
578 log_link_warning_errno(link
, r
, "Failed to get RA option type: %m");
584 case SD_NDISC_OPTION_PREFIX_INFORMATION
: {
587 r
= sd_ndisc_router_prefix_get_flags(rt
, &flags
);
589 log_link_warning_errno(link
, r
, "Failed to get RA prefix flags: %m");
593 if (flags
& ND_OPT_PI_FLAG_ONLINK
)
594 ndisc_router_process_onlink_prefix(link
, rt
);
595 if (flags
& ND_OPT_PI_FLAG_AUTO
)
596 ndisc_router_process_autonomous_prefix(link
, rt
);
601 case SD_NDISC_OPTION_ROUTE_INFORMATION
:
602 ndisc_router_process_route(link
, rt
);
605 case SD_NDISC_OPTION_RDNSS
:
606 if (link
->network
->ipv6_accept_ra_use_dns
)
607 ndisc_router_process_rdnss(link
, rt
);
610 case SD_NDISC_OPTION_DNSSL
:
611 if (link
->network
->ipv6_accept_ra_use_dns
)
612 ndisc_router_process_dnssl(link
, rt
);
616 r
= sd_ndisc_router_option_next(rt
);
620 static void ndisc_router_handler(Link
*link
, sd_ndisc_router
*rt
) {
625 assert(link
->network
);
626 assert(link
->manager
);
629 r
= sd_ndisc_router_get_flags(rt
, &flags
);
631 log_link_warning_errno(link
, r
, "Failed to get RA flags: %m");
635 if (flags
& (ND_RA_FLAG_MANAGED
| ND_RA_FLAG_OTHER
)) {
636 /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
637 r
= dhcp6_request_address(link
, !(flags
& ND_RA_FLAG_MANAGED
));
638 if (r
< 0 && r
!= -EBUSY
)
639 log_link_warning_errno(link
, r
, "Could not acquire DHCPv6 lease on NDisc request: %m");
641 log_link_debug(link
, "Acquiring DHCPv6 lease on NDisc request");
644 ndisc_router_process_default(link
, rt
);
645 ndisc_router_process_options(link
, rt
);
648 static void ndisc_handler(sd_ndisc
*nd
, sd_ndisc_event event
, sd_ndisc_router
*rt
, void *userdata
) {
649 Link
*link
= userdata
;
653 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
658 case SD_NDISC_EVENT_ROUTER
:
659 ndisc_router_handler(link
, rt
);
662 case SD_NDISC_EVENT_TIMEOUT
:
663 link
->ndisc_configured
= true;
664 link_check_ready(link
);
668 log_link_warning(link
, "IPv6 Neighbor Discovery unknown event: %d", event
);
672 int ndisc_configure(Link
*link
) {
677 r
= sd_ndisc_new(&link
->ndisc
);
681 r
= sd_ndisc_attach_event(link
->ndisc
, NULL
, 0);
685 r
= sd_ndisc_set_mac(link
->ndisc
, &link
->mac
);
689 r
= sd_ndisc_set_ifindex(link
->ndisc
, link
->ifindex
);
693 r
= sd_ndisc_set_callback(link
->ndisc
, ndisc_handler
, link
);
700 void ndisc_vacuum(Link
*link
) {
708 /* Removes all RDNSS and DNSSL entries whose validity time has passed */
710 time_now
= now(clock_boottime_or_monotonic());
712 SET_FOREACH(r
, link
->ndisc_rdnss
, i
)
713 if (r
->valid_until
< time_now
) {
714 free(set_remove(link
->ndisc_rdnss
, r
));
718 SET_FOREACH(d
, link
->ndisc_dnssl
, i
)
719 if (d
->valid_until
< time_now
) {
720 free(set_remove(link
->ndisc_dnssl
, d
));
725 void ndisc_flush(Link
*link
) {
728 /* Removes all RDNSS and DNSSL entries, without exception */
730 link
->ndisc_rdnss
= set_free_free(link
->ndisc_rdnss
);
731 link
->ndisc_dnssl
= set_free_free(link
->ndisc_dnssl
);