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>
21 #include <netinet/in.h>
22 #include <netinet/ip6.h>
25 #include <sys/ioctl.h>
29 #include "alloc-util.h"
31 #include "icmp6-util.h"
32 #include "in-addr-util.h"
34 #include "socket-util.h"
35 #include "string-util.h"
37 #define NDISC_ROUTER_SOLICITATION_INTERVAL 4 * USEC_PER_SEC
38 #define NDISC_MAX_ROUTER_SOLICITATIONS 3
42 NDISC_STATE_SOLICITATION_SENT
,
43 NDISC_STATE_ADVERTISMENT_LISTEN
,
45 _NDISC_STATE_INVALID
= -1,
48 #define IP6_MIN_MTU (unsigned)1280
49 #define ICMP6_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr))
50 #define NDISC_OPT_LEN_UNITS 8
52 #define ND_RA_FLAG_PREF 0x18
53 #define ND_RA_FLAG_PREF_LOW 0x03
54 #define ND_RA_FLAG_PREF_MEDIUM 0x0
55 #define ND_RA_FLAG_PREF_HIGH 0x1
56 #define ND_RA_FLAG_PREF_INVALID 0x2
58 typedef struct NDiscPrefix NDiscPrefix
;
65 LIST_FIELDS(NDiscPrefix
, prefixes
);
75 enum NDiscState state
;
79 struct ether_addr mac_addr
;
81 LIST_HEAD(NDiscPrefix
, prefixes
);
83 sd_event_source
*recv
;
84 sd_event_source
*timeout
;
86 sd_ndisc_router_callback_t router_callback
;
87 sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback
;
88 sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback
;
89 sd_ndisc_callback_t callback
;
93 #define log_ndisc(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "NDisc CLIENT: " fmt, ##__VA_ARGS__)
95 static NDiscPrefix
*ndisc_prefix_unref(NDiscPrefix
*prefix
) {
100 assert(prefix
->n_ref
> 0);
103 if (prefix
->n_ref
> 0)
107 LIST_REMOVE(prefixes
, prefix
->nd
->prefixes
, prefix
);
114 static int ndisc_prefix_new(sd_ndisc
*nd
, NDiscPrefix
**ret
) {
115 _cleanup_free_ NDiscPrefix
*prefix
= NULL
;
119 prefix
= new0(NDiscPrefix
, 1);
124 LIST_INIT(prefixes
, prefix
);
133 int sd_ndisc_set_callback(sd_ndisc
*nd
,
134 sd_ndisc_router_callback_t router_callback
,
135 sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback
,
136 sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback
,
137 sd_ndisc_callback_t callback
,
141 nd
->router_callback
= router_callback
;
142 nd
->prefix_onlink_callback
= prefix_onlink_callback
;
143 nd
->prefix_autonomous_callback
= prefix_autonomous_callback
;
144 nd
->callback
= callback
;
145 nd
->userdata
= userdata
;
150 int sd_ndisc_set_index(sd_ndisc
*nd
, int interface_index
) {
152 assert(interface_index
>= -1);
154 nd
->index
= interface_index
;
159 int sd_ndisc_set_mac(sd_ndisc
*nd
, const struct ether_addr
*mac_addr
) {
163 memcpy(&nd
->mac_addr
, mac_addr
, sizeof(nd
->mac_addr
));
171 int sd_ndisc_attach_event(sd_ndisc
*nd
, sd_event
*event
, int priority
) {
174 assert_return(nd
, -EINVAL
);
175 assert_return(!nd
->event
, -EBUSY
);
178 nd
->event
= sd_event_ref(event
);
180 r
= sd_event_default(&nd
->event
);
185 nd
->event_priority
= priority
;
190 int sd_ndisc_detach_event(sd_ndisc
*nd
) {
191 assert_return(nd
, -EINVAL
);
193 nd
->event
= sd_event_unref(nd
->event
);
198 sd_event
*sd_ndisc_get_event(sd_ndisc
*nd
) {
204 sd_ndisc
*sd_ndisc_ref(sd_ndisc
*nd
) {
209 assert(nd
->n_ref
> 0);
215 static int ndisc_init(sd_ndisc
*nd
) {
218 nd
->recv
= sd_event_source_unref(nd
->recv
);
219 nd
->fd
= asynchronous_close(nd
->fd
);
220 nd
->timeout
= sd_event_source_unref(nd
->timeout
);
225 sd_ndisc
*sd_ndisc_unref(sd_ndisc
*nd
) {
226 NDiscPrefix
*prefix
, *p
;
231 assert(nd
->n_ref
> 0);
238 sd_ndisc_detach_event(nd
);
240 LIST_FOREACH_SAFE(prefixes
, prefix
, p
, nd
->prefixes
)
241 prefix
= ndisc_prefix_unref(prefix
);
248 int sd_ndisc_new(sd_ndisc
**ret
) {
249 _cleanup_(sd_ndisc_unrefp
) sd_ndisc
*nd
= NULL
;
253 nd
= new0(sd_ndisc
, 1);
262 LIST_HEAD_INIT(nd
->prefixes
);
270 int sd_ndisc_get_mtu(sd_ndisc
*nd
, uint32_t *mtu
) {
271 assert_return(nd
, -EINVAL
);
272 assert_return(mtu
, -EINVAL
);
282 static int prefix_match(const struct in6_addr
*prefix
, uint8_t prefixlen
,
283 const struct in6_addr
*addr
,
284 uint8_t addr_prefixlen
) {
285 uint8_t bytes
, mask
, len
;
287 assert_return(prefix
, -EINVAL
);
288 assert_return(addr
, -EINVAL
);
290 len
= MIN(prefixlen
, addr_prefixlen
);
293 mask
= 0xff << (8 - len
% 8);
295 if (memcmp(prefix
, addr
, bytes
) != 0 ||
296 (prefix
->s6_addr
[bytes
] & mask
) != (addr
->s6_addr
[bytes
] & mask
))
297 return -EADDRNOTAVAIL
;
302 static int ndisc_prefix_match(sd_ndisc
*nd
, const struct in6_addr
*addr
,
303 uint8_t addr_len
, NDiscPrefix
**result
) {
304 NDiscPrefix
*prefix
, *p
;
310 r
= sd_event_now(nd
->event
, clock_boottime_or_monotonic(), &time_now
);
314 LIST_FOREACH_SAFE(prefixes
, prefix
, p
, nd
->prefixes
) {
315 if (prefix
->valid_until
< time_now
) {
316 prefix
= ndisc_prefix_unref(prefix
);
321 if (prefix_match(&prefix
->addr
, prefix
->len
, addr
, addr_len
) >= 0) {
327 return -EADDRNOTAVAIL
;
330 static int ndisc_prefix_update(sd_ndisc
*nd
, ssize_t len
,
331 const struct nd_opt_prefix_info
*prefix_opt
) {
333 uint32_t lifetime_valid
, lifetime_preferred
;
335 char time_string
[FORMAT_TIMESPAN_MAX
];
341 if (len
< prefix_opt
->nd_opt_pi_len
)
344 if (!(prefix_opt
->nd_opt_pi_flags_reserved
& (ND_OPT_PI_FLAG_ONLINK
| ND_OPT_PI_FLAG_AUTO
)))
347 if (in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &prefix_opt
->nd_opt_pi_prefix
) > 0)
350 lifetime_valid
= be32toh(prefix_opt
->nd_opt_pi_valid_time
);
351 lifetime_preferred
= be32toh(prefix_opt
->nd_opt_pi_preferred_time
);
353 if (lifetime_valid
< lifetime_preferred
)
356 r
= ndisc_prefix_match(nd
, &prefix_opt
->nd_opt_pi_prefix
,
357 prefix_opt
->nd_opt_pi_prefix_len
, &prefix
);
359 if (r
< 0 && r
!= -EADDRNOTAVAIL
)
362 /* if router advertisment prefix valid timeout is zero, the timeout
363 callback will be called immediately to clean up the prefix */
365 if (r
== -EADDRNOTAVAIL
) {
366 r
= ndisc_prefix_new(nd
, &prefix
);
370 prefix
->len
= prefix_opt
->nd_opt_pi_prefix_len
;
372 memcpy(&prefix
->addr
, &prefix_opt
->nd_opt_pi_prefix
,
373 sizeof(prefix
->addr
));
375 log_ndisc(nd
, "New prefix "SD_NDISC_ADDRESS_FORMAT_STR
"/%d lifetime %d expires in %s",
376 SD_NDISC_ADDRESS_FORMAT_VAL(prefix
->addr
),
377 prefix
->len
, lifetime_valid
,
378 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, lifetime_valid
* USEC_PER_SEC
, USEC_PER_SEC
));
380 LIST_PREPEND(prefixes
, nd
->prefixes
, prefix
);
383 if (prefix
->len
!= prefix_opt
->nd_opt_pi_prefix_len
) {
386 prefixlen
= MIN(prefix
->len
, prefix_opt
->nd_opt_pi_prefix_len
);
388 log_ndisc(nd
, "Prefix length mismatch %d/%d using %d",
390 prefix_opt
->nd_opt_pi_prefix_len
,
393 prefix
->len
= prefixlen
;
396 log_ndisc(nd
, "Update prefix "SD_NDISC_ADDRESS_FORMAT_STR
"/%d lifetime %d expires in %s",
397 SD_NDISC_ADDRESS_FORMAT_VAL(prefix
->addr
),
398 prefix
->len
, lifetime_valid
,
399 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, lifetime_valid
* USEC_PER_SEC
, USEC_PER_SEC
));
402 r
= sd_event_now(nd
->event
, clock_boottime_or_monotonic(), &time_now
);
406 prefix
->valid_until
= time_now
+ lifetime_valid
* USEC_PER_SEC
;
408 if ((prefix_opt
->nd_opt_pi_flags_reserved
& ND_OPT_PI_FLAG_ONLINK
) && nd
->prefix_onlink_callback
)
409 nd
->prefix_onlink_callback(nd
, &prefix
->addr
, prefix
->len
, prefix
->valid_until
, nd
->userdata
);
411 if ((prefix_opt
->nd_opt_pi_flags_reserved
& ND_OPT_PI_FLAG_AUTO
) && nd
->prefix_autonomous_callback
)
412 nd
->prefix_autonomous_callback(nd
, &prefix
->addr
, prefix
->len
, lifetime_preferred
, lifetime_valid
,
418 static int ndisc_ra_parse(sd_ndisc
*nd
, struct nd_router_advert
*ra
, ssize_t len
) {
420 struct nd_opt_hdr
*opt_hdr
;
422 assert_return(nd
, -EINVAL
);
423 assert_return(ra
, -EINVAL
);
426 if (len
< NDISC_OPT_LEN_UNITS
) {
427 log_ndisc(nd
, "Router Advertisement below minimum length");
435 while (len
!= 0 && len
>= opt_hdr
->nd_opt_len
* NDISC_OPT_LEN_UNITS
) {
436 struct nd_opt_mtu
*opt_mtu
;
438 struct nd_opt_prefix_info
*opt_prefix
;
440 if (opt_hdr
->nd_opt_len
== 0)
443 switch (opt_hdr
->nd_opt_type
) {
447 mtu
= be32toh(opt_mtu
->nd_opt_mtu_mtu
);
449 if (mtu
!= nd
->mtu
) {
450 nd
->mtu
= MAX(mtu
, IP6_MIN_MTU
);
452 log_ndisc(nd
, "Router Advertisement link MTU %d using %d",
458 case ND_OPT_PREFIX_INFORMATION
:
461 ndisc_prefix_update(nd
, len
, opt_prefix
);
466 len
-= opt_hdr
->nd_opt_len
* NDISC_OPT_LEN_UNITS
;
467 opt
= (void *)((char *)opt
+
468 opt_hdr
->nd_opt_len
* NDISC_OPT_LEN_UNITS
);
473 log_ndisc(nd
, "Router Advertisement contains %zd bytes of trailing garbage", len
);
478 static int ndisc_router_advertisment_recv(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
479 _cleanup_free_
struct nd_router_advert
*ra
= NULL
;
480 sd_ndisc
*nd
= userdata
;
482 struct cmsghdr cmsghdr
;
483 uint8_t buf
[CMSG_LEN(sizeof(int))];
485 struct iovec iov
= {};
486 union sockaddr_union sa
= {};
487 struct msghdr msg
= {
489 .msg_namelen
= sizeof(sa
),
492 .msg_control
= &control
,
493 .msg_controllen
= sizeof(control
),
495 struct cmsghdr
*cmsg
;
499 int r
, pref
, stateful
, buflen
= 0;
505 r
= ioctl(fd
, FIONREAD
, &buflen
);
509 /* This really should not happen */
512 iov
.iov_len
= buflen
;
514 ra
= malloc(iov
.iov_len
);
520 len
= recvmsg(fd
, &msg
, 0);
522 if (errno
== EAGAIN
|| errno
== EINTR
)
525 log_ndisc(nd
, "Could not receive message from ICMPv6 socket: %m");
527 } else if ((size_t)len
< sizeof(struct nd_router_advert
)) {
529 } else if (msg
.msg_namelen
== 0)
530 gw
= NULL
; /* only happens when running the test-suite over a socketpair */
531 else if (msg
.msg_namelen
!= sizeof(sa
.in6
)) {
532 log_ndisc(nd
, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)msg
.msg_namelen
);
535 gw
= &sa
.in6
.sin6_addr
;
537 assert(!(msg
.msg_flags
& MSG_CTRUNC
));
538 assert(!(msg
.msg_flags
& MSG_TRUNC
));
540 CMSG_FOREACH(cmsg
, &msg
) {
541 if (cmsg
->cmsg_level
== SOL_IPV6
&&
542 cmsg
->cmsg_type
== IPV6_HOPLIMIT
&&
543 cmsg
->cmsg_len
== CMSG_LEN(sizeof(int))) {
544 int hops
= *(int*)CMSG_DATA(cmsg
);
547 log_ndisc(nd
, "Received RA with invalid hop limit %d. Ignoring.", hops
);
555 if (gw
&& !in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) gw
)) {
556 _cleanup_free_
char *addr
= NULL
;
558 (void)in_addr_to_string(AF_INET6
, (const union in_addr_union
*) gw
, &addr
);
560 log_ndisc(nd
, "Received RA from non-link-local address %s. Ignoring.", strna(addr
));
564 if (ra
->nd_ra_type
!= ND_ROUTER_ADVERT
)
567 if (ra
->nd_ra_code
!= 0)
570 nd
->timeout
= sd_event_source_unref(nd
->timeout
);
572 nd
->state
= NDISC_STATE_ADVERTISMENT_LISTEN
;
574 stateful
= ra
->nd_ra_flags_reserved
& (ND_RA_FLAG_MANAGED
| ND_RA_FLAG_OTHER
);
575 pref
= (ra
->nd_ra_flags_reserved
& ND_RA_FLAG_PREF
) >> 3;
578 case ND_RA_FLAG_PREF_LOW
:
579 case ND_RA_FLAG_PREF_HIGH
:
582 pref
= ND_RA_FLAG_PREF_MEDIUM
;
586 lifetime
= be16toh(ra
->nd_ra_router_lifetime
);
588 log_ndisc(nd
, "Received Router Advertisement: flags %s preference %s lifetime %u sec",
589 stateful
& ND_RA_FLAG_MANAGED
? "MANAGED" : stateful
& ND_RA_FLAG_OTHER
? "OTHER" : "none",
590 pref
== ND_RA_FLAG_PREF_HIGH
? "high" : pref
== ND_RA_FLAG_PREF_LOW
? "low" : "medium",
593 r
= ndisc_ra_parse(nd
, ra
, len
);
595 log_ndisc(nd
, "Could not parse Router Advertisement: %s", strerror(-r
));
599 if (nd
->router_callback
)
600 nd
->router_callback(nd
, stateful
, gw
, lifetime
, pref
, nd
->userdata
);
605 static int ndisc_router_solicitation_timeout(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
606 sd_ndisc
*nd
= userdata
;
607 uint64_t time_now
, next_timeout
;
614 nd
->timeout
= sd_event_source_unref(nd
->timeout
);
616 if (nd
->nd_sent
>= NDISC_MAX_ROUTER_SOLICITATIONS
) {
618 nd
->callback(nd
, SD_NDISC_EVENT_TIMEOUT
, nd
->userdata
);
619 nd
->state
= NDISC_STATE_ADVERTISMENT_LISTEN
;
621 r
= icmp6_send_router_solicitation(nd
->fd
, &nd
->mac_addr
);
623 log_ndisc(nd
, "Error sending Router Solicitation");
625 nd
->state
= NDISC_STATE_SOLICITATION_SENT
;
626 log_ndisc(nd
, "Sent Router Solicitation");
631 assert_se(sd_event_now(nd
->event
, clock_boottime_or_monotonic(), &time_now
) >= 0);
633 next_timeout
= time_now
+ NDISC_ROUTER_SOLICITATION_INTERVAL
;
635 r
= sd_event_add_time(nd
->event
, &nd
->timeout
, clock_boottime_or_monotonic(),
637 ndisc_router_solicitation_timeout
, nd
);
639 /* we cannot continue if we are unable to rearm the timer */
644 r
= sd_event_source_set_priority(nd
->timeout
, nd
->event_priority
);
648 r
= sd_event_source_set_description(nd
->timeout
, "ndisc-timeout");
656 int sd_ndisc_stop(sd_ndisc
*nd
) {
657 assert_return(nd
, -EINVAL
);
658 assert_return(nd
->event
, -EINVAL
);
660 log_ndisc(client
, "Stop NDisc");
664 nd
->state
= NDISC_STATE_IDLE
;
667 nd
->callback(nd
, SD_NDISC_EVENT_STOP
, nd
->userdata
);
672 int sd_ndisc_router_discovery_start(sd_ndisc
*nd
) {
678 if (nd
->state
!= NDISC_STATE_IDLE
)
684 r
= icmp6_bind_router_solicitation(nd
->index
);
690 r
= sd_event_add_io(nd
->event
, &nd
->recv
, nd
->fd
, EPOLLIN
,
691 ndisc_router_advertisment_recv
, nd
);
695 r
= sd_event_source_set_priority(nd
->recv
, nd
->event_priority
);
699 r
= sd_event_source_set_description(nd
->recv
, "ndisc-receive-message");
703 r
= sd_event_add_time(nd
->event
, &nd
->timeout
, clock_boottime_or_monotonic(),
704 0, 0, ndisc_router_solicitation_timeout
, nd
);
708 r
= sd_event_source_set_priority(nd
->timeout
, nd
->event_priority
);
712 r
= sd_event_source_set_description(nd
->timeout
, "ndisc-timeout");
717 log_ndisc(client
, "Start Router Solicitation");