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/ip6.h>
24 #include <netinet/in.h>
25 #include <sys/ioctl.h>
29 #include "socket-util.h"
31 #include "icmp6-util.h"
34 #define NDISC_ROUTER_SOLICITATION_INTERVAL 4 * USEC_PER_SEC
35 #define NDISC_MAX_ROUTER_SOLICITATIONS 3
39 NDISC_STATE_SOLICITATION_SENT
,
40 NDISC_STATE_ADVERTISMENT_LISTEN
,
42 _NDISC_STATE_INVALID
= -1,
45 #define IP6_MIN_MTU (unsigned)1280
46 #define ICMP6_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr))
47 #define NDISC_OPT_LEN_UNITS 8
49 typedef struct NDiscPrefix NDiscPrefix
;
54 LIST_FIELDS(NDiscPrefix
, prefixes
);
57 sd_event_source
*timeout_valid
;
64 enum NDiscState state
;
68 struct ether_addr mac_addr
;
70 NDiscPrefix
*expired_prefix
;
71 LIST_HEAD(NDiscPrefix
, prefixes
);
73 sd_event_source
*recv
;
74 sd_event_source
*timeout
;
76 sd_ndisc_callback_t callback
;
80 #define log_ndisc(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "NDisc CLIENT: " fmt, ##__VA_ARGS__)
82 static NDiscPrefix
*ndisc_prefix_unref(NDiscPrefix
*prefix
) {
87 assert(prefix
->n_ref
> 0);
90 if (prefix
->n_ref
> 0)
93 prefix
->timeout_valid
= sd_event_source_unref(prefix
->timeout_valid
);
98 static int ndisc_prefix_new(NDiscPrefix
**ret
) {
99 _cleanup_free_ NDiscPrefix
*prefix
= NULL
;
103 prefix
= new0(NDiscPrefix
, 1);
108 LIST_INIT(prefixes
, prefix
);
116 static void ndisc_notify(sd_ndisc
*nd
, int event
) {
118 nd
->callback(nd
, event
, nd
->userdata
);
121 int sd_ndisc_set_callback(sd_ndisc
*nd
, sd_ndisc_callback_t callback
,
125 nd
->callback
= callback
;
126 nd
->userdata
= userdata
;
131 int sd_ndisc_set_index(sd_ndisc
*nd
, int interface_index
) {
133 assert(interface_index
>= -1);
135 nd
->index
= interface_index
;
140 int sd_ndisc_set_mac(sd_ndisc
*nd
, const struct ether_addr
*mac_addr
) {
144 memcpy(&nd
->mac_addr
, mac_addr
, sizeof(nd
->mac_addr
));
152 int sd_ndisc_attach_event(sd_ndisc
*nd
, sd_event
*event
, int priority
) {
155 assert_return(nd
, -EINVAL
);
156 assert_return(!nd
->event
, -EBUSY
);
159 nd
->event
= sd_event_ref(event
);
161 r
= sd_event_default(&nd
->event
);
166 nd
->event_priority
= priority
;
171 int sd_ndisc_detach_event(sd_ndisc
*nd
) {
172 assert_return(nd
, -EINVAL
);
174 nd
->event
= sd_event_unref(nd
->event
);
179 sd_event
*sd_ndisc_get_event(sd_ndisc
*nd
) {
185 sd_ndisc
*sd_ndisc_ref(sd_ndisc
*nd
) {
190 assert(nd
->n_ref
> 0);
196 static int ndisc_init(sd_ndisc
*nd
) {
199 nd
->recv
= sd_event_source_unref(nd
->recv
);
200 nd
->fd
= asynchronous_close(nd
->fd
);
201 nd
->timeout
= sd_event_source_unref(nd
->timeout
);
206 sd_ndisc
*sd_ndisc_unref(sd_ndisc
*nd
) {
207 NDiscPrefix
*prefix
, *p
;
212 assert(nd
->n_ref
> 0);
219 sd_ndisc_detach_event(nd
);
221 LIST_FOREACH_SAFE(prefixes
, prefix
, p
, nd
->prefixes
) {
222 LIST_REMOVE(prefixes
, nd
->prefixes
, prefix
);
224 prefix
= ndisc_prefix_unref(prefix
);
232 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ndisc
*, sd_ndisc_unref
);
233 #define _cleanup_sd_ndisc_free_ _cleanup_(sd_ndisc_unrefp)
235 int sd_ndisc_new(sd_ndisc
**ret
) {
236 _cleanup_sd_ndisc_free_ sd_ndisc
*nd
= NULL
;
240 nd
= new0(sd_ndisc
, 1);
249 LIST_HEAD_INIT(nd
->prefixes
);
257 int sd_ndisc_get_mtu(sd_ndisc
*nd
, uint32_t *mtu
) {
258 assert_return(nd
, -EINVAL
);
259 assert_return(mtu
, -EINVAL
);
269 static int ndisc_prefix_timeout(sd_event_source
*s
, uint64_t usec
,
271 sd_ndisc
*nd
= userdata
;
272 NDiscPrefix
*prefix
, *p
;
276 LIST_FOREACH_SAFE(prefixes
, prefix
, p
, nd
->prefixes
) {
277 if (prefix
->timeout_valid
!= s
)
280 log_ndisc(nd
, "Prefix expired "SD_NDISC_ADDRESS_FORMAT_STR
"/%d",
281 SD_NDISC_ADDRESS_FORMAT_VAL(prefix
->addr
),
284 LIST_REMOVE(prefixes
, nd
->prefixes
, prefix
);
286 nd
->expired_prefix
= prefix
;
288 SD_NDISC_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED
);
289 nd
->expired_prefix
= NULL
;
291 prefix
= ndisc_prefix_unref(prefix
);
299 static int ndisc_prefix_set_timeout(sd_ndisc
*nd
,
305 assert_return(prefix
, -EINVAL
);
307 r
= sd_event_now(nd
->event
, clock_boottime_or_monotonic(), &time_now
);
311 prefix
->timeout_valid
= sd_event_source_unref(prefix
->timeout_valid
);
313 r
= sd_event_add_time(nd
->event
, &prefix
->timeout_valid
,
314 clock_boottime_or_monotonic(), time_now
+ valid
,
315 USEC_PER_SEC
, ndisc_prefix_timeout
, nd
);
319 r
= sd_event_source_set_priority(prefix
->timeout_valid
,
324 r
= sd_event_source_set_description(prefix
->timeout_valid
,
325 "ndisc-prefix-timeout");
329 prefix
->timeout_valid
=
330 sd_event_source_unref(prefix
->timeout_valid
);
335 static int prefix_match(const struct in6_addr
*prefix
, uint8_t prefixlen
,
336 const struct in6_addr
*addr
,
337 uint8_t addr_prefixlen
) {
338 uint8_t bytes
, mask
, len
;
340 assert_return(prefix
, -EINVAL
);
341 assert_return(addr
, -EINVAL
);
343 len
= MIN(prefixlen
, addr_prefixlen
);
346 mask
= 0xff << (8 - len
% 8);
348 if (memcmp(prefix
, addr
, bytes
) != 0 ||
349 (prefix
->s6_addr
[bytes
] & mask
) != (addr
->s6_addr
[bytes
] & mask
))
350 return -EADDRNOTAVAIL
;
355 static int ndisc_prefix_match(NDiscPrefix
*head
, const struct in6_addr
*addr
,
356 uint8_t addr_len
, NDiscPrefix
**result
) {
359 LIST_FOREACH(prefixes
, prefix
, head
) {
360 if (prefix_match(&prefix
->addr
, prefix
->len
, addr
, addr_len
) >= 0) {
366 return -EADDRNOTAVAIL
;
369 int sd_ndisc_prefix_match(struct in6_addr
*prefix
, uint8_t prefixlen
,
370 struct in6_addr
*addr
) {
371 return prefix_match(prefix
, prefixlen
, addr
, sizeof(addr
->s6_addr
) * 8);
374 int sd_ndisc_get_prefixlen(sd_ndisc
*nd
, const struct in6_addr
*addr
,
375 uint8_t *prefixlen
) {
379 assert_return(nd
, -EINVAL
);
380 assert_return(addr
, -EINVAL
);
381 assert_return(prefixlen
, -EINVAL
);
383 r
= ndisc_prefix_match(nd
->prefixes
, addr
,
384 sizeof(addr
->s6_addr
) * 8, &prefix
);
388 *prefixlen
= prefix
->len
;
393 int sd_ndisc_get_expired_prefix(sd_ndisc
*nd
, struct in6_addr
**addr
, uint8_t *prefixlen
) {
394 assert_return(nd
, -EINVAL
);
395 assert_return(addr
, -EINVAL
);
396 assert_return(prefixlen
, -EINVAL
);
398 if (!nd
->expired_prefix
)
399 return -EADDRNOTAVAIL
;
401 *addr
= &nd
->expired_prefix
->addr
;
402 *prefixlen
= nd
->expired_prefix
->len
;
407 static int ndisc_prefix_update(sd_ndisc
*nd
, ssize_t len
,
408 const struct nd_opt_prefix_info
*prefix_opt
) {
412 char time_string
[FORMAT_TIMESPAN_MAX
];
414 assert_return(nd
, -EINVAL
);
415 assert_return(prefix_opt
, -EINVAL
);
417 if (len
< prefix_opt
->nd_opt_pi_len
)
420 if (!(prefix_opt
->nd_opt_pi_flags_reserved
& ND_OPT_PI_FLAG_ONLINK
))
423 lifetime
= be32toh(prefix_opt
->nd_opt_pi_valid_time
);
425 r
= ndisc_prefix_match(nd
->prefixes
,
426 &prefix_opt
->nd_opt_pi_prefix
,
427 prefix_opt
->nd_opt_pi_prefix_len
, &prefix
);
429 if (r
< 0 && r
!= -EADDRNOTAVAIL
)
432 /* if router advertisment prefix valid timeout is zero, the timeout
433 callback will be called immediately to clean up the prefix */
435 if (r
== -EADDRNOTAVAIL
) {
436 r
= ndisc_prefix_new(&prefix
);
440 prefix
->len
= prefix_opt
->nd_opt_pi_prefix_len
;
442 memcpy(&prefix
->addr
, &prefix_opt
->nd_opt_pi_prefix
,
443 sizeof(prefix
->addr
));
445 log_ndisc(nd
, "New prefix "SD_NDISC_ADDRESS_FORMAT_STR
"/%d lifetime %d expires in %s",
446 SD_NDISC_ADDRESS_FORMAT_VAL(prefix
->addr
),
447 prefix
->len
, lifetime
,
448 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, lifetime
* USEC_PER_SEC
, USEC_PER_SEC
));
450 LIST_PREPEND(prefixes
, nd
->prefixes
, prefix
);
453 if (prefix
->len
!= prefix_opt
->nd_opt_pi_prefix_len
) {
456 prefixlen
= MIN(prefix
->len
, prefix_opt
->nd_opt_pi_prefix_len
);
458 log_ndisc(nd
, "Prefix length mismatch %d/%d using %d",
460 prefix_opt
->nd_opt_pi_prefix_len
,
463 prefix
->len
= prefixlen
;
466 log_ndisc(nd
, "Update prefix "SD_NDISC_ADDRESS_FORMAT_STR
"/%d lifetime %d expires in %s",
467 SD_NDISC_ADDRESS_FORMAT_VAL(prefix
->addr
),
468 prefix
->len
, lifetime
,
469 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, lifetime
* USEC_PER_SEC
, USEC_PER_SEC
));
472 r
= ndisc_prefix_set_timeout(nd
, prefix
, lifetime
* USEC_PER_SEC
);
477 static int ndisc_ra_parse(sd_ndisc
*nd
, struct nd_router_advert
*ra
,
480 struct nd_opt_hdr
*opt_hdr
;
482 assert_return(nd
, -EINVAL
);
483 assert_return(ra
, -EINVAL
);
486 if (len
< NDISC_OPT_LEN_UNITS
) {
487 log_ndisc(nd
, "Router Advertisement below minimum length");
495 while (len
!= 0 && len
>= opt_hdr
->nd_opt_len
* NDISC_OPT_LEN_UNITS
) {
496 struct nd_opt_mtu
*opt_mtu
;
498 struct nd_opt_prefix_info
*opt_prefix
;
500 if (opt_hdr
->nd_opt_len
== 0)
503 switch (opt_hdr
->nd_opt_type
) {
507 mtu
= be32toh(opt_mtu
->nd_opt_mtu_mtu
);
509 if (mtu
!= nd
->mtu
) {
510 nd
->mtu
= MAX(mtu
, IP6_MIN_MTU
);
512 log_ndisc(nd
, "Router Advertisement link MTU %d using %d",
518 case ND_OPT_PREFIX_INFORMATION
:
521 ndisc_prefix_update(nd
, len
, opt_prefix
);
526 len
-= opt_hdr
->nd_opt_len
* NDISC_OPT_LEN_UNITS
;
527 opt
= (void *)((char *)opt
+
528 opt_hdr
->nd_opt_len
* NDISC_OPT_LEN_UNITS
);
533 log_ndisc(nd
, "Router Advertisement contains %zd bytes of trailing garbage", len
);
538 static int ndisc_router_advertisment_recv(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
539 sd_ndisc
*nd
= userdata
;
542 _cleanup_free_
struct nd_router_advert
*ra
= NULL
;
543 int event
= SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE
;
549 r
= ioctl(fd
, FIONREAD
, &buflen
);
550 if (r
< 0 || buflen
<= 0)
551 buflen
= ICMP6_RECV_SIZE
;
557 len
= read(fd
, ra
, buflen
);
559 log_ndisc(nd
, "Could not receive message from UDP socket: %m");
563 if (ra
->nd_ra_type
!= ND_ROUTER_ADVERT
)
566 if (ra
->nd_ra_code
!= 0)
569 nd
->timeout
= sd_event_source_unref(nd
->timeout
);
571 nd
->state
= NDISC_STATE_ADVERTISMENT_LISTEN
;
573 if (ra
->nd_ra_flags_reserved
& ND_RA_FLAG_OTHER
)
574 event
= SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER
;
576 if (ra
->nd_ra_flags_reserved
& ND_RA_FLAG_MANAGED
)
577 event
= SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED
;
579 log_ndisc(nd
, "Received Router Advertisement flags %s/%s",
580 ra
->nd_ra_flags_reserved
& ND_RA_FLAG_MANAGED
? "MANAGED": "none",
581 ra
->nd_ra_flags_reserved
& ND_RA_FLAG_OTHER
? "OTHER": "none");
583 if (event
!= SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE
) {
584 r
= ndisc_ra_parse(nd
, ra
, len
);
586 log_ndisc(nd
, "Could not parse Router Advertisement: %s",
592 ndisc_notify(nd
, event
);
597 static int ndisc_router_solicitation_timeout(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
598 sd_ndisc
*nd
= userdata
;
599 uint64_t time_now
, next_timeout
;
600 struct ether_addr unset
= { };
601 struct ether_addr
*addr
= NULL
;
608 nd
->timeout
= sd_event_source_unref(nd
->timeout
);
610 if (nd
->nd_sent
>= NDISC_MAX_ROUTER_SOLICITATIONS
) {
611 ndisc_notify(nd
, SD_NDISC_EVENT_ROUTER_ADVERTISMENT_TIMEOUT
);
612 nd
->state
= NDISC_STATE_ADVERTISMENT_LISTEN
;
614 if (memcmp(&nd
->mac_addr
, &unset
, sizeof(struct ether_addr
)))
615 addr
= &nd
->mac_addr
;
617 r
= icmp6_send_router_solicitation(nd
->fd
, addr
);
619 log_ndisc(nd
, "Error sending Router Solicitation");
621 nd
->state
= NDISC_STATE_SOLICITATION_SENT
;
622 log_ndisc(nd
, "Sent Router Solicitation");
627 r
= sd_event_now(nd
->event
, clock_boottime_or_monotonic(), &time_now
);
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
);
643 r
= sd_event_source_set_priority(nd
->timeout
,
650 r
= sd_event_source_set_description(nd
->timeout
, "ndisc-timeout");
660 int sd_ndisc_stop(sd_ndisc
*nd
) {
661 assert_return(nd
, -EINVAL
);
662 assert_return(nd
->event
, -EINVAL
);
664 log_ndisc(client
, "Stop NDisc");
668 nd
->state
= NDISC_STATE_IDLE
;
673 int sd_ndisc_router_discovery_start(sd_ndisc
*nd
) {
679 if (nd
->state
!= NDISC_STATE_IDLE
)
685 r
= icmp6_bind_router_solicitation(nd
->index
);
691 r
= sd_event_add_io(nd
->event
, &nd
->recv
, nd
->fd
, EPOLLIN
,
692 ndisc_router_advertisment_recv
, nd
);
696 r
= sd_event_source_set_priority(nd
->recv
, nd
->event_priority
);
700 r
= sd_event_source_set_description(nd
->recv
, "ndisc-receive-message");
704 r
= sd_event_add_time(nd
->event
, &nd
->timeout
, clock_boottime_or_monotonic(),
705 0, 0, ndisc_router_solicitation_timeout
, nd
);
709 r
= sd_event_source_set_priority(nd
->timeout
, nd
->event_priority
);
713 r
= sd_event_source_set_description(nd
->timeout
, "ndisc-timeout");
718 log_ndisc(client
, "Start Router Solicitation");