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>
27 #include "socket-util.h"
30 #include "dhcp6-internal.h"
33 #define ICMP6_ROUTER_SOLICITATION_INTERVAL 4 * USEC_PER_SEC
34 #define ICMP6_MAX_ROUTER_SOLICITATIONS 3
37 ICMP6_NEIGHBOR_DISCOVERY_IDLE
= 0,
38 ICMP6_ROUTER_SOLICITATION_SENT
= 10,
39 ICMP6_ROUTER_ADVERTISMENT_LISTEN
= 11,
42 #define IP6_MIN_MTU (unsigned)1280
43 #define ICMP6_ND_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr))
44 #define ICMP6_OPT_LEN_UNITS 8
46 typedef struct ICMP6Prefix ICMP6Prefix
;
51 LIST_FIELDS(ICMP6Prefix
, prefixes
);
54 sd_event_source
*timeout_valid
;
61 enum icmp6_nd_state state
;
65 struct ether_addr mac_addr
;
67 ICMP6Prefix
*expired_prefix
;
68 LIST_HEAD(ICMP6Prefix
, prefixes
);
70 sd_event_source
*recv
;
71 sd_event_source
*timeout
;
73 sd_icmp6_nd_callback_t callback
;
77 #define log_icmp6_nd(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "ICMPv6 CLIENT: " fmt, ##__VA_ARGS__)
79 static ICMP6Prefix
*icmp6_prefix_unref(ICMP6Prefix
*prefix
) {
84 assert(prefix
->n_ref
> 0);
87 if (prefix
->n_ref
> 0)
90 prefix
->timeout_valid
= sd_event_source_unref(prefix
->timeout_valid
);
95 static int icmp6_prefix_new(ICMP6Prefix
**ret
) {
96 _cleanup_free_ ICMP6Prefix
*prefix
= NULL
;
100 prefix
= new0(ICMP6Prefix
, 1);
105 LIST_INIT(prefixes
, prefix
);
113 static void icmp6_nd_notify(sd_icmp6_nd
*nd
, int event
) {
115 nd
->callback(nd
, event
, nd
->userdata
);
118 int sd_icmp6_nd_set_callback(sd_icmp6_nd
*nd
, sd_icmp6_nd_callback_t callback
,
122 nd
->callback
= callback
;
123 nd
->userdata
= userdata
;
128 int sd_icmp6_nd_set_index(sd_icmp6_nd
*nd
, int interface_index
) {
130 assert(interface_index
>= -1);
132 nd
->index
= interface_index
;
137 int sd_icmp6_nd_set_mac(sd_icmp6_nd
*nd
, const struct ether_addr
*mac_addr
) {
141 memcpy(&nd
->mac_addr
, mac_addr
, sizeof(nd
->mac_addr
));
149 int sd_icmp6_nd_attach_event(sd_icmp6_nd
*nd
, sd_event
*event
, int priority
) {
152 assert_return(nd
, -EINVAL
);
153 assert_return(!nd
->event
, -EBUSY
);
156 nd
->event
= sd_event_ref(event
);
158 r
= sd_event_default(&nd
->event
);
163 nd
->event_priority
= priority
;
168 int sd_icmp6_nd_detach_event(sd_icmp6_nd
*nd
) {
169 assert_return(nd
, -EINVAL
);
171 nd
->event
= sd_event_unref(nd
->event
);
176 sd_event
*sd_icmp6_nd_get_event(sd_icmp6_nd
*nd
) {
182 sd_icmp6_nd
*sd_icmp6_nd_ref(sd_icmp6_nd
*nd
) {
187 assert(nd
->n_ref
> 0);
193 static int icmp6_nd_init(sd_icmp6_nd
*nd
) {
196 nd
->recv
= sd_event_source_unref(nd
->recv
);
197 nd
->fd
= asynchronous_close(nd
->fd
);
198 nd
->timeout
= sd_event_source_unref(nd
->timeout
);
203 sd_icmp6_nd
*sd_icmp6_nd_unref(sd_icmp6_nd
*nd
) {
204 ICMP6Prefix
*prefix
, *p
;
209 assert(nd
->n_ref
> 0);
216 sd_icmp6_nd_detach_event(nd
);
218 LIST_FOREACH_SAFE(prefixes
, prefix
, p
, nd
->prefixes
) {
219 LIST_REMOVE(prefixes
, nd
->prefixes
, prefix
);
221 prefix
= icmp6_prefix_unref(prefix
);
229 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_icmp6_nd
*, sd_icmp6_nd_unref
);
230 #define _cleanup_sd_icmp6_nd_free_ _cleanup_(sd_icmp6_nd_unrefp)
232 int sd_icmp6_nd_new(sd_icmp6_nd
**ret
) {
233 _cleanup_sd_icmp6_nd_free_ sd_icmp6_nd
*nd
= NULL
;
237 nd
= new0(sd_icmp6_nd
, 1);
246 LIST_HEAD_INIT(nd
->prefixes
);
254 int sd_icmp6_ra_get_mtu(sd_icmp6_nd
*nd
, uint32_t *mtu
) {
255 assert_return(nd
, -EINVAL
);
256 assert_return(mtu
, -EINVAL
);
266 static int icmp6_ra_prefix_timeout(sd_event_source
*s
, uint64_t usec
,
268 sd_icmp6_nd
*nd
= userdata
;
269 ICMP6Prefix
*prefix
, *p
;
273 LIST_FOREACH_SAFE(prefixes
, prefix
, p
, nd
->prefixes
) {
274 if (prefix
->timeout_valid
!= s
)
277 log_icmp6_nd(nd
, "Prefix expired "SD_ICMP6_ND_ADDRESS_FORMAT_STR
"/%d",
278 SD_ICMP6_ND_ADDRESS_FORMAT_VAL(prefix
->addr
),
281 LIST_REMOVE(prefixes
, nd
->prefixes
, prefix
);
283 nd
->expired_prefix
= prefix
;
285 SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED
);
286 nd
->expired_prefix
= NULL
;
288 prefix
= icmp6_prefix_unref(prefix
);
296 static int icmp6_ra_prefix_set_timeout(sd_icmp6_nd
*nd
,
302 assert_return(prefix
, -EINVAL
);
304 r
= sd_event_now(nd
->event
, clock_boottime_or_monotonic(), &time_now
);
308 prefix
->timeout_valid
= sd_event_source_unref(prefix
->timeout_valid
);
310 r
= sd_event_add_time(nd
->event
, &prefix
->timeout_valid
,
311 clock_boottime_or_monotonic(), time_now
+ valid
,
312 USEC_PER_SEC
, icmp6_ra_prefix_timeout
, nd
);
316 r
= sd_event_source_set_priority(prefix
->timeout_valid
,
321 r
= sd_event_source_set_description(prefix
->timeout_valid
,
322 "icmp6-prefix-timeout");
326 prefix
->timeout_valid
=
327 sd_event_source_unref(prefix
->timeout_valid
);
332 static int icmp6_prefix_match(const struct in6_addr
*prefix
, uint8_t prefixlen
,
333 const struct in6_addr
*addr
,
334 uint8_t addr_prefixlen
) {
335 uint8_t bytes
, mask
, len
;
337 assert_return(prefix
, -EINVAL
);
338 assert_return(addr
, -EINVAL
);
340 len
= MIN(prefixlen
, addr_prefixlen
);
343 mask
= 0xff << (8 - len
% 8);
345 if (memcmp(prefix
, addr
, bytes
) != 0 ||
346 (prefix
->s6_addr
[bytes
] & mask
) != (addr
->s6_addr
[bytes
] & mask
))
347 return -EADDRNOTAVAIL
;
352 static int icmp6_ra_prefix_match(ICMP6Prefix
*head
, const struct in6_addr
*addr
,
353 uint8_t addr_len
, ICMP6Prefix
**result
) {
356 LIST_FOREACH(prefixes
, prefix
, head
) {
357 if (icmp6_prefix_match(&prefix
->addr
, prefix
->len
, addr
,
364 return -EADDRNOTAVAIL
;
367 int sd_icmp6_prefix_match(struct in6_addr
*prefix
, uint8_t prefixlen
,
368 struct in6_addr
*addr
) {
369 return icmp6_prefix_match(prefix
, prefixlen
, addr
,
370 sizeof(addr
->s6_addr
) * 8);
373 int sd_icmp6_ra_get_prefixlen(sd_icmp6_nd
*nd
, const struct in6_addr
*addr
,
374 uint8_t *prefixlen
) {
378 assert_return(nd
, -EINVAL
);
379 assert_return(addr
, -EINVAL
);
380 assert_return(prefixlen
, -EINVAL
);
382 r
= icmp6_ra_prefix_match(nd
->prefixes
, addr
,
383 sizeof(addr
->s6_addr
) * 8, &prefix
);
387 *prefixlen
= prefix
->len
;
392 int sd_icmp6_ra_get_expired_prefix(sd_icmp6_nd
*nd
, struct in6_addr
**addr
, uint8_t *prefixlen
) {
393 assert_return(nd
, -EINVAL
);
394 assert_return(addr
, -EINVAL
);
395 assert_return(prefixlen
, -EINVAL
);
397 if (!nd
->expired_prefix
)
398 return -EADDRNOTAVAIL
;
400 *addr
= &nd
->expired_prefix
->addr
;
401 *prefixlen
= nd
->expired_prefix
->len
;
406 static int icmp6_ra_prefix_update(sd_icmp6_nd
*nd
, ssize_t len
,
407 const struct nd_opt_prefix_info
*prefix_opt
) {
411 char time_string
[FORMAT_TIMESPAN_MAX
];
413 assert_return(nd
, -EINVAL
);
414 assert_return(prefix_opt
, -EINVAL
);
416 if (len
< prefix_opt
->nd_opt_pi_len
)
419 if (!(prefix_opt
->nd_opt_pi_flags_reserved
& ND_OPT_PI_FLAG_ONLINK
))
422 lifetime
= be32toh(prefix_opt
->nd_opt_pi_valid_time
);
424 r
= icmp6_ra_prefix_match(nd
->prefixes
,
425 &prefix_opt
->nd_opt_pi_prefix
,
426 prefix_opt
->nd_opt_pi_prefix_len
, &prefix
);
428 if (r
< 0 && r
!= -EADDRNOTAVAIL
)
431 /* if router advertisment prefix valid timeout is zero, the timeout
432 callback will be called immediately to clean up the prefix */
434 if (r
== -EADDRNOTAVAIL
) {
435 r
= icmp6_prefix_new(&prefix
);
439 prefix
->len
= prefix_opt
->nd_opt_pi_prefix_len
;
441 memcpy(&prefix
->addr
, &prefix_opt
->nd_opt_pi_prefix
,
442 sizeof(prefix
->addr
));
444 log_icmp6_nd(nd
, "New prefix "SD_ICMP6_ND_ADDRESS_FORMAT_STR
"/%d lifetime %d expires in %s",
445 SD_ICMP6_ND_ADDRESS_FORMAT_VAL(prefix
->addr
),
446 prefix
->len
, lifetime
,
447 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, lifetime
* USEC_PER_SEC
, USEC_PER_SEC
));
449 LIST_PREPEND(prefixes
, nd
->prefixes
, prefix
);
452 if (prefix
->len
!= prefix_opt
->nd_opt_pi_prefix_len
) {
455 prefixlen
= MIN(prefix
->len
, prefix_opt
->nd_opt_pi_prefix_len
);
457 log_icmp6_nd(nd
, "Prefix length mismatch %d/%d using %d",
459 prefix_opt
->nd_opt_pi_prefix_len
,
462 prefix
->len
= prefixlen
;
465 log_icmp6_nd(nd
, "Update prefix "SD_ICMP6_ND_ADDRESS_FORMAT_STR
"/%d lifetime %d expires in %s",
466 SD_ICMP6_ND_ADDRESS_FORMAT_VAL(prefix
->addr
),
467 prefix
->len
, lifetime
,
468 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, lifetime
* USEC_PER_SEC
, USEC_PER_SEC
));
471 r
= icmp6_ra_prefix_set_timeout(nd
, prefix
, lifetime
* USEC_PER_SEC
);
476 static int icmp6_ra_parse(sd_icmp6_nd
*nd
, struct nd_router_advert
*ra
,
479 struct nd_opt_hdr
*opt_hdr
;
481 assert_return(nd
, -EINVAL
);
482 assert_return(ra
, -EINVAL
);
485 if (len
< ICMP6_OPT_LEN_UNITS
) {
486 log_icmp6_nd(nd
, "Router Advertisement below minimum length");
494 while (len
!= 0 && len
>= opt_hdr
->nd_opt_len
* ICMP6_OPT_LEN_UNITS
) {
495 struct nd_opt_mtu
*opt_mtu
;
497 struct nd_opt_prefix_info
*opt_prefix
;
499 if (opt_hdr
->nd_opt_len
== 0)
502 switch (opt_hdr
->nd_opt_type
) {
506 mtu
= be32toh(opt_mtu
->nd_opt_mtu_mtu
);
508 if (mtu
!= nd
->mtu
) {
509 nd
->mtu
= MAX(mtu
, IP6_MIN_MTU
);
511 log_icmp6_nd(nd
, "Router Advertisement link MTU %d using %d",
517 case ND_OPT_PREFIX_INFORMATION
:
520 icmp6_ra_prefix_update(nd
, len
, opt_prefix
);
525 len
-= opt_hdr
->nd_opt_len
* ICMP6_OPT_LEN_UNITS
;
526 opt
= (void *)((char *)opt
+
527 opt_hdr
->nd_opt_len
* ICMP6_OPT_LEN_UNITS
);
532 log_icmp6_nd(nd
, "Router Advertisement contains %zd bytes of trailing garbage", len
);
537 static int icmp6_router_advertisment_recv(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
538 sd_icmp6_nd
*nd
= userdata
;
541 _cleanup_free_
struct nd_router_advert
*ra
= NULL
;
542 int event
= SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE
;
548 r
= ioctl(fd
, FIONREAD
, &buflen
);
549 if (r
< 0 || buflen
<= 0)
550 buflen
= ICMP6_ND_RECV_SIZE
;
556 len
= read(fd
, ra
, buflen
);
558 log_icmp6_nd(nd
, "Could not receive message from UDP socket: %m");
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
= ICMP6_ROUTER_ADVERTISMENT_LISTEN
;
572 if (ra
->nd_ra_flags_reserved
& ND_RA_FLAG_OTHER
)
573 event
= SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER
;
575 if (ra
->nd_ra_flags_reserved
& ND_RA_FLAG_MANAGED
)
576 event
= SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED
;
578 log_icmp6_nd(nd
, "Received Router Advertisement flags %s/%s",
579 ra
->nd_ra_flags_reserved
& ND_RA_FLAG_MANAGED
? "MANAGED": "none",
580 ra
->nd_ra_flags_reserved
& ND_RA_FLAG_OTHER
? "OTHER": "none");
582 if (event
!= SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE
) {
583 r
= icmp6_ra_parse(nd
, ra
, len
);
585 log_icmp6_nd(nd
, "Could not parse Router Advertisement: %s",
591 icmp6_nd_notify(nd
, event
);
596 static int icmp6_router_solicitation_timeout(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
597 sd_icmp6_nd
*nd
= userdata
;
598 uint64_t time_now
, next_timeout
;
599 struct ether_addr unset
= { };
600 struct ether_addr
*addr
= NULL
;
607 nd
->timeout
= sd_event_source_unref(nd
->timeout
);
609 if (nd
->nd_sent
>= ICMP6_MAX_ROUTER_SOLICITATIONS
) {
610 icmp6_nd_notify(nd
, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_TIMEOUT
);
611 nd
->state
= ICMP6_ROUTER_ADVERTISMENT_LISTEN
;
613 if (memcmp(&nd
->mac_addr
, &unset
, sizeof(struct ether_addr
)))
614 addr
= &nd
->mac_addr
;
616 r
= dhcp_network_icmp6_send_router_solicitation(nd
->fd
, addr
);
618 log_icmp6_nd(nd
, "Error sending Router Solicitation");
620 nd
->state
= ICMP6_ROUTER_SOLICITATION_SENT
;
621 log_icmp6_nd(nd
, "Sent Router Solicitation");
626 r
= sd_event_now(nd
->event
, clock_boottime_or_monotonic(), &time_now
);
628 icmp6_nd_notify(nd
, r
);
632 next_timeout
= time_now
+ ICMP6_ROUTER_SOLICITATION_INTERVAL
;
634 r
= sd_event_add_time(nd
->event
, &nd
->timeout
, clock_boottime_or_monotonic(),
636 icmp6_router_solicitation_timeout
, nd
);
638 icmp6_nd_notify(nd
, r
);
642 r
= sd_event_source_set_priority(nd
->timeout
,
645 icmp6_nd_notify(nd
, r
);
649 r
= sd_event_source_set_description(nd
->timeout
, "icmp6-timeout");
651 icmp6_nd_notify(nd
, r
);
659 int sd_icmp6_nd_stop(sd_icmp6_nd
*nd
) {
660 assert_return(nd
, -EINVAL
);
661 assert_return(nd
->event
, -EINVAL
);
663 log_icmp6_nd(client
, "Stop ICMPv6");
667 nd
->state
= ICMP6_NEIGHBOR_DISCOVERY_IDLE
;
672 int sd_icmp6_router_solicitation_start(sd_icmp6_nd
*nd
) {
678 if (nd
->state
!= ICMP6_NEIGHBOR_DISCOVERY_IDLE
)
684 r
= dhcp_network_icmp6_bind_router_solicitation(nd
->index
);
690 r
= sd_event_add_io(nd
->event
, &nd
->recv
, nd
->fd
, EPOLLIN
,
691 icmp6_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
, "icmp6-receive-message");
703 r
= sd_event_add_time(nd
->event
, &nd
->timeout
, clock_boottime_or_monotonic(),
704 0, 0, icmp6_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
, "icmp6-timeout");
717 log_icmp6_nd(client
, "Start Router Solicitation");