2014-06-19 14:38:55 +02:00
|
|
|
/***
|
|
|
|
This file is part of systemd.
|
|
|
|
|
|
|
|
Copyright (C) 2014 Intel Corporation. All rights reserved.
|
|
|
|
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
|
|
|
under the terms of the GNU Lesser General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
systemd is distributed in the hope that it will be useful, but
|
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
|
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
|
|
|
|
#include <netinet/icmp6.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <netinet/in.h>
|
2015-01-20 18:35:57 +01:00
|
|
|
#include <netinet/ip6.h>
|
2014-06-19 14:38:55 +02:00
|
|
|
#include <stdbool.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <string.h>
|
2015-01-20 18:35:57 +01:00
|
|
|
#include <sys/ioctl.h>
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "sd-ndisc.h"
|
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2014-06-19 14:38:55 +02:00
|
|
|
#include "async.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "icmp6-util.h"
|
2015-10-19 22:15:50 +02:00
|
|
|
#include "in-addr-util.h"
|
2015-10-16 18:09:10 +02:00
|
|
|
#include "list.h"
|
|
|
|
#include "socket-util.h"
|
2015-11-19 12:18:04 +01:00
|
|
|
#include "string-util.h"
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2016-05-23 15:57:39 +02:00
|
|
|
#define NDISC_ROUTER_SOLICITATION_INTERVAL (4U * USEC_PER_SEC)
|
|
|
|
#define NDISC_MAX_ROUTER_SOLICITATIONS 3U
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
enum NDiscState {
|
2015-10-18 17:13:03 +02:00
|
|
|
NDISC_STATE_IDLE,
|
|
|
|
NDISC_STATE_SOLICITATION_SENT,
|
|
|
|
NDISC_STATE_ADVERTISMENT_LISTEN,
|
|
|
|
_NDISC_STATE_MAX,
|
|
|
|
_NDISC_STATE_INVALID = -1,
|
2014-06-19 14:38:55 +02:00
|
|
|
};
|
|
|
|
|
2016-05-23 15:57:39 +02:00
|
|
|
#define IP6_MIN_MTU 1280U
|
2015-10-16 19:17:50 +02:00
|
|
|
#define ICMP6_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr))
|
2016-05-23 15:57:39 +02:00
|
|
|
#define NDISC_OPT_LEN_UNITS 8U
|
2015-01-20 18:35:57 +01:00
|
|
|
|
2015-10-19 22:15:50 +02:00
|
|
|
#define ND_RA_FLAG_PREF 0x18
|
|
|
|
#define ND_RA_FLAG_PREF_LOW 0x03
|
|
|
|
#define ND_RA_FLAG_PREF_MEDIUM 0x0
|
|
|
|
#define ND_RA_FLAG_PREF_HIGH 0x1
|
|
|
|
#define ND_RA_FLAG_PREF_INVALID 0x2
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
typedef struct NDiscPrefix NDiscPrefix;
|
2015-01-20 18:35:58 +01:00
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
struct NDiscPrefix {
|
2015-08-27 19:56:52 +02:00
|
|
|
unsigned n_ref;
|
2015-01-20 18:35:58 +01:00
|
|
|
|
2015-10-18 17:27:10 +02:00
|
|
|
sd_ndisc *nd;
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
LIST_FIELDS(NDiscPrefix, prefixes);
|
2015-01-20 18:35:58 +01:00
|
|
|
|
|
|
|
uint8_t len;
|
2015-10-18 17:45:22 +02:00
|
|
|
usec_t valid_until;
|
2015-01-20 18:35:58 +01:00
|
|
|
struct in6_addr addr;
|
|
|
|
};
|
|
|
|
|
2015-10-16 17:07:47 +02:00
|
|
|
struct sd_ndisc {
|
2015-08-27 19:56:52 +02:00
|
|
|
unsigned n_ref;
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
enum NDiscState state;
|
2014-06-19 14:38:55 +02:00
|
|
|
sd_event *event;
|
|
|
|
int event_priority;
|
2016-05-23 16:13:18 +02:00
|
|
|
int ifindex;
|
2014-06-19 14:38:55 +02:00
|
|
|
struct ether_addr mac_addr;
|
2015-01-20 18:35:58 +01:00
|
|
|
uint32_t mtu;
|
2015-10-16 19:17:50 +02:00
|
|
|
LIST_HEAD(NDiscPrefix, prefixes);
|
2014-06-19 14:38:55 +02:00
|
|
|
int fd;
|
|
|
|
sd_event_source *recv;
|
|
|
|
sd_event_source *timeout;
|
2016-05-23 15:57:39 +02:00
|
|
|
unsigned nd_sent;
|
2015-10-19 22:15:50 +02:00
|
|
|
sd_ndisc_router_callback_t router_callback;
|
|
|
|
sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback;
|
|
|
|
sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback;
|
2015-10-16 17:07:47 +02:00
|
|
|
sd_ndisc_callback_t callback;
|
2014-06-19 14:38:55 +02:00
|
|
|
void *userdata;
|
|
|
|
};
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
#define log_ndisc(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "NDisc CLIENT: " fmt, ##__VA_ARGS__)
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
static NDiscPrefix *ndisc_prefix_unref(NDiscPrefix *prefix) {
|
2015-01-20 18:35:58 +01:00
|
|
|
|
2015-08-27 19:56:52 +02:00
|
|
|
if (!prefix)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
assert(prefix->n_ref > 0);
|
|
|
|
prefix->n_ref--;
|
|
|
|
|
|
|
|
if (prefix->n_ref > 0)
|
|
|
|
return NULL;
|
2015-01-20 18:35:58 +01:00
|
|
|
|
2015-10-18 17:27:10 +02:00
|
|
|
if (prefix->nd)
|
|
|
|
LIST_REMOVE(prefixes, prefix->nd->prefixes, prefix);
|
|
|
|
|
2015-08-27 19:56:52 +02:00
|
|
|
free(prefix);
|
2015-10-18 17:27:10 +02:00
|
|
|
|
2015-01-20 18:35:58 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-10-18 17:27:10 +02:00
|
|
|
static int ndisc_prefix_new(sd_ndisc *nd, NDiscPrefix **ret) {
|
2016-01-11 23:26:57 +01:00
|
|
|
NDiscPrefix *prefix;
|
2015-01-20 18:35:58 +01:00
|
|
|
|
|
|
|
assert(ret);
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
prefix = new0(NDiscPrefix, 1);
|
2015-01-20 18:35:58 +01:00
|
|
|
if (!prefix)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2015-08-27 19:56:52 +02:00
|
|
|
prefix->n_ref = 1;
|
2015-01-20 18:35:58 +01:00
|
|
|
LIST_INIT(prefixes, prefix);
|
2015-10-18 17:27:10 +02:00
|
|
|
prefix->nd = nd;
|
2015-01-20 18:35:58 +01:00
|
|
|
|
|
|
|
*ret = prefix;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-19 22:15:50 +02:00
|
|
|
int sd_ndisc_set_callback(sd_ndisc *nd,
|
|
|
|
sd_ndisc_router_callback_t router_callback,
|
|
|
|
sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback,
|
|
|
|
sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback,
|
|
|
|
sd_ndisc_callback_t callback,
|
|
|
|
void *userdata) {
|
2014-06-19 14:38:55 +02:00
|
|
|
assert(nd);
|
|
|
|
|
2015-10-19 22:15:50 +02:00
|
|
|
nd->router_callback = router_callback;
|
|
|
|
nd->prefix_onlink_callback = prefix_onlink_callback;
|
|
|
|
nd->prefix_autonomous_callback = prefix_autonomous_callback;
|
2014-06-19 14:38:55 +02:00
|
|
|
nd->callback = callback;
|
|
|
|
nd->userdata = userdata;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-23 16:13:18 +02:00
|
|
|
int sd_ndisc_set_ifindex(sd_ndisc *nd, int ifindex) {
|
|
|
|
assert_return(nd, -EINVAL);
|
|
|
|
assert_return(ifindex > 0, -EINVAL);
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2016-05-23 16:13:18 +02:00
|
|
|
nd->ifindex = ifindex;
|
2014-06-19 14:38:55 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-16 17:07:47 +02:00
|
|
|
int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr) {
|
2014-06-19 14:38:55 +02:00
|
|
|
assert(nd);
|
|
|
|
|
|
|
|
if (mac_addr)
|
|
|
|
memcpy(&nd->mac_addr, mac_addr, sizeof(nd->mac_addr));
|
|
|
|
else
|
2014-12-11 16:58:45 +01:00
|
|
|
zero(nd->mac_addr);
|
2014-06-19 14:38:55 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-02-16 19:33:36 +01:00
|
|
|
int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority) {
|
2014-06-19 14:38:55 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(nd, -EINVAL);
|
|
|
|
assert_return(!nd->event, -EBUSY);
|
|
|
|
|
|
|
|
if (event)
|
|
|
|
nd->event = sd_event_ref(event);
|
|
|
|
else {
|
|
|
|
r = sd_event_default(&nd->event);
|
|
|
|
if (r < 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
nd->event_priority = priority;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-16 17:07:47 +02:00
|
|
|
int sd_ndisc_detach_event(sd_ndisc *nd) {
|
2014-06-19 14:38:55 +02:00
|
|
|
assert_return(nd, -EINVAL);
|
|
|
|
|
|
|
|
nd->event = sd_event_unref(nd->event);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-16 17:07:47 +02:00
|
|
|
sd_event *sd_ndisc_get_event(sd_ndisc *nd) {
|
2014-06-19 14:38:55 +02:00
|
|
|
assert(nd);
|
|
|
|
|
|
|
|
return nd->event;
|
|
|
|
}
|
|
|
|
|
2015-10-16 17:07:47 +02:00
|
|
|
sd_ndisc *sd_ndisc_ref(sd_ndisc *nd) {
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2015-08-27 19:56:52 +02:00
|
|
|
if (!nd)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
assert(nd->n_ref > 0);
|
|
|
|
nd->n_ref++;
|
2014-06-19 14:38:55 +02:00
|
|
|
|
|
|
|
return nd;
|
|
|
|
}
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
static int ndisc_init(sd_ndisc *nd) {
|
2014-06-19 14:38:55 +02:00
|
|
|
assert(nd);
|
|
|
|
|
|
|
|
nd->recv = sd_event_source_unref(nd->recv);
|
|
|
|
nd->fd = asynchronous_close(nd->fd);
|
|
|
|
nd->timeout = sd_event_source_unref(nd->timeout);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-16 17:07:47 +02:00
|
|
|
sd_ndisc *sd_ndisc_unref(sd_ndisc *nd) {
|
2015-10-16 19:17:50 +02:00
|
|
|
NDiscPrefix *prefix, *p;
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2015-08-27 19:56:52 +02:00
|
|
|
if (!nd)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
assert(nd->n_ref > 0);
|
|
|
|
nd->n_ref--;
|
|
|
|
|
|
|
|
if (nd->n_ref > 0)
|
|
|
|
return NULL;
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
ndisc_init(nd);
|
2015-10-16 17:07:47 +02:00
|
|
|
sd_ndisc_detach_event(nd);
|
2015-01-20 18:35:58 +01:00
|
|
|
|
2015-10-18 17:27:10 +02:00
|
|
|
LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes)
|
2015-10-16 19:17:50 +02:00
|
|
|
prefix = ndisc_prefix_unref(prefix);
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2015-08-27 19:56:52 +02:00
|
|
|
free(nd);
|
|
|
|
|
2014-06-19 14:38:55 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-10-16 17:07:47 +02:00
|
|
|
int sd_ndisc_new(sd_ndisc **ret) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
|
2014-06-19 14:38:55 +02:00
|
|
|
|
|
|
|
assert(ret);
|
|
|
|
|
2015-10-16 17:07:47 +02:00
|
|
|
nd = new0(sd_ndisc, 1);
|
2014-06-19 14:38:55 +02:00
|
|
|
if (!nd)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2015-08-27 19:56:52 +02:00
|
|
|
nd->n_ref = 1;
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2016-05-23 16:13:18 +02:00
|
|
|
nd->ifindex = -1;
|
2014-06-19 19:32:29 +02:00
|
|
|
nd->fd = -1;
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2015-01-20 18:35:58 +01:00
|
|
|
LIST_HEAD_INIT(nd->prefixes);
|
|
|
|
|
2014-06-19 14:38:55 +02:00
|
|
|
*ret = nd;
|
|
|
|
nd = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-16 17:07:47 +02:00
|
|
|
int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu) {
|
2015-01-20 18:35:59 +01:00
|
|
|
assert_return(nd, -EINVAL);
|
|
|
|
assert_return(mtu, -EINVAL);
|
|
|
|
|
|
|
|
if (nd->mtu == 0)
|
|
|
|
return -ENOMSG;
|
|
|
|
|
|
|
|
*mtu = nd->mtu;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
static int prefix_match(const struct in6_addr *prefix, uint8_t prefixlen,
|
|
|
|
const struct in6_addr *addr,
|
|
|
|
uint8_t addr_prefixlen) {
|
2015-01-20 18:36:01 +01:00
|
|
|
uint8_t bytes, mask, len;
|
|
|
|
|
|
|
|
assert_return(prefix, -EINVAL);
|
|
|
|
assert_return(addr, -EINVAL);
|
|
|
|
|
|
|
|
len = MIN(prefixlen, addr_prefixlen);
|
|
|
|
|
|
|
|
bytes = len / 8;
|
|
|
|
mask = 0xff << (8 - len % 8);
|
|
|
|
|
|
|
|
if (memcmp(prefix, addr, bytes) != 0 ||
|
|
|
|
(prefix->s6_addr[bytes] & mask) != (addr->s6_addr[bytes] & mask))
|
|
|
|
return -EADDRNOTAVAIL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-18 17:45:22 +02:00
|
|
|
static int ndisc_prefix_match(sd_ndisc *nd, const struct in6_addr *addr,
|
|
|
|
uint8_t addr_len, NDiscPrefix **result) {
|
|
|
|
NDiscPrefix *prefix, *p;
|
|
|
|
usec_t time_now;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(nd);
|
|
|
|
|
|
|
|
r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) {
|
|
|
|
if (prefix->valid_until < time_now) {
|
|
|
|
prefix = ndisc_prefix_unref(prefix);
|
|
|
|
continue;
|
|
|
|
}
|
2015-01-20 18:36:01 +01:00
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
if (prefix_match(&prefix->addr, prefix->len, addr, addr_len) >= 0) {
|
2015-01-20 18:36:01 +01:00
|
|
|
*result = prefix;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EADDRNOTAVAIL;
|
|
|
|
}
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len,
|
2015-10-18 17:45:22 +02:00
|
|
|
const struct nd_opt_prefix_info *prefix_opt) {
|
2015-10-16 19:17:50 +02:00
|
|
|
NDiscPrefix *prefix;
|
2015-10-19 22:15:50 +02:00
|
|
|
uint32_t lifetime_valid, lifetime_preferred;
|
2015-10-18 17:45:22 +02:00
|
|
|
usec_t time_now;
|
2015-01-20 18:36:01 +01:00
|
|
|
char time_string[FORMAT_TIMESPAN_MAX];
|
2015-10-18 17:45:22 +02:00
|
|
|
int r;
|
2015-01-20 18:36:01 +01:00
|
|
|
|
2015-10-18 17:45:22 +02:00
|
|
|
assert(nd);
|
|
|
|
assert(prefix_opt);
|
2015-01-20 18:36:01 +01:00
|
|
|
|
|
|
|
if (len < prefix_opt->nd_opt_pi_len)
|
|
|
|
return -ENOMSG;
|
|
|
|
|
2015-10-19 22:15:50 +02:00
|
|
|
if (!(prefix_opt->nd_opt_pi_flags_reserved & (ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO)))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &prefix_opt->nd_opt_pi_prefix) > 0)
|
2015-01-20 18:36:01 +01:00
|
|
|
return 0;
|
|
|
|
|
2015-10-19 22:15:50 +02:00
|
|
|
lifetime_valid = be32toh(prefix_opt->nd_opt_pi_valid_time);
|
|
|
|
lifetime_preferred = be32toh(prefix_opt->nd_opt_pi_preferred_time);
|
|
|
|
|
|
|
|
if (lifetime_valid < lifetime_preferred)
|
|
|
|
return 0;
|
2015-01-20 18:36:01 +01:00
|
|
|
|
2015-10-18 17:45:22 +02:00
|
|
|
r = ndisc_prefix_match(nd, &prefix_opt->nd_opt_pi_prefix,
|
|
|
|
prefix_opt->nd_opt_pi_prefix_len, &prefix);
|
2016-01-13 02:22:30 +01:00
|
|
|
if (r < 0) {
|
|
|
|
if (r != -EADDRNOTAVAIL)
|
|
|
|
return r;
|
2015-01-20 18:36:01 +01:00
|
|
|
|
2016-01-13 02:22:30 +01:00
|
|
|
/* if router advertisment prefix valid timeout is zero, the timeout
|
|
|
|
callback will be called immediately to clean up the prefix */
|
2015-01-20 18:36:01 +01:00
|
|
|
|
2015-10-18 17:27:10 +02:00
|
|
|
r = ndisc_prefix_new(nd, &prefix);
|
2015-01-20 18:36:01 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
prefix->len = prefix_opt->nd_opt_pi_prefix_len;
|
|
|
|
|
|
|
|
memcpy(&prefix->addr, &prefix_opt->nd_opt_pi_prefix,
|
|
|
|
sizeof(prefix->addr));
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
log_ndisc(nd, "New prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
|
2016-01-13 02:22:30 +01:00
|
|
|
SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr),
|
|
|
|
prefix->len, lifetime_valid,
|
|
|
|
format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC));
|
2015-01-20 18:36:01 +01:00
|
|
|
|
|
|
|
LIST_PREPEND(prefixes, nd->prefixes, prefix);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (prefix->len != prefix_opt->nd_opt_pi_prefix_len) {
|
|
|
|
uint8_t prefixlen;
|
|
|
|
|
|
|
|
prefixlen = MIN(prefix->len, prefix_opt->nd_opt_pi_prefix_len);
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
log_ndisc(nd, "Prefix length mismatch %d/%d using %d",
|
2016-01-13 02:22:30 +01:00
|
|
|
prefix->len,
|
|
|
|
prefix_opt->nd_opt_pi_prefix_len,
|
|
|
|
prefixlen);
|
2015-01-20 18:36:01 +01:00
|
|
|
|
|
|
|
prefix->len = prefixlen;
|
|
|
|
}
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
log_ndisc(nd, "Update prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
|
2016-01-13 02:22:30 +01:00
|
|
|
SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr),
|
|
|
|
prefix->len, lifetime_valid,
|
|
|
|
format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC));
|
2015-01-20 18:36:01 +01:00
|
|
|
}
|
|
|
|
|
2015-10-18 17:45:22 +02:00
|
|
|
r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-10-19 22:15:50 +02:00
|
|
|
prefix->valid_until = time_now + lifetime_valid * USEC_PER_SEC;
|
|
|
|
|
|
|
|
if ((prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) && nd->prefix_onlink_callback)
|
|
|
|
nd->prefix_onlink_callback(nd, &prefix->addr, prefix->len, prefix->valid_until, nd->userdata);
|
|
|
|
|
|
|
|
if ((prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) && nd->prefix_autonomous_callback)
|
|
|
|
nd->prefix_autonomous_callback(nd, &prefix->addr, prefix->len, lifetime_preferred, lifetime_valid,
|
|
|
|
nd->userdata);
|
2015-01-20 18:36:01 +01:00
|
|
|
|
2015-10-19 22:15:50 +02:00
|
|
|
return 0;
|
2015-01-20 18:36:01 +01:00
|
|
|
}
|
|
|
|
|
2015-11-23 15:59:58 +01:00
|
|
|
static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra, ssize_t len) {
|
2015-01-20 18:35:57 +01:00
|
|
|
void *opt;
|
|
|
|
struct nd_opt_hdr *opt_hdr;
|
|
|
|
|
|
|
|
assert_return(nd, -EINVAL);
|
|
|
|
assert_return(ra, -EINVAL);
|
|
|
|
|
|
|
|
len -= sizeof(*ra);
|
2015-10-16 19:17:50 +02:00
|
|
|
if (len < NDISC_OPT_LEN_UNITS) {
|
|
|
|
log_ndisc(nd, "Router Advertisement below minimum length");
|
2015-01-20 18:35:57 +01:00
|
|
|
|
|
|
|
return -ENOMSG;
|
|
|
|
}
|
|
|
|
|
|
|
|
opt = ra + 1;
|
|
|
|
opt_hdr = opt;
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
while (len != 0 && len >= opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS) {
|
2015-01-20 18:35:59 +01:00
|
|
|
struct nd_opt_mtu *opt_mtu;
|
|
|
|
uint32_t mtu;
|
2015-01-20 18:36:01 +01:00
|
|
|
struct nd_opt_prefix_info *opt_prefix;
|
2015-01-20 18:35:57 +01:00
|
|
|
|
|
|
|
if (opt_hdr->nd_opt_len == 0)
|
|
|
|
return -ENOMSG;
|
|
|
|
|
|
|
|
switch (opt_hdr->nd_opt_type) {
|
2015-01-20 18:35:59 +01:00
|
|
|
case ND_OPT_MTU:
|
|
|
|
opt_mtu = opt;
|
|
|
|
|
|
|
|
mtu = be32toh(opt_mtu->nd_opt_mtu_mtu);
|
|
|
|
|
|
|
|
if (mtu != nd->mtu) {
|
|
|
|
nd->mtu = MAX(mtu, IP6_MIN_MTU);
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
log_ndisc(nd, "Router Advertisement link MTU %d using %d",
|
2016-01-13 02:22:30 +01:00
|
|
|
mtu, nd->mtu);
|
2015-01-20 18:35:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2015-01-20 18:35:57 +01:00
|
|
|
|
2015-01-20 18:36:01 +01:00
|
|
|
case ND_OPT_PREFIX_INFORMATION:
|
|
|
|
opt_prefix = opt;
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
ndisc_prefix_update(nd, len, opt_prefix);
|
2015-01-20 18:36:01 +01:00
|
|
|
|
|
|
|
break;
|
2015-01-20 18:35:57 +01:00
|
|
|
}
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
len -= opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS;
|
2015-01-20 18:35:57 +01:00
|
|
|
opt = (void *)((char *)opt +
|
2015-10-16 19:17:50 +02:00
|
|
|
opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS);
|
2015-01-20 18:35:57 +01:00
|
|
|
opt_hdr = opt;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len > 0)
|
2015-10-16 19:17:50 +02:00
|
|
|
log_ndisc(nd, "Router Advertisement contains %zd bytes of trailing garbage", len);
|
2015-01-20 18:35:57 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
2015-10-19 22:15:50 +02:00
|
|
|
_cleanup_free_ struct nd_router_advert *ra = NULL;
|
2015-10-16 17:07:47 +02:00
|
|
|
sd_ndisc *nd = userdata;
|
2015-11-23 15:59:58 +01:00
|
|
|
union {
|
|
|
|
struct cmsghdr cmsghdr;
|
|
|
|
uint8_t buf[CMSG_LEN(sizeof(int))];
|
|
|
|
} control = {};
|
|
|
|
struct iovec iov = {};
|
|
|
|
union sockaddr_union sa = {};
|
|
|
|
struct msghdr msg = {
|
|
|
|
.msg_name = &sa.sa,
|
|
|
|
.msg_namelen = sizeof(sa),
|
|
|
|
.msg_iov = &iov,
|
|
|
|
.msg_iovlen = 1,
|
|
|
|
.msg_control = &control,
|
|
|
|
.msg_controllen = sizeof(control),
|
|
|
|
};
|
|
|
|
struct cmsghdr *cmsg;
|
2015-11-19 12:18:04 +01:00
|
|
|
struct in6_addr *gw;
|
2015-10-19 22:15:50 +02:00
|
|
|
unsigned lifetime;
|
2016-02-15 22:50:01 +01:00
|
|
|
ssize_t len, buflen;
|
|
|
|
int r, pref, stateful;
|
2014-06-19 14:38:55 +02:00
|
|
|
|
|
|
|
assert(s);
|
|
|
|
assert(nd);
|
|
|
|
assert(nd->event);
|
|
|
|
|
2016-02-15 22:50:01 +01:00
|
|
|
buflen = next_datagram_size_fd(fd);
|
|
|
|
if (buflen < 0)
|
|
|
|
return buflen;
|
2015-01-20 18:35:57 +01:00
|
|
|
|
2015-11-23 15:59:58 +01:00
|
|
|
iov.iov_len = buflen;
|
|
|
|
|
|
|
|
ra = malloc(iov.iov_len);
|
2015-01-20 18:35:57 +01:00
|
|
|
if (!ra)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2015-11-23 15:59:58 +01:00
|
|
|
iov.iov_base = ra;
|
|
|
|
|
|
|
|
len = recvmsg(fd, &msg, 0);
|
2015-01-20 18:35:57 +01:00
|
|
|
if (len < 0) {
|
2015-11-24 18:25:52 +01:00
|
|
|
if (errno == EAGAIN || errno == EINTR)
|
|
|
|
return 0;
|
|
|
|
|
2015-10-19 22:15:50 +02:00
|
|
|
log_ndisc(nd, "Could not receive message from ICMPv6 socket: %m");
|
2015-11-24 18:25:52 +01:00
|
|
|
return -errno;
|
2016-05-23 15:56:01 +02:00
|
|
|
}
|
|
|
|
if ((size_t) len < sizeof(struct nd_router_advert)) {
|
|
|
|
log_ndisc(nd, "Too small to be a router advertisement: ignoring");
|
2015-11-23 15:59:58 +01:00
|
|
|
return 0;
|
2016-05-23 15:56:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (msg.msg_namelen == 0)
|
2015-11-19 12:18:04 +01:00
|
|
|
gw = NULL; /* only happens when running the test-suite over a socketpair */
|
2015-11-23 15:59:58 +01:00
|
|
|
else if (msg.msg_namelen != sizeof(sa.in6)) {
|
|
|
|
log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)msg.msg_namelen);
|
2014-06-19 14:38:55 +02:00
|
|
|
return 0;
|
2015-11-19 12:18:04 +01:00
|
|
|
} else
|
2015-11-23 15:59:58 +01:00
|
|
|
gw = &sa.in6.sin6_addr;
|
|
|
|
|
|
|
|
assert(!(msg.msg_flags & MSG_CTRUNC));
|
|
|
|
assert(!(msg.msg_flags & MSG_TRUNC));
|
|
|
|
|
|
|
|
CMSG_FOREACH(cmsg, &msg) {
|
|
|
|
if (cmsg->cmsg_level == SOL_IPV6 &&
|
|
|
|
cmsg->cmsg_type == IPV6_HOPLIMIT &&
|
|
|
|
cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
|
|
|
|
int hops = *(int*)CMSG_DATA(cmsg);
|
|
|
|
|
|
|
|
if (hops != 255) {
|
|
|
|
log_ndisc(nd, "Received RA with invalid hop limit %d. Ignoring.", hops);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-11-19 12:18:04 +01:00
|
|
|
|
|
|
|
if (gw && !in_addr_is_link_local(AF_INET6, (const union in_addr_union*) gw)) {
|
|
|
|
_cleanup_free_ char *addr = NULL;
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2015-11-19 12:18:04 +01:00
|
|
|
(void)in_addr_to_string(AF_INET6, (const union in_addr_union*) gw, &addr);
|
|
|
|
|
|
|
|
log_ndisc(nd, "Received RA from non-link-local address %s. Ignoring.", strna(addr));
|
2015-11-19 01:23:24 +01:00
|
|
|
return 0;
|
2015-11-19 12:18:04 +01:00
|
|
|
}
|
2015-11-19 01:23:24 +01:00
|
|
|
|
2015-01-20 18:35:57 +01:00
|
|
|
if (ra->nd_ra_type != ND_ROUTER_ADVERT)
|
2014-06-19 14:38:55 +02:00
|
|
|
return 0;
|
|
|
|
|
2015-01-20 18:35:57 +01:00
|
|
|
if (ra->nd_ra_code != 0)
|
2014-06-19 14:38:55 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
nd->timeout = sd_event_source_unref(nd->timeout);
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
nd->state = NDISC_STATE_ADVERTISMENT_LISTEN;
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2015-10-19 22:15:50 +02:00
|
|
|
stateful = ra->nd_ra_flags_reserved & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER);
|
2015-11-12 16:37:18 +01:00
|
|
|
pref = (ra->nd_ra_flags_reserved & ND_RA_FLAG_PREF) >> 3;
|
networkd: ndisc - handle router advertisement in userspace
Router Discovery is a core part of IPv6, which by default is handled by the kernel.
However, the kernel implementation is meant as a fall-back, and to fully support
the protocol a userspace implementation is desired.
The protocol essentially listens for Router Advertisement packets from routers
on the local link and use these to configure the client automatically. The four
main pieces of information are: what kind (if any) of DHCPv6 configuration should
be performed; a default gateway; the prefixes that should be considered to be on
the local link; and the prefixes with which we can preform SLAAC in order to pick
a global IPv6 address.
A lot of additional information is also available, which we do not yet fully
support, but which will eventually allow us to avoid the need for DHCPv6 in the
common case.
Short-term, the reason for wanting this is in userspace was the desire to fully
track all the addresses on links we manage, and that is not possible for addresses
managed by the kernel (as the kernel does not expose to us the fact that it
manages these addresses). Moreover, we would like to support stable privacy
addresses, which will soon be mandated and the legacy MAC-based global addresses
deprecated, to do this well we need to handle the generation in userspace. Lastly,
more long-term we wish to support more RA options than what the kernel exposes.
2015-11-03 13:02:16 +01:00
|
|
|
|
|
|
|
switch (pref) {
|
2015-10-19 22:15:50 +02:00
|
|
|
case ND_RA_FLAG_PREF_LOW:
|
|
|
|
case ND_RA_FLAG_PREF_HIGH:
|
|
|
|
break;
|
|
|
|
default:
|
networkd: ndisc - handle router advertisement in userspace
Router Discovery is a core part of IPv6, which by default is handled by the kernel.
However, the kernel implementation is meant as a fall-back, and to fully support
the protocol a userspace implementation is desired.
The protocol essentially listens for Router Advertisement packets from routers
on the local link and use these to configure the client automatically. The four
main pieces of information are: what kind (if any) of DHCPv6 configuration should
be performed; a default gateway; the prefixes that should be considered to be on
the local link; and the prefixes with which we can preform SLAAC in order to pick
a global IPv6 address.
A lot of additional information is also available, which we do not yet fully
support, but which will eventually allow us to avoid the need for DHCPv6 in the
common case.
Short-term, the reason for wanting this is in userspace was the desire to fully
track all the addresses on links we manage, and that is not possible for addresses
managed by the kernel (as the kernel does not expose to us the fact that it
manages these addresses). Moreover, we would like to support stable privacy
addresses, which will soon be mandated and the legacy MAC-based global addresses
deprecated, to do this well we need to handle the generation in userspace. Lastly,
more long-term we wish to support more RA options than what the kernel exposes.
2015-11-03 13:02:16 +01:00
|
|
|
pref = ND_RA_FLAG_PREF_MEDIUM;
|
2015-10-19 22:15:50 +02:00
|
|
|
break;
|
|
|
|
}
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2015-10-19 22:15:50 +02:00
|
|
|
lifetime = be16toh(ra->nd_ra_router_lifetime);
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2015-10-19 22:15:50 +02:00
|
|
|
log_ndisc(nd, "Received Router Advertisement: flags %s preference %s lifetime %u sec",
|
|
|
|
stateful & ND_RA_FLAG_MANAGED ? "MANAGED" : stateful & ND_RA_FLAG_OTHER ? "OTHER" : "none",
|
networkd: ndisc - handle router advertisement in userspace
Router Discovery is a core part of IPv6, which by default is handled by the kernel.
However, the kernel implementation is meant as a fall-back, and to fully support
the protocol a userspace implementation is desired.
The protocol essentially listens for Router Advertisement packets from routers
on the local link and use these to configure the client automatically. The four
main pieces of information are: what kind (if any) of DHCPv6 configuration should
be performed; a default gateway; the prefixes that should be considered to be on
the local link; and the prefixes with which we can preform SLAAC in order to pick
a global IPv6 address.
A lot of additional information is also available, which we do not yet fully
support, but which will eventually allow us to avoid the need for DHCPv6 in the
common case.
Short-term, the reason for wanting this is in userspace was the desire to fully
track all the addresses on links we manage, and that is not possible for addresses
managed by the kernel (as the kernel does not expose to us the fact that it
manages these addresses). Moreover, we would like to support stable privacy
addresses, which will soon be mandated and the legacy MAC-based global addresses
deprecated, to do this well we need to handle the generation in userspace. Lastly,
more long-term we wish to support more RA options than what the kernel exposes.
2015-11-03 13:02:16 +01:00
|
|
|
pref == ND_RA_FLAG_PREF_HIGH ? "high" : pref == ND_RA_FLAG_PREF_LOW ? "low" : "medium",
|
2015-10-19 22:15:50 +02:00
|
|
|
lifetime);
|
2015-01-20 18:35:57 +01:00
|
|
|
|
2015-10-19 22:15:50 +02:00
|
|
|
r = ndisc_ra_parse(nd, ra, len);
|
|
|
|
if (r < 0) {
|
|
|
|
log_ndisc(nd, "Could not parse Router Advertisement: %s", strerror(-r));
|
|
|
|
return 0;
|
2015-01-20 18:35:57 +01:00
|
|
|
}
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2015-10-19 22:15:50 +02:00
|
|
|
if (nd->router_callback)
|
2015-11-19 12:18:04 +01:00
|
|
|
nd->router_callback(nd, stateful, gw, lifetime, pref, nd->userdata);
|
2014-06-19 14:38:55 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
|
2015-10-16 17:07:47 +02:00
|
|
|
sd_ndisc *nd = userdata;
|
2014-06-19 14:38:55 +02:00
|
|
|
uint64_t time_now, next_timeout;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(s);
|
|
|
|
assert(nd);
|
|
|
|
assert(nd->event);
|
|
|
|
|
|
|
|
nd->timeout = sd_event_source_unref(nd->timeout);
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
if (nd->nd_sent >= NDISC_MAX_ROUTER_SOLICITATIONS) {
|
2015-10-19 22:15:50 +02:00
|
|
|
if (nd->callback)
|
|
|
|
nd->callback(nd, SD_NDISC_EVENT_TIMEOUT, nd->userdata);
|
2015-10-16 19:17:50 +02:00
|
|
|
nd->state = NDISC_STATE_ADVERTISMENT_LISTEN;
|
2014-06-19 14:38:55 +02:00
|
|
|
} else {
|
2015-11-23 15:49:10 +01:00
|
|
|
r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr);
|
2014-06-19 14:38:55 +02:00
|
|
|
if (r < 0)
|
2015-10-16 19:17:50 +02:00
|
|
|
log_ndisc(nd, "Error sending Router Solicitation");
|
2014-06-19 14:38:55 +02:00
|
|
|
else {
|
2015-10-16 19:17:50 +02:00
|
|
|
nd->state = NDISC_STATE_SOLICITATION_SENT;
|
|
|
|
log_ndisc(nd, "Sent Router Solicitation");
|
2014-06-19 14:38:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
nd->nd_sent++;
|
|
|
|
|
2015-10-22 17:34:58 +02:00
|
|
|
assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
next_timeout = time_now + NDISC_ROUTER_SOLICITATION_INTERVAL;
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2014-07-24 18:53:01 +02:00
|
|
|
r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
|
2014-06-19 14:38:55 +02:00
|
|
|
next_timeout, 0,
|
2015-10-16 19:17:50 +02:00
|
|
|
ndisc_router_solicitation_timeout, nd);
|
2014-06-19 14:38:55 +02:00
|
|
|
if (r < 0) {
|
2015-10-22 17:46:35 +02:00
|
|
|
/* we cannot continue if we are unable to rearm the timer */
|
|
|
|
sd_ndisc_stop(nd);
|
2014-06-19 14:38:55 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-22 17:34:58 +02:00
|
|
|
r = sd_event_source_set_priority(nd->timeout, nd->event_priority);
|
|
|
|
if (r < 0)
|
2014-06-19 14:38:55 +02:00
|
|
|
return 0;
|
2014-08-28 15:46:29 +02:00
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
r = sd_event_source_set_description(nd->timeout, "ndisc-timeout");
|
2015-10-22 17:34:58 +02:00
|
|
|
if (r < 0)
|
2014-08-28 15:46:29 +02:00
|
|
|
return 0;
|
2014-06-19 14:38:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-16 17:07:47 +02:00
|
|
|
int sd_ndisc_stop(sd_ndisc *nd) {
|
2014-06-25 11:33:48 +02:00
|
|
|
assert_return(nd, -EINVAL);
|
|
|
|
assert_return(nd->event, -EINVAL);
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
log_ndisc(client, "Stop NDisc");
|
2014-06-25 11:33:48 +02:00
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
ndisc_init(nd);
|
2014-06-25 11:33:48 +02:00
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
nd->state = NDISC_STATE_IDLE;
|
2014-06-25 11:33:48 +02:00
|
|
|
|
2015-10-19 22:15:50 +02:00
|
|
|
if (nd->callback)
|
|
|
|
nd->callback(nd, SD_NDISC_EVENT_STOP, nd->userdata);
|
2015-10-22 17:46:35 +02:00
|
|
|
|
2014-06-25 11:33:48 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-16 17:07:47 +02:00
|
|
|
int sd_ndisc_router_discovery_start(sd_ndisc *nd) {
|
2014-06-19 14:38:55 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(nd);
|
|
|
|
assert(nd->event);
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
if (nd->state != NDISC_STATE_IDLE)
|
2015-11-16 16:46:14 +01:00
|
|
|
return -EBUSY;
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2016-05-23 16:13:18 +02:00
|
|
|
if (nd->ifindex < 1)
|
2014-06-19 14:38:55 +02:00
|
|
|
return -EINVAL;
|
|
|
|
|
2016-05-23 16:13:18 +02:00
|
|
|
r = icmp6_bind_router_solicitation(nd->ifindex);
|
2014-06-19 14:38:55 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
nd->fd = r;
|
|
|
|
|
|
|
|
r = sd_event_add_io(nd->event, &nd->recv, nd->fd, EPOLLIN,
|
2015-10-16 19:17:50 +02:00
|
|
|
ndisc_router_advertisment_recv, nd);
|
2014-06-19 14:38:55 +02:00
|
|
|
if (r < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
r = sd_event_source_set_priority(nd->recv, nd->event_priority);
|
|
|
|
if (r < 0)
|
|
|
|
goto error;
|
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
r = sd_event_source_set_description(nd->recv, "ndisc-receive-message");
|
2014-08-28 15:46:29 +02:00
|
|
|
if (r < 0)
|
|
|
|
goto error;
|
|
|
|
|
2014-07-24 18:53:01 +02:00
|
|
|
r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
|
2015-10-16 19:17:50 +02:00
|
|
|
0, 0, ndisc_router_solicitation_timeout, nd);
|
2014-06-19 14:38:55 +02:00
|
|
|
if (r < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
r = sd_event_source_set_priority(nd->timeout, nd->event_priority);
|
2014-08-28 15:46:29 +02:00
|
|
|
if (r < 0)
|
|
|
|
goto error;
|
2014-06-19 14:38:55 +02:00
|
|
|
|
2015-10-16 19:17:50 +02:00
|
|
|
r = sd_event_source_set_description(nd->timeout, "ndisc-timeout");
|
2014-06-19 14:38:55 +02:00
|
|
|
error:
|
|
|
|
if (r < 0)
|
2015-10-16 19:17:50 +02:00
|
|
|
ndisc_init(nd);
|
2014-06-19 14:38:55 +02:00
|
|
|
else
|
2015-10-16 19:17:50 +02:00
|
|
|
log_ndisc(client, "Start Router Solicitation");
|
2014-06-19 14:38:55 +02:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|