2 This file is part of systemd.
4 Copyright (C) 2014 Intel Corporation. All rights reserved.
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 #include <netinet/icmp6.h>
25 #include "networkd-ndisc.h"
27 #define NDISC_DNSSL_MAX 64U
28 #define NDISC_RDNSS_MAX 64U
30 static int ndisc_netlink_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, void *userdata
) {
31 _cleanup_link_unref_ Link
*link
= userdata
;
35 assert(link
->ndisc_messages
> 0);
37 link
->ndisc_messages
--;
39 r
= sd_netlink_message_get_errno(m
);
40 if (r
< 0 && r
!= -EEXIST
) {
41 log_link_error_errno(link
, r
, "Could not set NDisc route or address: %m");
42 link_enter_failed(link
);
45 if (link
->ndisc_messages
== 0) {
46 link
->ndisc_configured
= true;
47 link_check_ready(link
);
53 static void ndisc_router_process_default(Link
*link
, sd_ndisc_router
*rt
) {
54 _cleanup_route_free_ Route
*route
= NULL
;
55 struct in6_addr gateway
;
66 r
= sd_ndisc_router_get_lifetime(rt
, &lifetime
);
68 log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
71 if (lifetime
== 0) /* not a default router */
74 r
= sd_ndisc_router_get_address(rt
, &gateway
);
76 log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
80 SET_FOREACH(address
, link
->addresses
, i
) {
81 if (!memcmp(&gateway
, &address
->in_addr
.in6
,
82 sizeof(address
->in_addr
.in6
))) {
83 char buffer
[INET6_ADDRSTRLEN
];
85 log_link_debug(link
, "No NDisc route added, gateway %s matches local address",
87 &address
->in_addr
.in6
,
88 buffer
, sizeof(buffer
)));
93 SET_FOREACH(address
, link
->addresses_foreign
, i
) {
94 if (!memcmp(&gateway
, &address
->in_addr
.in6
,
95 sizeof(address
->in_addr
.in6
))) {
96 char buffer
[INET6_ADDRSTRLEN
];
98 log_link_debug(link
, "No NDisc route added, gateway %s matches local address",
100 &address
->in_addr
.in6
,
101 buffer
, sizeof(buffer
)));
106 r
= sd_ndisc_router_get_preference(rt
, &preference
);
108 log_link_warning_errno(link
, r
, "Failed to get default router preference from RA: %m");
112 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
114 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
118 r
= route_new(&route
);
120 log_link_error_errno(link
, r
, "Could not allocate route: %m");
124 route
->family
= AF_INET6
;
125 route
->table
= link
->network
->ipv6_accept_ra_route_table
;
126 route
->protocol
= RTPROT_RA
;
127 route
->pref
= preference
;
128 route
->gw
.in6
= gateway
;
129 route
->lifetime
= time_now
+ lifetime
* USEC_PER_SEC
;
131 r
= route_configure(route
, link
, ndisc_netlink_handler
);
133 log_link_warning_errno(link
, r
, "Could not set default route: %m");
134 link_enter_failed(link
);
138 link
->ndisc_messages
++;
141 static void ndisc_router_process_autonomous_prefix(Link
*link
, sd_ndisc_router
*rt
) {
142 _cleanup_address_free_ Address
*address
= NULL
;
143 uint32_t lifetime_valid
, lifetime_preferred
;
150 r
= sd_ndisc_router_prefix_get_prefixlen(rt
, &prefixlen
);
152 log_link_error_errno(link
, r
, "Failed to get prefix length: %m");
156 r
= sd_ndisc_router_prefix_get_valid_lifetime(rt
, &lifetime_valid
);
158 log_link_error_errno(link
, r
, "Failed to get prefix valid lifetime: %m");
162 r
= sd_ndisc_router_prefix_get_preferred_lifetime(rt
, &lifetime_preferred
);
164 log_link_error_errno(link
, r
, "Failed to get prefix preferred lifetime: %m");
168 r
= address_new(&address
);
170 log_link_error_errno(link
, r
, "Could not allocate address: %m");
174 address
->family
= AF_INET6
;
175 r
= sd_ndisc_router_prefix_get_address(rt
, &address
->in_addr
.in6
);
177 log_link_error_errno(link
, r
, "Failed to get prefix address: %m");
181 if (in_addr_is_null(AF_INET6
, (const union in_addr_union
*) &link
->network
->ipv6_token
) == 0)
182 memcpy(((char *)&address
->in_addr
.in6
) + 8, ((char *)&link
->network
->ipv6_token
) + 8, 8);
184 /* see RFC4291 section 2.5.1 */
185 address
->in_addr
.in6
.s6_addr
[8] = link
->mac
.ether_addr_octet
[0];
186 address
->in_addr
.in6
.s6_addr
[8] ^= 1 << 1;
187 address
->in_addr
.in6
.s6_addr
[9] = link
->mac
.ether_addr_octet
[1];
188 address
->in_addr
.in6
.s6_addr
[10] = link
->mac
.ether_addr_octet
[2];
189 address
->in_addr
.in6
.s6_addr
[11] = 0xff;
190 address
->in_addr
.in6
.s6_addr
[12] = 0xfe;
191 address
->in_addr
.in6
.s6_addr
[13] = link
->mac
.ether_addr_octet
[3];
192 address
->in_addr
.in6
.s6_addr
[14] = link
->mac
.ether_addr_octet
[4];
193 address
->in_addr
.in6
.s6_addr
[15] = link
->mac
.ether_addr_octet
[5];
195 address
->prefixlen
= prefixlen
;
196 address
->flags
= IFA_F_NOPREFIXROUTE
|IFA_F_MANAGETEMPADDR
;
197 address
->cinfo
.ifa_prefered
= lifetime_preferred
;
198 address
->cinfo
.ifa_valid
= lifetime_valid
;
200 r
= address_configure(address
, link
, ndisc_netlink_handler
, true);
202 log_link_warning_errno(link
, r
, "Could not set SLAAC address: %m");
203 link_enter_failed(link
);
207 link
->ndisc_messages
++;
210 static void ndisc_router_process_onlink_prefix(Link
*link
, sd_ndisc_router
*rt
) {
211 _cleanup_route_free_ Route
*route
= NULL
;
220 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
222 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
226 r
= sd_ndisc_router_prefix_get_prefixlen(rt
, &prefixlen
);
228 log_link_error_errno(link
, r
, "Failed to get prefix length: %m");
232 r
= sd_ndisc_router_prefix_get_valid_lifetime(rt
, &lifetime
);
234 log_link_error_errno(link
, r
, "Failed to get prefix lifetime: %m");
238 r
= route_new(&route
);
240 log_link_error_errno(link
, r
, "Could not allocate route: %m");
244 route
->family
= AF_INET6
;
245 route
->table
= link
->network
->ipv6_accept_ra_route_table
;
246 route
->protocol
= RTPROT_RA
;
247 route
->flags
= RTM_F_PREFIX
;
248 route
->dst_prefixlen
= prefixlen
;
249 route
->lifetime
= time_now
+ lifetime
* USEC_PER_SEC
;
251 r
= sd_ndisc_router_prefix_get_address(rt
, &route
->dst
.in6
);
253 log_link_error_errno(link
, r
, "Failed to get prefix address: %m");
257 r
= route_configure(route
, link
, ndisc_netlink_handler
);
259 log_link_warning_errno(link
, r
, "Could not set prefix route: %m");
260 link_enter_failed(link
);
264 link
->ndisc_messages
++;
267 static void ndisc_router_process_route(Link
*link
, sd_ndisc_router
*rt
) {
268 _cleanup_route_free_ Route
*route
= NULL
;
269 struct in6_addr gateway
;
271 unsigned preference
, prefixlen
;
277 r
= sd_ndisc_router_route_get_lifetime(rt
, &lifetime
);
279 log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
285 r
= sd_ndisc_router_get_address(rt
, &gateway
);
287 log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
291 r
= sd_ndisc_router_route_get_prefixlen(rt
, &prefixlen
);
293 log_link_warning_errno(link
, r
, "Failed to get route prefix length: %m");
297 r
= sd_ndisc_router_route_get_preference(rt
, &preference
);
299 log_link_warning_errno(link
, r
, "Failed to get default router preference from RA: %m");
303 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
305 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
309 r
= route_new(&route
);
311 log_link_error_errno(link
, r
, "Could not allocate route: %m");
315 route
->family
= AF_INET6
;
316 route
->table
= link
->network
->ipv6_accept_ra_route_table
;
317 route
->protocol
= RTPROT_RA
;
318 route
->pref
= preference
;
319 route
->gw
.in6
= gateway
;
320 route
->dst_prefixlen
= prefixlen
;
321 route
->lifetime
= time_now
+ lifetime
* USEC_PER_SEC
;
323 r
= sd_ndisc_router_route_get_address(rt
, &route
->dst
.in6
);
325 log_link_error_errno(link
, r
, "Failed to get route address: %m");
329 r
= route_configure(route
, link
, ndisc_netlink_handler
);
331 log_link_warning_errno(link
, r
, "Could not set additional route: %m");
332 link_enter_failed(link
);
336 link
->ndisc_messages
++;
339 static void ndisc_rdnss_hash_func(const void *p
, struct siphash
*state
) {
340 const NDiscRDNSS
*x
= p
;
342 siphash24_compress(&x
->address
, sizeof(x
->address
), state
);
345 static int ndisc_rdnss_compare_func(const void *_a
, const void *_b
) {
346 const NDiscRDNSS
*a
= _a
, *b
= _b
;
348 return memcmp(&a
->address
, &b
->address
, sizeof(a
->address
));
351 static const struct hash_ops ndisc_rdnss_hash_ops
= {
352 .hash
= ndisc_rdnss_hash_func
,
353 .compare
= ndisc_rdnss_compare_func
356 static void ndisc_router_process_rdnss(Link
*link
, sd_ndisc_router
*rt
) {
358 const struct in6_addr
*a
;
365 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
367 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
371 r
= sd_ndisc_router_rdnss_get_lifetime(rt
, &lifetime
);
373 log_link_warning_errno(link
, r
, "Failed to get RDNSS lifetime: %m");
377 n
= sd_ndisc_router_rdnss_get_addresses(rt
, &a
);
379 log_link_warning_errno(link
, n
, "Failed to get RDNSS addresses: %m");
383 for (i
= 0; i
< n
; i
++) {
389 (void) set_remove(link
->ndisc_rdnss
, &d
);
394 x
= set_get(link
->ndisc_rdnss
, &d
);
396 x
->valid_until
= time_now
+ lifetime
* USEC_PER_SEC
;
402 if (set_size(link
->ndisc_rdnss
) >= NDISC_RDNSS_MAX
) {
403 log_link_warning(link
, "Too many RDNSS records per link, ignoring.");
407 r
= set_ensure_allocated(&link
->ndisc_rdnss
, &ndisc_rdnss_hash_ops
);
413 x
= new0(NDiscRDNSS
, 1);
420 x
->valid_until
= time_now
+ lifetime
* USEC_PER_SEC
;
422 r
= set_put(link
->ndisc_rdnss
, x
);
434 static void ndisc_dnssl_hash_func(const void *p
, struct siphash
*state
) {
435 const NDiscDNSSL
*x
= p
;
437 siphash24_compress(NDISC_DNSSL_DOMAIN(x
), strlen(NDISC_DNSSL_DOMAIN(x
)), state
);
440 static int ndisc_dnssl_compare_func(const void *_a
, const void *_b
) {
441 const NDiscDNSSL
*a
= _a
, *b
= _b
;
443 return strcmp(NDISC_DNSSL_DOMAIN(a
), NDISC_DNSSL_DOMAIN(b
));
446 static const struct hash_ops ndisc_dnssl_hash_ops
= {
447 .hash
= ndisc_dnssl_hash_func
,
448 .compare
= ndisc_dnssl_compare_func
451 static void ndisc_router_process_dnssl(Link
*link
, sd_ndisc_router
*rt
) {
452 _cleanup_strv_free_
char **l
= NULL
;
461 r
= sd_ndisc_router_get_timestamp(rt
, clock_boottime_or_monotonic(), &time_now
);
463 log_link_warning_errno(link
, r
, "Failed to get RA timestamp: %m");
467 r
= sd_ndisc_router_dnssl_get_lifetime(rt
, &lifetime
);
469 log_link_warning_errno(link
, r
, "Failed to get RDNSS lifetime: %m");
473 r
= sd_ndisc_router_dnssl_get_domains(rt
, &l
);
475 log_link_warning_errno(link
, r
, "Failed to get RDNSS addresses: %m");
480 _cleanup_free_ NDiscDNSSL
*s
;
483 s
= malloc0(ALIGN(sizeof(NDiscDNSSL
)) + strlen(*i
) + 1);
489 strcpy(NDISC_DNSSL_DOMAIN(s
), *i
);
492 (void) set_remove(link
->ndisc_dnssl
, s
);
497 x
= set_get(link
->ndisc_dnssl
, s
);
499 x
->valid_until
= time_now
+ lifetime
* USEC_PER_SEC
;
505 if (set_size(link
->ndisc_dnssl
) >= NDISC_DNSSL_MAX
) {
506 log_link_warning(link
, "Too many DNSSL records per link, ignoring.");
510 r
= set_ensure_allocated(&link
->ndisc_dnssl
, &ndisc_dnssl_hash_ops
);
516 s
->valid_until
= time_now
+ lifetime
* USEC_PER_SEC
;
518 r
= set_put(link
->ndisc_dnssl
, s
);
530 static void ndisc_router_process_options(Link
*link
, sd_ndisc_router
*rt
) {
536 r
= sd_ndisc_router_option_rewind(rt
);
541 log_link_warning_errno(link
, r
, "Failed to iterate through options: %m");
544 if (r
== 0) /* EOF */
547 r
= sd_ndisc_router_option_get_type(rt
, &type
);
549 log_link_warning_errno(link
, r
, "Failed to get RA option type: %m");
555 case SD_NDISC_OPTION_PREFIX_INFORMATION
: {
558 r
= sd_ndisc_router_prefix_get_flags(rt
, &flags
);
560 log_link_warning_errno(link
, r
, "Failed to get RA prefix flags: %m");
564 if (flags
& ND_OPT_PI_FLAG_ONLINK
)
565 ndisc_router_process_onlink_prefix(link
, rt
);
566 if (flags
& ND_OPT_PI_FLAG_AUTO
)
567 ndisc_router_process_autonomous_prefix(link
, rt
);
572 case SD_NDISC_OPTION_ROUTE_INFORMATION
:
573 ndisc_router_process_route(link
, rt
);
576 case SD_NDISC_OPTION_RDNSS
:
577 ndisc_router_process_rdnss(link
, rt
);
580 case SD_NDISC_OPTION_DNSSL
:
581 ndisc_router_process_dnssl(link
, rt
);
585 r
= sd_ndisc_router_option_next(rt
);
589 static void ndisc_router_handler(Link
*link
, sd_ndisc_router
*rt
) {
594 assert(link
->network
);
595 assert(link
->manager
);
598 r
= sd_ndisc_router_get_flags(rt
, &flags
);
600 log_link_warning_errno(link
, r
, "Failed to get RA flags: %m");
604 if (flags
& (ND_RA_FLAG_MANAGED
| ND_RA_FLAG_OTHER
)) {
605 /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
606 r
= dhcp6_request_address(link
, !(flags
& ND_RA_FLAG_MANAGED
));
607 if (r
< 0 && r
!= -EBUSY
)
608 log_link_warning_errno(link
, r
, "Could not acquire DHCPv6 lease on NDisc request: %m");
610 log_link_debug(link
, "Acquiring DHCPv6 lease on NDisc request");
613 ndisc_router_process_default(link
, rt
);
614 ndisc_router_process_options(link
, rt
);
617 static void ndisc_handler(sd_ndisc
*nd
, sd_ndisc_event event
, sd_ndisc_router
*rt
, void *userdata
) {
618 Link
*link
= userdata
;
622 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
627 case SD_NDISC_EVENT_ROUTER
:
628 ndisc_router_handler(link
, rt
);
631 case SD_NDISC_EVENT_TIMEOUT
:
632 link
->ndisc_configured
= true;
633 link_check_ready(link
);
637 log_link_warning(link
, "IPv6 Neighbor Discovery unknown event: %d", event
);
641 int ndisc_configure(Link
*link
) {
646 r
= sd_ndisc_new(&link
->ndisc
);
650 r
= sd_ndisc_attach_event(link
->ndisc
, NULL
, 0);
654 r
= sd_ndisc_set_mac(link
->ndisc
, &link
->mac
);
658 r
= sd_ndisc_set_ifindex(link
->ndisc
, link
->ifindex
);
662 r
= sd_ndisc_set_callback(link
->ndisc
, ndisc_handler
, link
);
669 void ndisc_vacuum(Link
*link
) {
677 /* Removes all RDNSS and DNSSL entries whose validity time has passed */
679 time_now
= now(clock_boottime_or_monotonic());
681 SET_FOREACH(r
, link
->ndisc_rdnss
, i
)
682 if (r
->valid_until
< time_now
) {
683 (void) set_remove(link
->ndisc_rdnss
, r
);
687 SET_FOREACH(d
, link
->ndisc_dnssl
, i
)
688 if (d
->valid_until
< time_now
) {
689 (void) set_remove(link
->ndisc_dnssl
, d
);