1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2014 Intel Corporation. All rights reserved.
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <netinet/icmp6.h>
23 #include <netinet/in.h>
24 #include <netinet/ip6.h>
27 #include <sys/ioctl.h>
31 #include "alloc-util.h"
33 #include "icmp6-util.h"
34 #include "in-addr-util.h"
36 #include "socket-util.h"
37 #include "string-util.h"
39 #define NDISC_ROUTER_SOLICITATION_INTERVAL 4 * USEC_PER_SEC
40 #define NDISC_MAX_ROUTER_SOLICITATIONS 3
44 NDISC_STATE_SOLICITATION_SENT
,
45 NDISC_STATE_ADVERTISMENT_LISTEN
,
47 _NDISC_STATE_INVALID
= -1,
50 #define IP6_MIN_MTU (unsigned)1280
51 #define ICMP6_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr))
52 #define NDISC_OPT_LEN_UNITS 8
54 #define ND_RA_FLAG_PREF 0x18
55 #define ND_RA_FLAG_PREF_LOW 0x03
56 #define ND_RA_FLAG_PREF_MEDIUM 0x0
57 #define ND_RA_FLAG_PREF_HIGH 0x1
58 #define ND_RA_FLAG_PREF_INVALID 0x2
60 typedef struct NDiscPrefix NDiscPrefix
;
67 LIST_FIELDS(NDiscPrefix
, prefixes
);
77 enum NDiscState state
;
81 struct ether_addr mac_addr
;
83 LIST_HEAD(NDiscPrefix
, prefixes
);
85 sd_event_source
*recv
;
86 sd_event_source
*timeout
;
88 sd_ndisc_router_callback_t router_callback
;
89 sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback
;
90 sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback
;
91 sd_ndisc_callback_t callback
;
95 #define log_ndisc(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "NDisc CLIENT: " fmt, ##__VA_ARGS__)
97 static NDiscPrefix
*ndisc_prefix_unref(NDiscPrefix
*prefix
) {
102 assert(prefix
->n_ref
> 0);
105 if (prefix
->n_ref
> 0)
109 LIST_REMOVE(prefixes
, prefix
->nd
->prefixes
, prefix
);
116 static int ndisc_prefix_new(sd_ndisc
*nd
, NDiscPrefix
**ret
) {
121 prefix
= new0(NDiscPrefix
, 1);
126 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
);
320 if (prefix_match(&prefix
->addr
, prefix
->len
, addr
, addr_len
) >= 0) {
326 return -EADDRNOTAVAIL
;
329 static int ndisc_prefix_update(sd_ndisc
*nd
, ssize_t len
,
330 const struct nd_opt_prefix_info
*prefix_opt
) {
332 uint32_t lifetime_valid
, lifetime_preferred
;
334 char time_string
[FORMAT_TIMESPAN_MAX
];
340 if (len
< prefix_opt
->nd_opt_pi_len
)
343 if (!(prefix_opt
->nd_opt_pi_flags_reserved
& (ND_OPT_PI_FLAG_ONLINK
| ND_OPT_PI_FLAG_AUTO
)))
346 if (in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &prefix_opt
->nd_opt_pi_prefix
) > 0)
349 lifetime_valid
= be32toh(prefix_opt
->nd_opt_pi_valid_time
);
350 lifetime_preferred
= be32toh(prefix_opt
->nd_opt_pi_preferred_time
);
352 if (lifetime_valid
< lifetime_preferred
)
355 r
= ndisc_prefix_match(nd
, &prefix_opt
->nd_opt_pi_prefix
,
356 prefix_opt
->nd_opt_pi_prefix_len
, &prefix
);
358 if (r
!= -EADDRNOTAVAIL
)
361 /* if router advertisment prefix valid timeout is zero, the timeout
362 callback will be called immediately to clean up the prefix */
364 r
= ndisc_prefix_new(nd
, &prefix
);
368 prefix
->len
= prefix_opt
->nd_opt_pi_prefix_len
;
370 memcpy(&prefix
->addr
, &prefix_opt
->nd_opt_pi_prefix
,
371 sizeof(prefix
->addr
));
373 log_ndisc(nd
, "New prefix "SD_NDISC_ADDRESS_FORMAT_STR
"/%d lifetime %d expires in %s",
374 SD_NDISC_ADDRESS_FORMAT_VAL(prefix
->addr
),
375 prefix
->len
, lifetime_valid
,
376 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, lifetime_valid
* USEC_PER_SEC
, USEC_PER_SEC
));
378 LIST_PREPEND(prefixes
, nd
->prefixes
, prefix
);
381 if (prefix
->len
!= prefix_opt
->nd_opt_pi_prefix_len
) {
384 prefixlen
= MIN(prefix
->len
, prefix_opt
->nd_opt_pi_prefix_len
);
386 log_ndisc(nd
, "Prefix length mismatch %d/%d using %d",
388 prefix_opt
->nd_opt_pi_prefix_len
,
391 prefix
->len
= prefixlen
;
394 log_ndisc(nd
, "Update prefix "SD_NDISC_ADDRESS_FORMAT_STR
"/%d lifetime %d expires in %s",
395 SD_NDISC_ADDRESS_FORMAT_VAL(prefix
->addr
),
396 prefix
->len
, lifetime_valid
,
397 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, lifetime_valid
* USEC_PER_SEC
, USEC_PER_SEC
));
400 r
= sd_event_now(nd
->event
, clock_boottime_or_monotonic(), &time_now
);
404 prefix
->valid_until
= time_now
+ lifetime_valid
* USEC_PER_SEC
;
406 if ((prefix_opt
->nd_opt_pi_flags_reserved
& ND_OPT_PI_FLAG_ONLINK
) && nd
->prefix_onlink_callback
)
407 nd
->prefix_onlink_callback(nd
, &prefix
->addr
, prefix
->len
, prefix
->valid_until
, nd
->userdata
);
409 if ((prefix_opt
->nd_opt_pi_flags_reserved
& ND_OPT_PI_FLAG_AUTO
) && nd
->prefix_autonomous_callback
)
410 nd
->prefix_autonomous_callback(nd
, &prefix
->addr
, prefix
->len
, lifetime_preferred
, lifetime_valid
,
416 static int ndisc_ra_parse(sd_ndisc
*nd
, struct nd_router_advert
*ra
, ssize_t len
) {
418 struct nd_opt_hdr
*opt_hdr
;
420 assert_return(nd
, -EINVAL
);
421 assert_return(ra
, -EINVAL
);
424 if (len
< NDISC_OPT_LEN_UNITS
) {
425 log_ndisc(nd
, "Router Advertisement below minimum length");
433 while (len
!= 0 && len
>= opt_hdr
->nd_opt_len
* NDISC_OPT_LEN_UNITS
) {
434 struct nd_opt_mtu
*opt_mtu
;
436 struct nd_opt_prefix_info
*opt_prefix
;
438 if (opt_hdr
->nd_opt_len
== 0)
441 switch (opt_hdr
->nd_opt_type
) {
445 mtu
= be32toh(opt_mtu
->nd_opt_mtu_mtu
);
447 if (mtu
!= nd
->mtu
) {
448 nd
->mtu
= MAX(mtu
, IP6_MIN_MTU
);
450 log_ndisc(nd
, "Router Advertisement link MTU %d using %d",
456 case ND_OPT_PREFIX_INFORMATION
:
459 ndisc_prefix_update(nd
, len
, opt_prefix
);
464 len
-= opt_hdr
->nd_opt_len
* NDISC_OPT_LEN_UNITS
;
465 opt
= (void *)((char *)opt
+
466 opt_hdr
->nd_opt_len
* NDISC_OPT_LEN_UNITS
);
471 log_ndisc(nd
, "Router Advertisement contains %zd bytes of trailing garbage", len
);
476 static int ndisc_router_advertisment_recv(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
477 _cleanup_free_
struct nd_router_advert
*ra
= NULL
;
478 sd_ndisc
*nd
= userdata
;
480 struct cmsghdr cmsghdr
;
481 uint8_t buf
[CMSG_LEN(sizeof(int))];
483 struct iovec iov
= {};
484 union sockaddr_union sa
= {};
485 struct msghdr msg
= {
487 .msg_namelen
= sizeof(sa
),
490 .msg_control
= &control
,
491 .msg_controllen
= sizeof(control
),
493 struct cmsghdr
*cmsg
;
497 int r
, pref
, stateful
, buflen
= 0;
503 r
= ioctl(fd
, FIONREAD
, &buflen
);
507 /* This really should not happen */
510 iov
.iov_len
= buflen
;
512 ra
= malloc(iov
.iov_len
);
518 len
= recvmsg(fd
, &msg
, 0);
520 if (errno
== EAGAIN
|| errno
== EINTR
)
523 log_ndisc(nd
, "Could not receive message from ICMPv6 socket: %m");
525 } else if ((size_t)len
< sizeof(struct nd_router_advert
)) {
527 } else if (msg
.msg_namelen
== 0)
528 gw
= NULL
; /* only happens when running the test-suite over a socketpair */
529 else if (msg
.msg_namelen
!= sizeof(sa
.in6
)) {
530 log_ndisc(nd
, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)msg
.msg_namelen
);
533 gw
= &sa
.in6
.sin6_addr
;
535 assert(!(msg
.msg_flags
& MSG_CTRUNC
));
536 assert(!(msg
.msg_flags
& MSG_TRUNC
));
538 CMSG_FOREACH(cmsg
, &msg
) {
539 if (cmsg
->cmsg_level
== SOL_IPV6
&&
540 cmsg
->cmsg_type
== IPV6_HOPLIMIT
&&
541 cmsg
->cmsg_len
== CMSG_LEN(sizeof(int))) {
542 int hops
= *(int*)CMSG_DATA(cmsg
);
545 log_ndisc(nd
, "Received RA with invalid hop limit %d. Ignoring.", hops
);
553 if (gw
&& !in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) gw
)) {
554 _cleanup_free_
char *addr
= NULL
;
556 (void)in_addr_to_string(AF_INET6
, (const union in_addr_union
*) gw
, &addr
);
558 log_ndisc(nd
, "Received RA from non-link-local address %s. Ignoring.", strna(addr
));
562 if (ra
->nd_ra_type
!= ND_ROUTER_ADVERT
)
565 if (ra
->nd_ra_code
!= 0)
568 nd
->timeout
= sd_event_source_unref(nd
->timeout
);
570 nd
->state
= NDISC_STATE_ADVERTISMENT_LISTEN
;
572 stateful
= ra
->nd_ra_flags_reserved
& (ND_RA_FLAG_MANAGED
| ND_RA_FLAG_OTHER
);
573 pref
= (ra
->nd_ra_flags_reserved
& ND_RA_FLAG_PREF
) >> 3;
576 case ND_RA_FLAG_PREF_LOW
:
577 case ND_RA_FLAG_PREF_HIGH
:
580 pref
= ND_RA_FLAG_PREF_MEDIUM
;
584 lifetime
= be16toh(ra
->nd_ra_router_lifetime
);
586 log_ndisc(nd
, "Received Router Advertisement: flags %s preference %s lifetime %u sec",
587 stateful
& ND_RA_FLAG_MANAGED
? "MANAGED" : stateful
& ND_RA_FLAG_OTHER
? "OTHER" : "none",
588 pref
== ND_RA_FLAG_PREF_HIGH
? "high" : pref
== ND_RA_FLAG_PREF_LOW
? "low" : "medium",
591 r
= ndisc_ra_parse(nd
, ra
, len
);
593 log_ndisc(nd
, "Could not parse Router Advertisement: %s", strerror(-r
));
597 if (nd
->router_callback
)
598 nd
->router_callback(nd
, stateful
, gw
, lifetime
, pref
, nd
->userdata
);
603 static int ndisc_router_solicitation_timeout(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
604 sd_ndisc
*nd
= userdata
;
605 uint64_t time_now
, next_timeout
;
612 nd
->timeout
= sd_event_source_unref(nd
->timeout
);
614 if (nd
->nd_sent
>= NDISC_MAX_ROUTER_SOLICITATIONS
) {
616 nd
->callback(nd
, SD_NDISC_EVENT_TIMEOUT
, nd
->userdata
);
617 nd
->state
= NDISC_STATE_ADVERTISMENT_LISTEN
;
619 r
= icmp6_send_router_solicitation(nd
->fd
, &nd
->mac_addr
);
621 log_ndisc(nd
, "Error sending Router Solicitation");
623 nd
->state
= NDISC_STATE_SOLICITATION_SENT
;
624 log_ndisc(nd
, "Sent Router Solicitation");
629 assert_se(sd_event_now(nd
->event
, clock_boottime_or_monotonic(), &time_now
) >= 0);
631 next_timeout
= time_now
+ NDISC_ROUTER_SOLICITATION_INTERVAL
;
633 r
= sd_event_add_time(nd
->event
, &nd
->timeout
, clock_boottime_or_monotonic(),
635 ndisc_router_solicitation_timeout
, nd
);
637 /* we cannot continue if we are unable to rearm the timer */
642 r
= sd_event_source_set_priority(nd
->timeout
, nd
->event_priority
);
646 r
= sd_event_source_set_description(nd
->timeout
, "ndisc-timeout");
654 int sd_ndisc_stop(sd_ndisc
*nd
) {
655 assert_return(nd
, -EINVAL
);
656 assert_return(nd
->event
, -EINVAL
);
658 log_ndisc(client
, "Stop NDisc");
662 nd
->state
= NDISC_STATE_IDLE
;
665 nd
->callback(nd
, SD_NDISC_EVENT_STOP
, nd
->userdata
);
670 int sd_ndisc_router_discovery_start(sd_ndisc
*nd
) {
676 if (nd
->state
!= NDISC_STATE_IDLE
)
682 r
= icmp6_bind_router_solicitation(nd
->index
);
688 r
= sd_event_add_io(nd
->event
, &nd
->recv
, nd
->fd
, EPOLLIN
,
689 ndisc_router_advertisment_recv
, nd
);
693 r
= sd_event_source_set_priority(nd
->recv
, nd
->event_priority
);
697 r
= sd_event_source_set_description(nd
->recv
, "ndisc-receive-message");
701 r
= sd_event_add_time(nd
->event
, &nd
->timeout
, clock_boottime_or_monotonic(),
702 0, 0, ndisc_router_solicitation_timeout
, nd
);
706 r
= sd_event_source_set_priority(nd
->timeout
, nd
->event_priority
);
710 r
= sd_event_source_set_description(nd
->timeout
, "ndisc-timeout");
715 log_ndisc(client
, "Start Router Solicitation");