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"
31 #include "dhcp6-internal.h"
32 #include "sd-icmp6-nd.h"
34 #define ICMP6_ROUTER_SOLICITATION_INTERVAL 4 * USEC_PER_SEC
35 #define ICMP6_MAX_ROUTER_SOLICITATIONS 3
38 ICMP6_NEIGHBOR_DISCOVERY_IDLE = 0,
39 ICMP6_ROUTER_SOLICITATION_SENT = 10,
40 ICMP6_ROUTER_ADVERTISMENT_LISTEN = 11,
43 #define IP6_MIN_MTU (unsigned)1280
44 #define ICMP6_ND_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr))
45 #define ICMP6_OPT_LEN_UNITS 8
47 typedef struct ICMP6Prefix ICMP6Prefix;
52 LIST_FIELDS(ICMP6Prefix, prefixes);
55 sd_event_source *timeout_valid;
62 enum icmp6_nd_state state;
66 struct ether_addr mac_addr;
68 ICMP6Prefix *expired_prefix;
69 LIST_HEAD(ICMP6Prefix, prefixes);
71 sd_event_source *recv;
72 sd_event_source *timeout;
74 sd_icmp6_nd_callback_t callback;
78 #define log_icmp6_nd(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "ICMPv6 CLIENT: " fmt, ##__VA_ARGS__)
80 static ICMP6Prefix *icmp6_prefix_unref(ICMP6Prefix *prefix) {
81 if (prefix && REFCNT_DEC(prefix->n_ref) <= 0) {
82 prefix->timeout_valid =
83 sd_event_source_unref(prefix->timeout_valid);
91 static int icmp6_prefix_new(ICMP6Prefix **ret) {
92 _cleanup_free_ ICMP6Prefix *prefix = NULL;
96 prefix = new0(ICMP6Prefix, 1);
100 prefix->n_ref = REFCNT_INIT;
101 LIST_INIT(prefixes, prefix);
109 static void icmp6_nd_notify(sd_icmp6_nd *nd, int event)
112 nd->callback(nd, event, nd->userdata);
115 int sd_icmp6_nd_set_callback(sd_icmp6_nd *nd, sd_icmp6_nd_callback_t callback,
119 nd->callback = callback;
120 nd->userdata = userdata;
125 int sd_icmp6_nd_set_index(sd_icmp6_nd *nd, int interface_index) {
127 assert(interface_index >= -1);
129 nd->index = interface_index;
134 int sd_icmp6_nd_set_mac(sd_icmp6_nd *nd, const struct ether_addr *mac_addr) {
138 memcpy(&nd->mac_addr, mac_addr, sizeof(nd->mac_addr));
146 int sd_icmp6_nd_attach_event(sd_icmp6_nd *nd, sd_event *event, int priority) {
149 assert_return(nd, -EINVAL);
150 assert_return(!nd->event, -EBUSY);
153 nd->event = sd_event_ref(event);
155 r = sd_event_default(&nd->event);
160 nd->event_priority = priority;
165 int sd_icmp6_nd_detach_event(sd_icmp6_nd *nd) {
166 assert_return(nd, -EINVAL);
168 nd->event = sd_event_unref(nd->event);
173 sd_event *sd_icmp6_nd_get_event(sd_icmp6_nd *nd) {
179 sd_icmp6_nd *sd_icmp6_nd_ref(sd_icmp6_nd *nd) {
182 assert_se(REFCNT_INC(nd->n_ref) >= 2);
187 static int icmp6_nd_init(sd_icmp6_nd *nd) {
190 nd->recv = sd_event_source_unref(nd->recv);
191 nd->fd = asynchronous_close(nd->fd);
192 nd->timeout = sd_event_source_unref(nd->timeout);
197 sd_icmp6_nd *sd_icmp6_nd_unref(sd_icmp6_nd *nd) {
198 if (nd && REFCNT_DEC(nd->n_ref) == 0) {
199 ICMP6Prefix *prefix, *p;
202 sd_icmp6_nd_detach_event(nd);
204 LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) {
205 LIST_REMOVE(prefixes, nd->prefixes, prefix);
207 prefix = icmp6_prefix_unref(prefix);
216 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_icmp6_nd*, sd_icmp6_nd_unref);
217 #define _cleanup_sd_icmp6_nd_free_ _cleanup_(sd_icmp6_nd_unrefp)
219 int sd_icmp6_nd_new(sd_icmp6_nd **ret) {
220 _cleanup_sd_icmp6_nd_free_ sd_icmp6_nd *nd = NULL;
224 nd = new0(sd_icmp6_nd, 1);
228 nd->n_ref = REFCNT_INIT;
233 LIST_HEAD_INIT(nd->prefixes);
241 int sd_icmp6_ra_get_mtu(sd_icmp6_nd *nd, uint32_t *mtu) {
242 assert_return(nd, -EINVAL);
243 assert_return(mtu, -EINVAL);
253 static int icmp6_ra_prefix_timeout(sd_event_source *s, uint64_t usec,
255 sd_icmp6_nd *nd = userdata;
256 ICMP6Prefix *prefix, *p;
260 LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) {
261 if (prefix->timeout_valid != s)
264 log_icmp6_nd(nd, "Prefix expired "SD_ICMP6_ADDRESS_FORMAT_STR"/%d",
265 SD_ICMP6_ADDRESS_FORMAT_VAL(prefix->addr),
268 LIST_REMOVE(prefixes, nd->prefixes, prefix);
270 nd->expired_prefix = prefix;
272 ICMP6_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED);
273 nd->expired_prefix = NULL;
275 prefix = icmp6_prefix_unref(prefix);
283 static int icmp6_ra_prefix_set_timeout(sd_icmp6_nd *nd,
289 assert_return(prefix, -EINVAL);
291 r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
295 prefix->timeout_valid = sd_event_source_unref(prefix->timeout_valid);
297 r = sd_event_add_time(nd->event, &prefix->timeout_valid,
298 clock_boottime_or_monotonic(), time_now + valid,
299 USEC_PER_SEC, icmp6_ra_prefix_timeout, nd);
303 r = sd_event_source_set_priority(prefix->timeout_valid,
308 r = sd_event_source_set_description(prefix->timeout_valid,
309 "icmp6-prefix-timeout");
313 prefix->timeout_valid =
314 sd_event_source_unref(prefix->timeout_valid);
319 static int icmp6_prefix_match(const struct in6_addr *prefix, uint8_t prefixlen,
320 const struct in6_addr *addr,
321 uint8_t addr_prefixlen) {
322 uint8_t bytes, mask, len;
324 assert_return(prefix, -EINVAL);
325 assert_return(addr, -EINVAL);
327 len = MIN(prefixlen, addr_prefixlen);
330 mask = 0xff << (8 - len % 8);
332 if (memcmp(prefix, addr, bytes) != 0 ||
333 (prefix->s6_addr[bytes] & mask) != (addr->s6_addr[bytes] & mask))
334 return -EADDRNOTAVAIL;
339 static int icmp6_ra_prefix_match(ICMP6Prefix *head, const struct in6_addr *addr,
340 uint8_t addr_len, ICMP6Prefix **result) {
343 LIST_FOREACH(prefixes, prefix, head) {
344 if (icmp6_prefix_match(&prefix->addr, prefix->len, addr,
351 return -EADDRNOTAVAIL;
354 int sd_icmp6_prefix_match(struct in6_addr *prefix, uint8_t prefixlen,
355 struct in6_addr *addr) {
356 return icmp6_prefix_match(prefix, prefixlen, addr,
357 sizeof(addr->s6_addr) * 8);
360 int sd_icmp6_ra_get_prefixlen(sd_icmp6_nd *nd, const struct in6_addr *addr,
361 uint8_t *prefixlen) {
365 assert_return(nd, -EINVAL);
366 assert_return(addr, -EINVAL);
367 assert_return(prefixlen, -EINVAL);
369 r = icmp6_ra_prefix_match(nd->prefixes, addr,
370 sizeof(addr->s6_addr) * 8, &prefix);
374 *prefixlen = prefix->len;
379 int sd_icmp6_ra_get_expired_prefix(sd_icmp6_nd *nd, struct in6_addr **addr,
382 assert_return(nd, -EINVAL);
383 assert_return(addr, -EINVAL);
384 assert_return(prefixlen, -EINVAL);
386 if (!nd->expired_prefix)
387 return -EADDRNOTAVAIL;
389 *addr = &nd->expired_prefix->addr;
390 *prefixlen = nd->expired_prefix->len;
395 static int icmp6_ra_prefix_update(sd_icmp6_nd *nd, ssize_t len,
396 const struct nd_opt_prefix_info *prefix_opt) {
400 char time_string[FORMAT_TIMESPAN_MAX];
402 assert_return(nd, -EINVAL);
403 assert_return(prefix_opt, -EINVAL);
405 if (len < prefix_opt->nd_opt_pi_len)
408 if (!(prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK))
411 lifetime = be32toh(prefix_opt->nd_opt_pi_valid_time);
413 r = icmp6_ra_prefix_match(nd->prefixes,
414 &prefix_opt->nd_opt_pi_prefix,
415 prefix_opt->nd_opt_pi_prefix_len, &prefix);
417 if (r < 0 && r != -EADDRNOTAVAIL)
420 /* if router advertisment prefix valid timeout is zero, the timeout
421 callback will be called immediately to clean up the prefix */
423 if (r == -EADDRNOTAVAIL) {
424 r = icmp6_prefix_new(&prefix);
428 prefix->len = prefix_opt->nd_opt_pi_prefix_len;
430 memcpy(&prefix->addr, &prefix_opt->nd_opt_pi_prefix,
431 sizeof(prefix->addr));
433 log_icmp6_nd(nd, "New prefix "SD_ICMP6_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
434 SD_ICMP6_ADDRESS_FORMAT_VAL(prefix->addr),
435 prefix->len, lifetime,
436 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
437 lifetime * USEC_PER_SEC, 0));
439 LIST_PREPEND(prefixes, nd->prefixes, prefix);
442 if (prefix->len != prefix_opt->nd_opt_pi_prefix_len) {
445 prefixlen = MIN(prefix->len, prefix_opt->nd_opt_pi_prefix_len);
447 log_icmp6_nd(nd, "Prefix length mismatch %d/%d using %d",
449 prefix_opt->nd_opt_pi_prefix_len,
452 prefix->len = prefixlen;
455 log_icmp6_nd(nd, "Update prefix "SD_ICMP6_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
456 SD_ICMP6_ADDRESS_FORMAT_VAL(prefix->addr),
457 prefix->len, lifetime,
458 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
459 lifetime * USEC_PER_SEC, 0));
462 r = icmp6_ra_prefix_set_timeout(nd, prefix, lifetime * USEC_PER_SEC);
467 static int icmp6_ra_parse(sd_icmp6_nd *nd, struct nd_router_advert *ra,
470 struct nd_opt_hdr *opt_hdr;
472 assert_return(nd, -EINVAL);
473 assert_return(ra, -EINVAL);
476 if (len < ICMP6_OPT_LEN_UNITS) {
477 log_icmp6_nd(nd, "Router Advertisement below minimum length");
485 while (len != 0 && len >= opt_hdr->nd_opt_len * ICMP6_OPT_LEN_UNITS) {
486 struct nd_opt_mtu *opt_mtu;
488 struct nd_opt_prefix_info *opt_prefix;
490 if (opt_hdr->nd_opt_len == 0)
493 switch (opt_hdr->nd_opt_type) {
497 mtu = be32toh(opt_mtu->nd_opt_mtu_mtu);
499 if (mtu != nd->mtu) {
500 nd->mtu = MAX(mtu, IP6_MIN_MTU);
502 log_icmp6_nd(nd, "Router Advertisement link MTU %d using %d",
508 case ND_OPT_PREFIX_INFORMATION:
511 icmp6_ra_prefix_update(nd, len, opt_prefix);
516 len -= opt_hdr->nd_opt_len * ICMP6_OPT_LEN_UNITS;
517 opt = (void *)((char *)opt +
518 opt_hdr->nd_opt_len * ICMP6_OPT_LEN_UNITS);
523 log_icmp6_nd(nd, "Router Advertisement contains %zd bytes of trailing garbage", len);
528 static int icmp6_router_advertisment_recv(sd_event_source *s, int fd,
529 uint32_t revents, void *userdata)
531 sd_icmp6_nd *nd = userdata;
534 _cleanup_free_ struct nd_router_advert *ra = NULL;
535 int event = ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE;
541 r = ioctl(fd, FIONREAD, &buflen);
542 if (r < 0 || buflen <= 0)
543 buflen = ICMP6_ND_RECV_SIZE;
549 len = read(fd, ra, buflen);
551 log_icmp6_nd(nd, "Could not receive message from UDP socket: %m");
555 if (ra->nd_ra_type != ND_ROUTER_ADVERT)
558 if (ra->nd_ra_code != 0)
561 nd->timeout = sd_event_source_unref(nd->timeout);
563 nd->state = ICMP6_ROUTER_ADVERTISMENT_LISTEN;
565 if (ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER )
566 event = ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER;
568 if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
569 event = ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED;
571 log_icmp6_nd(nd, "Received Router Advertisement flags %s/%s",
572 ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED? "MANAGED": "none",
573 ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER? "OTHER": "none");
575 if (event != ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE) {
576 r = icmp6_ra_parse(nd, ra, len);
578 log_icmp6_nd(nd, "Could not parse Router Advertisement: %s",
584 icmp6_nd_notify(nd, event);
589 static int icmp6_router_solicitation_timeout(sd_event_source *s, uint64_t usec,
592 sd_icmp6_nd *nd = userdata;
593 uint64_t time_now, next_timeout;
594 struct ether_addr unset = { };
595 struct ether_addr *addr = NULL;
602 nd->timeout = sd_event_source_unref(nd->timeout);
604 if (nd->nd_sent >= ICMP6_MAX_ROUTER_SOLICITATIONS) {
605 icmp6_nd_notify(nd, ICMP6_EVENT_ROUTER_ADVERTISMENT_TIMEOUT);
606 nd->state = ICMP6_ROUTER_ADVERTISMENT_LISTEN;
608 if (memcmp(&nd->mac_addr, &unset, sizeof(struct ether_addr)))
609 addr = &nd->mac_addr;
611 r = dhcp_network_icmp6_send_router_solicitation(nd->fd, addr);
613 log_icmp6_nd(nd, "Error sending Router Solicitation");
615 nd->state = ICMP6_ROUTER_SOLICITATION_SENT;
616 log_icmp6_nd(nd, "Sent Router Solicitation");
621 r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
623 icmp6_nd_notify(nd, r);
627 next_timeout = time_now + ICMP6_ROUTER_SOLICITATION_INTERVAL;
629 r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
631 icmp6_router_solicitation_timeout, nd);
633 icmp6_nd_notify(nd, r);
637 r = sd_event_source_set_priority(nd->timeout,
640 icmp6_nd_notify(nd, r);
644 r = sd_event_source_set_description(nd->timeout, "icmp6-timeout");
646 icmp6_nd_notify(nd, r);
654 int sd_icmp6_nd_stop(sd_icmp6_nd *nd) {
655 assert_return(nd, -EINVAL);
656 assert_return(nd->event, -EINVAL);
658 log_icmp6_nd(client, "Stop ICMPv6");
662 nd->state = ICMP6_NEIGHBOR_DISCOVERY_IDLE;
667 int sd_icmp6_router_solicitation_start(sd_icmp6_nd *nd) {
673 if (nd->state != ICMP6_NEIGHBOR_DISCOVERY_IDLE)
679 r = dhcp_network_icmp6_bind_router_solicitation(nd->index);
685 r = sd_event_add_io(nd->event, &nd->recv, nd->fd, EPOLLIN,
686 icmp6_router_advertisment_recv, nd);
690 r = sd_event_source_set_priority(nd->recv, nd->event_priority);
694 r = sd_event_source_set_description(nd->recv, "icmp6-receive-message");
698 r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
699 0, 0, icmp6_router_solicitation_timeout, nd);
703 r = sd_event_source_set_priority(nd->timeout, nd->event_priority);
707 r = sd_event_source_set_description(nd->timeout, "icmp6-timeout");
712 log_icmp6_nd(client, "Start Router Solicitation");