mirror of
https://github.com/systemd/systemd.git
synced 2024-11-24 10:43:35 +08:00
Merge pull request #3394 from poettering/triple-tstamp
timestamping improvements and IPv6 RA revamp
This commit is contained in:
commit
a849538e3b
@ -3430,6 +3430,9 @@ libsystemd_network_la_SOURCES = \
|
||||
src/libsystemd-network/network-internal.c \
|
||||
src/libsystemd-network/network-internal.h \
|
||||
src/libsystemd-network/sd-ndisc.c \
|
||||
src/libsystemd-network/ndisc-internal.h \
|
||||
src/libsystemd-network/ndisc-router.h \
|
||||
src/libsystemd-network/ndisc-router.c \
|
||||
src/libsystemd-network/icmp6-util.h \
|
||||
src/libsystemd-network/icmp6-util.c \
|
||||
src/libsystemd-network/sd-dhcp6-client.c \
|
||||
@ -5462,6 +5465,7 @@ libnetworkd_core_la_SOURCES = \
|
||||
src/network/networkd-ipv4ll.c \
|
||||
src/network/networkd-dhcp4.c \
|
||||
src/network/networkd-dhcp6.c \
|
||||
src/network/networkd-ndisc.h \
|
||||
src/network/networkd-ndisc.c \
|
||||
src/network/networkd-network.h \
|
||||
src/network/networkd-network.c \
|
||||
|
@ -528,23 +528,19 @@
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>IPv6AcceptRouterAdvertisements=</varname></term>
|
||||
<listitem><para>Force the setting of the <filename>accept_ra</filename>
|
||||
(router advertisements) setting for the interface.
|
||||
When unset, the kernel default is used, and router
|
||||
advertisements are accepted only when local forwarding
|
||||
is disabled for that interface.
|
||||
When router advertisements are accepted, they will
|
||||
trigger the start of the DHCPv6 client if the relevant
|
||||
flags are passed, or if no routers are found on the link.
|
||||
Takes a boolean. If true, router advertisements are
|
||||
accepted, when false, router advertisements are ignored,
|
||||
independently of the local forwarding state.</para>
|
||||
<listitem><para>Enable or disable IPv6 Router Advertisement (RA) reception support for the interface. Takes
|
||||
a boolean parameter. If true, RAs are accepted; if false, RAs are ignored, independently of the local
|
||||
forwarding state. When not set, the kernel default is used, and RAs are accepted only when local forwarding
|
||||
is disabled for that interface. When RAs are accepted, they may trigger the start of the DHCPv6 client if
|
||||
the relevant flags are set in the RA data, or if no routers are found on the link.</para>
|
||||
|
||||
<para>See
|
||||
<ulink url="https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt">ip-sysctl.txt</ulink>
|
||||
in the kernel documentation, but note that systemd's
|
||||
setting of <constant>1</constant> corresponds to
|
||||
kernel's setting of <constant>2</constant>.</para>
|
||||
<para>Further settings for the IPv6 RA support may be configured in the
|
||||
<literal>[IPv6AcceptRouterAdvertisements]</literal> section, see below.</para>
|
||||
|
||||
<para>Also see <ulink
|
||||
url="https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt">ip-sysctl.txt</ulink> in the kernel
|
||||
documentation regarding <literal>accept_ra</literal>, but note that systemd's setting of
|
||||
<constant>1</constant> (i.e. true) corresponds to kernel's setting of <constant>2</constant>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
@ -799,7 +795,7 @@
|
||||
false.</para>
|
||||
|
||||
<para>It is recommended to enable this option only on trusted networks, as setting this affects resolution
|
||||
of all host names, in particular to single-label names. It is generally safer to use the supplied domain
|
||||
of all host names, in particular of single-label names. It is generally safer to use the supplied domain
|
||||
only as routing domain, rather than as search domain, in order to not have it affect local resolution of
|
||||
single-label names.</para>
|
||||
|
||||
@ -898,6 +894,47 @@
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>[IPv6AcceptRouterAdvertisements] Section Options</title>
|
||||
<para>The <literal>[IPv6AcceptRouterAdvertisements]</literal> section configures the IPv6 Router Advertisement
|
||||
(RA) client, if it is enabled with the <varname>IPv6AcceptRouterAdvertisements=</varname> setting described
|
||||
above:</para>
|
||||
|
||||
<variablelist class='network-directives'>
|
||||
<varlistentry>
|
||||
<term><varname>UseDNS=</varname></term>
|
||||
<listitem>
|
||||
<para>When true (the default), the DNS servers received in the Router Advertisement will be used and take
|
||||
precedence over any statically configured ones.</para>
|
||||
|
||||
<para>This corresponds to the <option>nameserver</option> option in <citerefentry
|
||||
project='man-pages'><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>UseDomains=</varname></term>
|
||||
<listitem>
|
||||
<para>Takes a boolean argument, or the special value <literal>route</literal>. When true, the domain name
|
||||
received via IPv6 Router Advertisement (RA) will be used as DNS search domain over this link, similar to
|
||||
the effect of the <option>Domains=</option> setting. If set to <literal>route</literal>, the domain name
|
||||
received via IPv6 RA will be used for routing DNS queries only, but not for searching, similar to the
|
||||
effect of the <option>Domains=</option> setting when the argument is prefixed with
|
||||
<literal>~</literal>. Defaults to false.</para>
|
||||
|
||||
<para>It is recommended to enable this option only on trusted networks, as setting this affects resolution
|
||||
of all host names, in particular of single-label names. It is generally safer to use the supplied domain
|
||||
only as routing domain, rather than as search domain, in order to not have it affect local resolution of
|
||||
single-label names.</para>
|
||||
|
||||
<para>When set to true, this setting corresponds to the <option>domain</option> option in <citerefentry
|
||||
project='man-pages'><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
|
||||
<refsect1>
|
||||
<title>[DHCPServer] Section Options</title>
|
||||
<para>The <literal>[DHCPServer]</literal> section contains
|
||||
|
@ -38,8 +38,7 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
|
||||
return "FAILURE";
|
||||
}
|
||||
|
||||
|
||||
if (level == EXIT_STATUS_SYSTEMD || level == EXIT_STATUS_LSB) {
|
||||
if (IN_SET(level, EXIT_STATUS_SYSTEMD, EXIT_STATUS_LSB)) {
|
||||
switch ((int) status) {
|
||||
|
||||
case EXIT_CHDIR:
|
||||
@ -189,13 +188,9 @@ bool is_clean_exit(int code, int status, ExitStatusSet *success_status) {
|
||||
/* If a daemon does not implement handlers for some of the
|
||||
* signals that's not considered an unclean shutdown */
|
||||
if (code == CLD_KILLED)
|
||||
return
|
||||
status == SIGHUP ||
|
||||
status == SIGINT ||
|
||||
status == SIGTERM ||
|
||||
status == SIGPIPE ||
|
||||
return IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE) ||
|
||||
(success_status &&
|
||||
set_contains(success_status->signal, INT_TO_PTR(status)));
|
||||
set_contains(success_status->signal, INT_TO_PTR(status)));
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -207,15 +202,14 @@ bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status) {
|
||||
|
||||
return
|
||||
code == CLD_EXITED &&
|
||||
(status == EXIT_NOTINSTALLED || status == EXIT_NOTCONFIGURED);
|
||||
IN_SET(status, EXIT_NOTINSTALLED, EXIT_NOTCONFIGURED);
|
||||
}
|
||||
|
||||
void exit_status_set_free(ExitStatusSet *x) {
|
||||
assert(x);
|
||||
|
||||
set_free(x->status);
|
||||
set_free(x->signal);
|
||||
x->status = x->signal = NULL;
|
||||
x->status = set_free(x->status);
|
||||
x->signal = set_free(x->signal);
|
||||
}
|
||||
|
||||
bool exit_status_set_is_empty(ExitStatusSet *x) {
|
||||
|
@ -25,6 +25,12 @@
|
||||
#include "macro.h"
|
||||
#include "set.h"
|
||||
|
||||
/* This defines pretty names for the LSB 'start' verb exit codes. Note that they shouldn't be confused with the LSB
|
||||
* 'status' verb exit codes which are defined very differently. For details see:
|
||||
*
|
||||
* https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
|
||||
*/
|
||||
|
||||
typedef enum ExitStatus {
|
||||
/* EXIT_SUCCESS defined by libc */
|
||||
/* EXIT_FAILURE defined by libc */
|
||||
@ -37,9 +43,7 @@ typedef enum ExitStatus {
|
||||
|
||||
/* The LSB suggests that error codes >= 200 are "reserved". We
|
||||
* use them here under the assumption that they hence are
|
||||
* unused by init scripts.
|
||||
*
|
||||
* http://refspecs.linuxfoundation.org/LSB_3.2.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html */
|
||||
* unused by init scripts. */
|
||||
|
||||
EXIT_CHDIR = 200,
|
||||
EXIT_NICE,
|
||||
@ -81,9 +85,9 @@ typedef enum ExitStatus {
|
||||
} ExitStatus;
|
||||
|
||||
typedef enum ExitStatusLevel {
|
||||
EXIT_STATUS_MINIMAL,
|
||||
EXIT_STATUS_SYSTEMD,
|
||||
EXIT_STATUS_LSB,
|
||||
EXIT_STATUS_MINIMAL, /* only cover libc EXIT_STATUS/EXIT_FAILURE */
|
||||
EXIT_STATUS_SYSTEMD, /* cover libc and systemd's own exit codes */
|
||||
EXIT_STATUS_LSB, /* cover libc, systemd's own and LSB exit codes */
|
||||
EXIT_STATUS_FULL = EXIT_STATUS_LSB
|
||||
} ExitStatusLevel;
|
||||
|
||||
|
@ -28,18 +28,26 @@
|
||||
#include "macro.h"
|
||||
#include "util.h"
|
||||
|
||||
bool in4_addr_is_null(const struct in_addr *a) {
|
||||
return a->s_addr == 0;
|
||||
}
|
||||
|
||||
bool in6_addr_is_null(const struct in6_addr *a) {
|
||||
return
|
||||
a->s6_addr32[0] == 0 &&
|
||||
a->s6_addr32[1] == 0 &&
|
||||
a->s6_addr32[2] == 0 &&
|
||||
a->s6_addr32[3] == 0;
|
||||
}
|
||||
|
||||
int in_addr_is_null(int family, const union in_addr_union *u) {
|
||||
assert(u);
|
||||
|
||||
if (family == AF_INET)
|
||||
return u->in.s_addr == 0;
|
||||
return in4_addr_is_null(&u->in);
|
||||
|
||||
if (family == AF_INET6)
|
||||
return
|
||||
u->in6.s6_addr32[0] == 0 &&
|
||||
u->in6.s6_addr32[1] == 0 &&
|
||||
u->in6.s6_addr32[2] == 0 &&
|
||||
u->in6.s6_addr32[3] == 0;
|
||||
return in6_addr_is_null(&u->in6);
|
||||
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
@ -36,6 +36,9 @@ struct in_addr_data {
|
||||
union in_addr_union address;
|
||||
};
|
||||
|
||||
bool in4_addr_is_null(const struct in_addr *a);
|
||||
bool in6_addr_is_null(const struct in6_addr *a);
|
||||
|
||||
int in_addr_is_null(int family, const union in_addr_union *u);
|
||||
int in_addr_is_link_local(int family, const union in_addr_union *u);
|
||||
int in_addr_is_localhost(int family, const union in_addr_union *u);
|
||||
|
@ -87,6 +87,16 @@ dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
|
||||
return ts;
|
||||
}
|
||||
|
||||
triple_timestamp* triple_timestamp_get(triple_timestamp *ts) {
|
||||
assert(ts);
|
||||
|
||||
ts->realtime = now(CLOCK_REALTIME);
|
||||
ts->monotonic = now(CLOCK_MONOTONIC);
|
||||
ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME) : USEC_INFINITY;
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
|
||||
int64_t delta;
|
||||
assert(ts);
|
||||
@ -104,6 +114,24 @@ dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
|
||||
return ts;
|
||||
}
|
||||
|
||||
triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) {
|
||||
int64_t delta;
|
||||
|
||||
assert(ts);
|
||||
|
||||
if (u == USEC_INFINITY || u <= 0) {
|
||||
ts->realtime = ts->monotonic = ts->boottime = u;
|
||||
return ts;
|
||||
}
|
||||
|
||||
ts->realtime = u;
|
||||
delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
|
||||
ts->monotonic = usec_sub(now(CLOCK_MONOTONIC), delta);
|
||||
ts->boottime = clock_boottime_supported() ? usec_sub(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY;
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
|
||||
int64_t delta;
|
||||
assert(ts);
|
||||
@ -136,6 +164,26 @@ dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, us
|
||||
return ts;
|
||||
}
|
||||
|
||||
usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) {
|
||||
|
||||
switch (clock) {
|
||||
|
||||
case CLOCK_REALTIME:
|
||||
case CLOCK_REALTIME_ALARM:
|
||||
return ts->realtime;
|
||||
|
||||
case CLOCK_MONOTONIC:
|
||||
return ts->monotonic;
|
||||
|
||||
case CLOCK_BOOTTIME:
|
||||
case CLOCK_BOOTTIME_ALARM:
|
||||
return ts->boottime;
|
||||
|
||||
default:
|
||||
return USEC_INFINITY;
|
||||
}
|
||||
}
|
||||
|
||||
usec_t timespec_load(const struct timespec *ts) {
|
||||
assert(ts);
|
||||
|
||||
@ -1107,6 +1155,30 @@ clockid_t clock_boottime_or_monotonic(void) {
|
||||
return CLOCK_MONOTONIC;
|
||||
}
|
||||
|
||||
bool clock_supported(clockid_t clock) {
|
||||
struct timespec ts;
|
||||
|
||||
switch (clock) {
|
||||
|
||||
case CLOCK_MONOTONIC:
|
||||
case CLOCK_REALTIME:
|
||||
return true;
|
||||
|
||||
case CLOCK_BOOTTIME:
|
||||
return clock_boottime_supported();
|
||||
|
||||
case CLOCK_BOOTTIME_ALARM:
|
||||
if (!clock_boottime_supported())
|
||||
return false;
|
||||
|
||||
/* fall through, after checking the cached value for CLOCK_BOOTTIME. */
|
||||
|
||||
default:
|
||||
/* For everything else, check properly */
|
||||
return clock_gettime(clock, &ts) >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
int get_timezone(char **tz) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
const char *e;
|
||||
|
@ -39,6 +39,12 @@ typedef struct dual_timestamp {
|
||||
usec_t monotonic;
|
||||
} dual_timestamp;
|
||||
|
||||
typedef struct triple_timestamp {
|
||||
usec_t realtime;
|
||||
usec_t monotonic;
|
||||
usec_t boottime;
|
||||
} triple_timestamp;
|
||||
|
||||
#define USEC_INFINITY ((usec_t) -1)
|
||||
#define NSEC_INFINITY ((nsec_t) -1)
|
||||
|
||||
@ -69,7 +75,8 @@ typedef struct dual_timestamp {
|
||||
|
||||
#define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1)
|
||||
|
||||
#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) { 0ULL, 0ULL })
|
||||
#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) {})
|
||||
#define TRIPLE_TIMESTAMP_NULL ((struct triple_timestamp) {})
|
||||
|
||||
usec_t now(clockid_t clock);
|
||||
nsec_t now_nsec(clockid_t clock);
|
||||
@ -79,11 +86,28 @@ dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
|
||||
dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u);
|
||||
dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u);
|
||||
|
||||
triple_timestamp* triple_timestamp_get(triple_timestamp *ts);
|
||||
triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u);
|
||||
|
||||
#define DUAL_TIMESTAMP_HAS_CLOCK(clock) \
|
||||
IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC)
|
||||
|
||||
#define TRIPLE_TIMESTAMP_HAS_CLOCK(clock) \
|
||||
IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM)
|
||||
|
||||
static inline bool dual_timestamp_is_set(dual_timestamp *ts) {
|
||||
return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
|
||||
(ts->monotonic > 0 && ts->monotonic != USEC_INFINITY));
|
||||
}
|
||||
|
||||
static inline bool triple_timestamp_is_set(triple_timestamp *ts) {
|
||||
return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
|
||||
(ts->monotonic > 0 && ts->monotonic != USEC_INFINITY) ||
|
||||
(ts->boottime > 0 && ts->boottime != USEC_INFINITY));
|
||||
}
|
||||
|
||||
usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock);
|
||||
|
||||
usec_t timespec_load(const struct timespec *ts) _pure_;
|
||||
struct timespec *timespec_store(struct timespec *ts, usec_t u);
|
||||
|
||||
@ -113,6 +137,7 @@ int get_timezones(char ***l);
|
||||
bool timezone_is_valid(const char *name);
|
||||
|
||||
bool clock_boottime_supported(void);
|
||||
bool clock_supported(clockid_t clock);
|
||||
clockid_t clock_boottime_or_monotonic(void);
|
||||
|
||||
#define xstrftime(buf, fmt, tm) \
|
||||
|
@ -109,3 +109,21 @@ static inline void unaligned_write_le64(void *_u, uint64_t a) {
|
||||
unaligned_write_le32(u, (uint32_t) a);
|
||||
unaligned_write_le32(u + 4, (uint32_t) (a >> 32));
|
||||
}
|
||||
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
#define unaligned_read_ne16 unaligned_read_be16
|
||||
#define unaligned_read_ne32 unaligned_read_be32
|
||||
#define unaligned_read_ne64 unaligned_read_be64
|
||||
|
||||
#define unaligned_write_ne16 unaligned_write_be16
|
||||
#define unaligned_write_ne32 unaligned_write_be32
|
||||
#define unaligned_write_ne64 unaligned_write_be64
|
||||
#else
|
||||
#define unaligned_read_ne16 unaligned_read_le16
|
||||
#define unaligned_read_ne32 unaligned_read_le32
|
||||
#define unaligned_read_ne64 unaligned_read_le64
|
||||
|
||||
#define unaligned_write_ne16 unaligned_write_le16
|
||||
#define unaligned_write_ne32 unaligned_write_le32
|
||||
#define unaligned_write_ne64 unaligned_write_le64
|
||||
#endif
|
||||
|
@ -49,7 +49,8 @@ int icmp6_bind_router_solicitation(int index) {
|
||||
};
|
||||
_cleanup_close_ int s = -1;
|
||||
char ifname[IF_NAMESIZE] = "";
|
||||
int r, zero = 0, one = 1, hops = 255;
|
||||
static const int zero = 0, one = 1, hops = 255;
|
||||
int r;
|
||||
|
||||
s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6);
|
||||
if (s < 0)
|
||||
@ -85,6 +86,10 @@ int icmp6_bind_router_solicitation(int index) {
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
r = setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
if (if_indextoname(index, ifname) == 0)
|
||||
return -errno;
|
||||
|
||||
|
@ -28,6 +28,8 @@
|
||||
#include "prioq.h"
|
||||
|
||||
struct sd_lldp {
|
||||
unsigned n_ref;
|
||||
|
||||
int ifindex;
|
||||
int fd;
|
||||
|
||||
|
@ -360,9 +360,16 @@ end_marker:
|
||||
void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
|
||||
assert(n);
|
||||
|
||||
if (n->ttl > 0)
|
||||
n->until = usec_add(now(clock_boottime_or_monotonic()), n->ttl * USEC_PER_SEC);
|
||||
else
|
||||
if (n->ttl > 0) {
|
||||
usec_t base;
|
||||
|
||||
/* Use the packet's timestamp if there is one known */
|
||||
base = triple_timestamp_by_clock(&n->timestamp, clock_boottime_or_monotonic());
|
||||
if (base <= 0 || base == USEC_INFINITY)
|
||||
base = now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */
|
||||
|
||||
n->until = usec_add(base, n->ttl * USEC_PER_SEC);
|
||||
} else
|
||||
n->until = 0;
|
||||
|
||||
if (n->lldp)
|
||||
@ -588,11 +595,11 @@ done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret) {
|
||||
_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
|
||||
assert_return(n, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
assert_return(ret_sec, -EINVAL);
|
||||
|
||||
*ret = n->ttl;
|
||||
*ret_sec = n->ttl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -651,7 +658,7 @@ _public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
|
||||
_public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
|
||||
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
|
||||
int r;
|
||||
|
||||
@ -668,7 +675,7 @@ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t ra
|
||||
return r;
|
||||
|
||||
*ret = n;
|
||||
n = 0;
|
||||
n = NULL;
|
||||
|
||||
return r;
|
||||
}
|
||||
@ -679,7 +686,7 @@ _public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
|
||||
assert(n->raw_size >= sizeof(struct ether_header));
|
||||
n->rindex = sizeof(struct ether_header);
|
||||
|
||||
return 0;
|
||||
return n->rindex < n->raw_size;
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
|
||||
@ -693,7 +700,7 @@ _public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
|
||||
if (n->rindex + 2 > n->raw_size) /* Truncated message */
|
||||
return -EBADMSG;
|
||||
|
||||
length = LLDP_NEIGHBOR_LENGTH(n);
|
||||
length = LLDP_NEIGHBOR_TLV_LENGTH(n);
|
||||
if (n->rindex + 2 + length > n->raw_size)
|
||||
return -EBADMSG;
|
||||
|
||||
@ -711,7 +718,7 @@ _public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
|
||||
if (n->rindex + 2 > n->raw_size)
|
||||
return -EBADMSG;
|
||||
|
||||
*type = LLDP_NEIGHBOR_TYPE(n);
|
||||
*type = LLDP_NEIGHBOR_TLV_TYPE(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -743,14 +750,14 @@ _public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], u
|
||||
if (r == 0)
|
||||
return -ENXIO;
|
||||
|
||||
length = LLDP_NEIGHBOR_LENGTH(n);
|
||||
length = LLDP_NEIGHBOR_TLV_LENGTH(n);
|
||||
if (length < 4)
|
||||
return -EBADMSG;
|
||||
|
||||
if (n->rindex + 2 + length > n->raw_size)
|
||||
return -EBADMSG;
|
||||
|
||||
d = LLDP_NEIGHBOR_DATA(n);
|
||||
d = LLDP_NEIGHBOR_TLV_DATA(n);
|
||||
memcpy(oui, d, 3);
|
||||
*subtype = d[3];
|
||||
|
||||
@ -782,8 +789,7 @@ _public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret,
|
||||
if (n->rindex + 2 > n->raw_size)
|
||||
return -EBADMSG;
|
||||
|
||||
length = LLDP_NEIGHBOR_LENGTH(n);
|
||||
|
||||
length = LLDP_NEIGHBOR_TLV_LENGTH(n);
|
||||
if (n->rindex + 2 + length > n->raw_size)
|
||||
return -EBADMSG;
|
||||
|
||||
@ -792,3 +798,16 @@ _public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
|
||||
assert_return(n, -EINVAL);
|
||||
assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
|
||||
assert_return(clock_supported(clock), -EOPNOTSUPP);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (!triple_timestamp_is_set(&n->timestamp))
|
||||
return -ENODATA;
|
||||
|
||||
*ret = triple_timestamp_by_clock(&n->timestamp, clock);
|
||||
return 0;
|
||||
}
|
||||
|
@ -43,6 +43,8 @@ struct sd_lldp_neighbor {
|
||||
sd_lldp *lldp;
|
||||
unsigned n_ref;
|
||||
|
||||
triple_timestamp timestamp;
|
||||
|
||||
usec_t until;
|
||||
unsigned prioq_idx;
|
||||
|
||||
@ -81,18 +83,18 @@ static inline void *LLDP_NEIGHBOR_RAW(const sd_lldp_neighbor *n) {
|
||||
return (uint8_t*) n + ALIGN(sizeof(sd_lldp_neighbor));
|
||||
}
|
||||
|
||||
static inline uint8_t LLDP_NEIGHBOR_TYPE(const sd_lldp_neighbor *n) {
|
||||
static inline uint8_t LLDP_NEIGHBOR_TLV_TYPE(const sd_lldp_neighbor *n) {
|
||||
return ((uint8_t*) LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1;
|
||||
}
|
||||
|
||||
static inline size_t LLDP_NEIGHBOR_LENGTH(const sd_lldp_neighbor *n) {
|
||||
static inline size_t LLDP_NEIGHBOR_TLV_LENGTH(const sd_lldp_neighbor *n) {
|
||||
uint8_t *p;
|
||||
|
||||
p = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
|
||||
return p[1] + (((size_t) (p[0] & 1)) << 8);
|
||||
}
|
||||
|
||||
static inline void* LLDP_NEIGHBOR_DATA(const sd_lldp_neighbor *n) {
|
||||
static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) {
|
||||
return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2;
|
||||
}
|
||||
|
||||
|
49
src/libsystemd-network/ndisc-internal.h
Normal file
49
src/libsystemd-network/ndisc-internal.h
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
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 "log.h"
|
||||
|
||||
#include "sd-ndisc.h"
|
||||
|
||||
struct sd_ndisc {
|
||||
unsigned n_ref;
|
||||
|
||||
int ifindex;
|
||||
int fd;
|
||||
|
||||
sd_event *event;
|
||||
int event_priority;
|
||||
|
||||
struct ether_addr mac_addr;
|
||||
uint8_t hop_limit;
|
||||
uint32_t mtu;
|
||||
|
||||
sd_event_source *recv_event_source;
|
||||
sd_event_source *timeout_event_source;
|
||||
|
||||
unsigned nd_sent;
|
||||
|
||||
sd_ndisc_callback_t callback;
|
||||
void *userdata;
|
||||
};
|
||||
|
||||
#define log_ndisc_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "NDISC: " fmt, ##__VA_ARGS__)
|
||||
#define log_ndisc(fmt, ...) log_ndisc_errno(0, fmt, ##__VA_ARGS__)
|
779
src/libsystemd-network/ndisc-router.c
Normal file
779
src/libsystemd-network/ndisc-router.c
Normal file
@ -0,0 +1,779 @@
|
||||
/***
|
||||
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>
|
||||
|
||||
#include "sd-ndisc.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dns-domain.h"
|
||||
#include "hostname-util.h"
|
||||
#include "missing.h"
|
||||
#include "ndisc-internal.h"
|
||||
#include "ndisc-router.h"
|
||||
#include "strv.h"
|
||||
|
||||
_public_ sd_ndisc_router* sd_ndisc_router_ref(sd_ndisc_router *rt) {
|
||||
if (!rt)
|
||||
return NULL;
|
||||
|
||||
assert(rt->n_ref > 0);
|
||||
rt->n_ref++;
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
_public_ sd_ndisc_router* sd_ndisc_router_unref(sd_ndisc_router *rt) {
|
||||
if (!rt)
|
||||
return NULL;
|
||||
|
||||
assert(rt->n_ref > 0);
|
||||
rt->n_ref--;
|
||||
|
||||
if (rt->n_ref > 0)
|
||||
return NULL;
|
||||
|
||||
free(rt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sd_ndisc_router *ndisc_router_new(size_t raw_size) {
|
||||
sd_ndisc_router *rt;
|
||||
|
||||
rt = malloc0(ALIGN(sizeof(sd_ndisc_router)) + raw_size);
|
||||
if (!rt)
|
||||
return NULL;
|
||||
|
||||
rt->raw_size = raw_size;
|
||||
rt->n_ref = 1;
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_from_raw(sd_ndisc_router **ret, const void *raw, size_t raw_size) {
|
||||
_cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
|
||||
int r;
|
||||
|
||||
assert_return(ret, -EINVAL);
|
||||
assert_return(raw || raw_size <= 0, -EINVAL);
|
||||
|
||||
rt = ndisc_router_new(raw_size);
|
||||
if (!rt)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(NDISC_ROUTER_RAW(rt), raw, raw_size);
|
||||
r = ndisc_router_parse(rt);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = rt;
|
||||
rt = NULL;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) {
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret_addr, -EINVAL);
|
||||
|
||||
if (in6_addr_is_null(&rt->address))
|
||||
return -ENODATA;
|
||||
|
||||
*ret_addr = rt->address;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret) {
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
|
||||
assert_return(clock_supported(clock), -EOPNOTSUPP);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (!triple_timestamp_is_set(&rt->timestamp))
|
||||
return -ENODATA;
|
||||
|
||||
*ret = triple_timestamp_by_clock(&rt->timestamp, clock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size) {
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
assert_return(size, -EINVAL);
|
||||
|
||||
*ret = NDISC_ROUTER_RAW(rt);
|
||||
*size = rt->raw_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ndisc_router_parse(sd_ndisc_router *rt) {
|
||||
struct nd_router_advert *a;
|
||||
const uint8_t *p;
|
||||
bool has_mtu = false, has_flag_extension = false;
|
||||
size_t left;
|
||||
|
||||
assert(rt);
|
||||
|
||||
if (rt->raw_size < sizeof(struct nd_router_advert)) {
|
||||
log_ndisc("Too small to be a router advertisement, ignoring.");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/* Router advertisement packets are neatly aligned to 64bit boundaries, hence we can access them directly */
|
||||
a = NDISC_ROUTER_RAW(rt);
|
||||
|
||||
if (a->nd_ra_type != ND_ROUTER_ADVERT) {
|
||||
log_ndisc("Received ND packet that is not a router advertisement, ignoring.");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if (a->nd_ra_code != 0) {
|
||||
log_ndisc("Received ND packet with wrong RA code, ignoring.");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
rt->hop_limit = a->nd_ra_curhoplimit;
|
||||
rt->flags = a->nd_ra_flags_reserved; /* the first 8bit */
|
||||
rt->lifetime = be16toh(a->nd_ra_router_lifetime);
|
||||
|
||||
rt->preference = (rt->flags >> 3) & 3;
|
||||
if (!IN_SET(rt->preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH))
|
||||
rt->preference = SD_NDISC_PREFERENCE_MEDIUM;
|
||||
|
||||
p = (const uint8_t*) NDISC_ROUTER_RAW(rt) + sizeof(struct nd_router_advert);
|
||||
left = rt->raw_size - sizeof(struct nd_router_advert);
|
||||
|
||||
for (;;) {
|
||||
uint8_t type;
|
||||
size_t length;
|
||||
|
||||
if (left == 0)
|
||||
break;
|
||||
|
||||
if (left < 2) {
|
||||
log_ndisc("Option lacks header, ignoring datagram.");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
type = p[0];
|
||||
length = p[1] * 8;
|
||||
|
||||
if (length == 0) {
|
||||
log_ndisc("Zero-length option, ignoring datagram.");
|
||||
return -EBADMSG;
|
||||
}
|
||||
if (left < length) {
|
||||
log_ndisc("Option truncated, ignoring datagram.");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
|
||||
case SD_NDISC_OPTION_PREFIX_INFORMATION:
|
||||
|
||||
if (length != 4*8) {
|
||||
log_ndisc("Prefix option of invalid size, ignoring datagram.");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if (p[2] > 128) {
|
||||
log_ndisc("Bad prefix length, ignoring datagram.");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SD_NDISC_OPTION_MTU: {
|
||||
uint32_t m;
|
||||
|
||||
if (has_mtu) {
|
||||
log_ndisc("MTU option specified twice, ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (length != 8) {
|
||||
log_ndisc("MTU option of invalid size, ignoring datagram.");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
m = be32toh(*(uint32_t*) (p + 4));
|
||||
if (m >= IPV6_MIN_MTU) /* ignore invalidly small MTUs */
|
||||
rt->mtu = m;
|
||||
|
||||
has_mtu = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case SD_NDISC_OPTION_ROUTE_INFORMATION:
|
||||
if (length < 1*8 || length > 3*8) {
|
||||
log_ndisc("Route information option of invalid size, ignoring datagram.");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if (p[2] > 128) {
|
||||
log_ndisc("Bad route prefix length, ignoring datagram.");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SD_NDISC_OPTION_RDNSS:
|
||||
if (length < 3*8 || (length % (2*8)) != 1*8) {
|
||||
log_ndisc("RDNSS option has invalid size.");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SD_NDISC_OPTION_FLAGS_EXTENSION:
|
||||
|
||||
if (has_flag_extension) {
|
||||
log_ndisc("Flags extension option specified twice, ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (length < 1*8) {
|
||||
log_ndisc("Flags extension option has invalid size.");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/* Add in the additional flags bits */
|
||||
rt->flags |=
|
||||
((uint64_t) p[2] << 8) |
|
||||
((uint64_t) p[3] << 16) |
|
||||
((uint64_t) p[4] << 24) |
|
||||
((uint64_t) p[5] << 32) |
|
||||
((uint64_t) p[6] << 40) |
|
||||
((uint64_t) p[7] << 48);
|
||||
|
||||
has_flag_extension = true;
|
||||
break;
|
||||
|
||||
case SD_NDISC_OPTION_DNSSL:
|
||||
if (length < 2*8) {
|
||||
log_ndisc("DNSSL option has invalid size.");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
p += length, left -= length;
|
||||
}
|
||||
|
||||
rt->rindex = sizeof(struct nd_router_advert);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret) {
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
*ret = rt->hop_limit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret_flags) {
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret_flags, -EINVAL);
|
||||
|
||||
*ret_flags = rt->flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint16_t *ret_lifetime) {
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret_lifetime, -EINVAL);
|
||||
|
||||
*ret_lifetime = rt->lifetime;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret) {
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
*ret = rt->preference;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) {
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (rt->mtu <= 0)
|
||||
return -ENODATA;
|
||||
|
||||
*ret = rt->mtu;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) {
|
||||
assert_return(rt, -EINVAL);
|
||||
|
||||
assert(rt->raw_size >= sizeof(struct nd_router_advert));
|
||||
rt->rindex = sizeof(struct nd_router_advert);
|
||||
|
||||
return rt->rindex < rt->raw_size;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_option_next(sd_ndisc_router *rt) {
|
||||
size_t length;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
|
||||
if (rt->rindex == rt->raw_size) /* EOF */
|
||||
return -ESPIPE;
|
||||
|
||||
if (rt->rindex + 2 > rt->raw_size) /* Truncated message */
|
||||
return -EBADMSG;
|
||||
|
||||
length = NDISC_ROUTER_OPTION_LENGTH(rt);
|
||||
if (rt->rindex + length > rt->raw_size)
|
||||
return -EBADMSG;
|
||||
|
||||
rt->rindex += length;
|
||||
return rt->rindex < rt->raw_size;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) {
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (rt->rindex == rt->raw_size) /* EOF */
|
||||
return -ESPIPE;
|
||||
|
||||
if (rt->rindex + 2 > rt->raw_size) /* Truncated message */
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = NDISC_ROUTER_OPTION_TYPE(rt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) {
|
||||
uint8_t k;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
|
||||
r = sd_ndisc_router_option_get_type(rt, &k);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return type == k;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size) {
|
||||
size_t length;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
assert_return(size, -EINVAL);
|
||||
|
||||
/* Note that this returns the full option, including the option header */
|
||||
|
||||
if (rt->rindex + 2 > rt->raw_size)
|
||||
return -EBADMSG;
|
||||
|
||||
length = NDISC_ROUTER_OPTION_LENGTH(rt);
|
||||
if (rt->rindex + length > rt->raw_size)
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
|
||||
*size = length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_prefix_info(sd_ndisc_router *rt, struct nd_opt_prefix_info **ret) {
|
||||
struct nd_opt_prefix_info *ri;
|
||||
size_t length;
|
||||
int r;
|
||||
|
||||
assert(rt);
|
||||
assert(ret);
|
||||
|
||||
r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_PREFIX_INFORMATION);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EMEDIUMTYPE;
|
||||
|
||||
length = NDISC_ROUTER_OPTION_LENGTH(rt);
|
||||
if (length != sizeof(struct nd_opt_prefix_info))
|
||||
return -EBADMSG;
|
||||
|
||||
ri = (struct nd_opt_prefix_info*) ((uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex);
|
||||
if (ri->nd_opt_pi_prefix_len > 128)
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = ri;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
|
||||
struct nd_opt_prefix_info *ri;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_prefix_info(rt, &ri);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = be32toh(ri->nd_opt_pi_valid_time);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
|
||||
struct nd_opt_prefix_info *pi;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_prefix_info(rt, &pi);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = be32toh(pi->nd_opt_pi_preferred_time);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret) {
|
||||
struct nd_opt_prefix_info *pi;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_prefix_info(rt, &pi);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = pi->nd_opt_pi_flags_reserved;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) {
|
||||
struct nd_opt_prefix_info *pi;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret_addr, -EINVAL);
|
||||
|
||||
r = get_prefix_info(rt, &pi);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret_addr = pi->nd_opt_pi_prefix;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
|
||||
struct nd_opt_prefix_info *pi;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_prefix_info(rt, &pi);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (pi->nd_opt_pi_prefix_len > 128)
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = pi->nd_opt_pi_prefix_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_route_info(sd_ndisc_router *rt, uint8_t **ret) {
|
||||
uint8_t *ri;
|
||||
size_t length;
|
||||
int r;
|
||||
|
||||
assert(rt);
|
||||
assert(ret);
|
||||
|
||||
r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_ROUTE_INFORMATION);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EMEDIUMTYPE;
|
||||
|
||||
length = NDISC_ROUTER_OPTION_LENGTH(rt);
|
||||
if (length < 1*8 || length > 3*8)
|
||||
return -EBADMSG;
|
||||
|
||||
ri = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
|
||||
|
||||
if (ri[2] > 128)
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = ri;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
|
||||
uint8_t *ri;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_route_info(rt, &ri);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = be32toh(*(uint32_t*) (ri + 4));
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) {
|
||||
uint8_t *ri;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret_addr, -EINVAL);
|
||||
|
||||
r = get_route_info(rt, &ri);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
zero(*ret_addr);
|
||||
memcpy(ret_addr, ri + 8, NDISC_ROUTER_OPTION_LENGTH(rt) - 8);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
|
||||
uint8_t *ri;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_route_info(rt, &ri);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = ri[2];
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret) {
|
||||
uint8_t *ri;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_route_info(rt, &ri);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = (ri[3] >> 3) & 3;
|
||||
if (!IN_SET(*ret, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH))
|
||||
*ret = SD_NDISC_PREFERENCE_MEDIUM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_rdnss_info(sd_ndisc_router *rt, uint8_t **ret) {
|
||||
size_t length;
|
||||
int r;
|
||||
|
||||
assert(rt);
|
||||
assert(ret);
|
||||
|
||||
r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_RDNSS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EMEDIUMTYPE;
|
||||
|
||||
length = NDISC_ROUTER_OPTION_LENGTH(rt);
|
||||
if (length < 3*8 || (length % (2*8)) != 1*8)
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret) {
|
||||
uint8_t *ri;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_rdnss_info(rt, &ri);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = (const struct in6_addr*) (ri + 8);
|
||||
return (NDISC_ROUTER_OPTION_LENGTH(rt) - 8) / 16;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
|
||||
uint8_t *ri;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_rdnss_info(rt, &ri);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = be32toh(*(uint32_t*) (ri + 4));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_dnssl_info(sd_ndisc_router *rt, uint8_t **ret) {
|
||||
size_t length;
|
||||
int r;
|
||||
|
||||
assert(rt);
|
||||
assert(ret);
|
||||
|
||||
r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_DNSSL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EMEDIUMTYPE;
|
||||
|
||||
length = NDISC_ROUTER_OPTION_LENGTH(rt);
|
||||
if (length < 2*8)
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
_cleanup_free_ char *e = NULL;
|
||||
size_t allocated = 0, n = 0, left;
|
||||
uint8_t *ri, *p;
|
||||
bool first = true;
|
||||
int r;
|
||||
unsigned k = 0;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_dnssl_info(rt, &ri);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = ri + 8;
|
||||
left = NDISC_ROUTER_OPTION_LENGTH(rt) - 8;
|
||||
|
||||
for (;;) {
|
||||
if (left == 0) {
|
||||
|
||||
if (n > 0) /* Not properly NUL terminated */
|
||||
return -EBADMSG;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (*p == 0) {
|
||||
/* Found NUL termination */
|
||||
|
||||
if (n > 0) {
|
||||
_cleanup_free_ char *normalized = NULL;
|
||||
|
||||
e[n] = 0;
|
||||
r = dns_name_normalize(e, &normalized);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Ignore the root domain name or "localhost" and friends */
|
||||
if (!is_localhost(normalized) &&
|
||||
!dns_name_is_root(normalized)) {
|
||||
|
||||
if (strv_push(&l, normalized) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
normalized = NULL;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
n = 0;
|
||||
first = true;
|
||||
p++, left--;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check for compression (which is not allowed) */
|
||||
if (*p > 63)
|
||||
return -EBADMSG;
|
||||
|
||||
if (1U + *p + 1U > left)
|
||||
return -EBADMSG;
|
||||
|
||||
if (!GREEDY_REALLOC(e, allocated, n + !first + DNS_LABEL_ESCAPED_MAX + 1U))
|
||||
return -ENOMEM;
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
e[n++] = '.';
|
||||
|
||||
r = dns_label_escape((char*) p+1, *p, e + n, DNS_LABEL_ESCAPED_MAX);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
n += r;
|
||||
|
||||
left -= 1 + *p;
|
||||
p += 1 + *p;
|
||||
}
|
||||
|
||||
if (strv_isempty(l)) {
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*ret = l;
|
||||
l = NULL;
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
_public_ int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret_sec) {
|
||||
uint8_t *ri;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret_sec, -EINVAL);
|
||||
|
||||
r = get_dnssl_info(rt, &ri);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret_sec = be32toh(*(uint32_t*) (ri + 4));
|
||||
return 0;
|
||||
}
|
62
src/libsystemd-network/ndisc-router.h
Normal file
62
src/libsystemd-network/ndisc-router.h
Normal file
@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
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 "sd-ndisc.h"
|
||||
|
||||
#include "time-util.h"
|
||||
|
||||
struct sd_ndisc_router {
|
||||
unsigned n_ref;
|
||||
|
||||
triple_timestamp timestamp;
|
||||
struct in6_addr address;
|
||||
|
||||
/* The raw packet size. The data is appended to the object, accessible via NDIS_ROUTER_RAW() */
|
||||
size_t raw_size;
|
||||
|
||||
/* The current read index for the iterative option interface */
|
||||
size_t rindex;
|
||||
|
||||
uint64_t flags;
|
||||
unsigned preference;
|
||||
uint16_t lifetime;
|
||||
|
||||
uint8_t hop_limit;
|
||||
uint32_t mtu;
|
||||
};
|
||||
|
||||
static inline void* NDISC_ROUTER_RAW(const sd_ndisc_router *rt) {
|
||||
return (uint8_t*) rt + ALIGN(sizeof(sd_ndisc_router));
|
||||
}
|
||||
|
||||
static inline void *NDISC_ROUTER_OPTION_DATA(const sd_ndisc_router *rt) {
|
||||
return ((uint8_t*) NDISC_ROUTER_RAW(rt)) + rt->rindex;
|
||||
}
|
||||
|
||||
static inline uint8_t NDISC_ROUTER_OPTION_TYPE(const sd_ndisc_router *rt) {
|
||||
return ((uint8_t*) NDISC_ROUTER_OPTION_DATA(rt))[0];
|
||||
}
|
||||
static inline size_t NDISC_ROUTER_OPTION_LENGTH(const sd_ndisc_router *rt) {
|
||||
return ((uint8_t*) NDISC_ROUTER_OPTION_DATA(rt))[1] * 8;
|
||||
}
|
||||
|
||||
sd_ndisc_router *ndisc_router_new(size_t raw_size);
|
||||
int ndisc_router_parse(sd_ndisc_router *rt);
|
@ -380,18 +380,21 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) {
|
||||
return size;
|
||||
}
|
||||
|
||||
void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses,
|
||||
size_t size) {
|
||||
void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size) {
|
||||
unsigned i;
|
||||
|
||||
assert(f);
|
||||
assert(addresses);
|
||||
assert(size);
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
fprintf(f, SD_NDISC_ADDRESS_FORMAT_STR"%s",
|
||||
SD_NDISC_ADDRESS_FORMAT_VAL(addresses[i]),
|
||||
(i < (size - 1)) ? " ": "");
|
||||
for (i = 0; i < size; i++) {
|
||||
char buffer[INET6_ADDRSTRLEN];
|
||||
|
||||
fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f);
|
||||
|
||||
if (i < size - 1)
|
||||
fputc(' ', f);
|
||||
}
|
||||
}
|
||||
|
||||
int deserialize_in6_addrs(struct in6_addr **ret, const char *string) {
|
||||
|
@ -43,7 +43,6 @@ static void lldp_flush_neighbors(sd_lldp *lldp) {
|
||||
|
||||
static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) {
|
||||
assert(lldp);
|
||||
assert(n);
|
||||
|
||||
log_lldp("Invoking callback for '%c'.", event);
|
||||
|
||||
@ -138,6 +137,7 @@ static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
|
||||
|
||||
if (lldp_neighbor_equal(n, old)) {
|
||||
/* Is this equal, then restart the TTL counter, but don't do anyting else. */
|
||||
old->timestamp = n->timestamp;
|
||||
lldp_start_timer(lldp, old);
|
||||
lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old);
|
||||
return 0;
|
||||
@ -171,7 +171,7 @@ static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
|
||||
|
||||
finish:
|
||||
if (old)
|
||||
lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n);
|
||||
lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
|
||||
|
||||
return r;
|
||||
}
|
||||
@ -202,6 +202,7 @@ static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, v
|
||||
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
|
||||
ssize_t space, length;
|
||||
sd_lldp *lldp = userdata;
|
||||
struct timespec ts;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(lldp);
|
||||
@ -215,21 +216,41 @@ static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, v
|
||||
return -ENOMEM;
|
||||
|
||||
length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
|
||||
if (length < 0)
|
||||
if (length < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
return 0;
|
||||
|
||||
return log_lldp_errno(errno, "Failed to read LLDP datagram: %m");
|
||||
}
|
||||
|
||||
if ((size_t) length != n->raw_size) {
|
||||
log_lldp("Packet size mismatch.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Try to get the timestamp of this packet if it is known */
|
||||
if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0)
|
||||
triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts));
|
||||
else
|
||||
triple_timestamp_get(&n->timestamp);
|
||||
|
||||
return lldp_handle_datagram(lldp, n);
|
||||
}
|
||||
|
||||
static void lldp_reset(sd_lldp *lldp) {
|
||||
assert(lldp);
|
||||
|
||||
lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source);
|
||||
lldp->io_event_source = sd_event_source_unref(lldp->io_event_source);
|
||||
lldp->fd = safe_close(lldp->fd);
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_start(sd_lldp *lldp) {
|
||||
int r;
|
||||
|
||||
assert_return(lldp, -EINVAL);
|
||||
assert_return(lldp->event, -EINVAL);
|
||||
assert_return(lldp->ifindex > 0, -EINVAL);
|
||||
|
||||
if (lldp->fd >= 0)
|
||||
return 0;
|
||||
@ -240,24 +261,21 @@ _public_ int sd_lldp_start(sd_lldp *lldp) {
|
||||
if (lldp->fd < 0)
|
||||
return lldp->fd;
|
||||
|
||||
if (lldp->event) {
|
||||
r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
(void) sd_event_source_set_description(lldp->io_event_source, "lldp-io");
|
||||
}
|
||||
(void) sd_event_source_set_description(lldp->io_event_source, "lldp-io");
|
||||
|
||||
log_lldp("Started LLDP client");
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
lldp->io_event_source = sd_event_source_unref(lldp->io_event_source);
|
||||
lldp->fd = safe_close(lldp->fd);
|
||||
|
||||
lldp_reset(lldp);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -267,10 +285,9 @@ _public_ int sd_lldp_stop(sd_lldp *lldp) {
|
||||
if (lldp->fd < 0)
|
||||
return 0;
|
||||
|
||||
lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source);
|
||||
lldp->io_event_source = sd_event_source_unref(lldp->io_event_source);
|
||||
lldp->fd = safe_close(lldp->fd);
|
||||
log_lldp("Stopping LLDP client");
|
||||
|
||||
lldp_reset(lldp);
|
||||
lldp_flush_neighbors(lldp);
|
||||
|
||||
return 1;
|
||||
@ -305,6 +322,12 @@ _public_ int sd_lldp_detach_event(sd_lldp *lldp) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ sd_event* sd_lldp_get_event(sd_lldp *lldp) {
|
||||
assert_return(lldp, NULL);
|
||||
|
||||
return lldp->event;
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) {
|
||||
assert_return(lldp, -EINVAL);
|
||||
|
||||
@ -314,39 +337,60 @@ _public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *us
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) {
|
||||
assert_return(lldp, -EINVAL);
|
||||
assert_return(ifindex > 0, -EINVAL);
|
||||
assert_return(lldp->fd < 0, -EBUSY);
|
||||
|
||||
lldp->ifindex = ifindex;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ sd_lldp* sd_lldp_ref(sd_lldp *lldp) {
|
||||
|
||||
if (!lldp)
|
||||
return NULL;
|
||||
|
||||
assert(lldp->n_ref > 0);
|
||||
lldp->n_ref++;
|
||||
|
||||
return lldp;
|
||||
}
|
||||
|
||||
_public_ sd_lldp* sd_lldp_unref(sd_lldp *lldp) {
|
||||
|
||||
if (!lldp)
|
||||
return NULL;
|
||||
|
||||
assert(lldp->n_ref > 0);
|
||||
lldp->n_ref --;
|
||||
|
||||
if (lldp->n_ref > 0)
|
||||
return NULL;
|
||||
|
||||
lldp_reset(lldp);
|
||||
sd_lldp_detach_event(lldp);
|
||||
lldp_flush_neighbors(lldp);
|
||||
|
||||
hashmap_free(lldp->neighbor_by_id);
|
||||
prioq_free(lldp->neighbor_by_expiry);
|
||||
|
||||
sd_event_source_unref(lldp->io_event_source);
|
||||
sd_event_source_unref(lldp->timer_event_source);
|
||||
sd_event_unref(lldp->event);
|
||||
safe_close(lldp->fd);
|
||||
|
||||
free(lldp);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_new(sd_lldp **ret, int ifindex) {
|
||||
_public_ int sd_lldp_new(sd_lldp **ret) {
|
||||
_cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
|
||||
int r;
|
||||
|
||||
assert_return(ret, -EINVAL);
|
||||
assert_return(ifindex > 0, -EINVAL);
|
||||
|
||||
lldp = new0(sd_lldp, 1);
|
||||
if (!lldp)
|
||||
return -ENOMEM;
|
||||
|
||||
lldp->n_ref = 1;
|
||||
lldp->fd = -1;
|
||||
lldp->ifindex = ifindex;
|
||||
lldp->neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX;
|
||||
lldp->capability_mask = (uint16_t) -1;
|
||||
|
||||
@ -486,11 +530,10 @@ _public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *
|
||||
/* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
|
||||
* that our own can be filtered out here. */
|
||||
|
||||
if (!addr) {
|
||||
if (addr)
|
||||
lldp->filter_address = *addr;
|
||||
else
|
||||
zero(lldp->filter_address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
lldp->filter_address = *addr;
|
||||
return 0;
|
||||
}
|
||||
|
@ -19,165 +19,71 @@
|
||||
|
||||
#include <netinet/icmp6.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip6.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "sd-ndisc.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "async.h"
|
||||
#include "fd-util.h"
|
||||
#include "icmp6-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "list.h"
|
||||
#include "ndisc-internal.h"
|
||||
#include "ndisc-router.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-util.h"
|
||||
#include "util.h"
|
||||
|
||||
#define NDISC_ROUTER_SOLICITATION_INTERVAL (4U * USEC_PER_SEC)
|
||||
#define NDISC_MAX_ROUTER_SOLICITATIONS 3U
|
||||
#define NDISC_MAX_ROUTER_SOLICITATIONS 3U
|
||||
|
||||
enum NDiscState {
|
||||
NDISC_STATE_IDLE,
|
||||
NDISC_STATE_SOLICITATION_SENT,
|
||||
NDISC_STATE_ADVERTISEMENT_LISTEN,
|
||||
_NDISC_STATE_MAX,
|
||||
_NDISC_STATE_INVALID = -1,
|
||||
};
|
||||
static void ndisc_callback(sd_ndisc *ndisc, sd_ndisc_event event, sd_ndisc_router *rt) {
|
||||
assert(ndisc);
|
||||
|
||||
#define IP6_MIN_MTU 1280U
|
||||
#define ICMP6_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr))
|
||||
#define NDISC_OPT_LEN_UNITS 8U
|
||||
log_ndisc("Invoking callback for '%c'.", event);
|
||||
|
||||
#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
|
||||
if (!ndisc->callback)
|
||||
return;
|
||||
|
||||
typedef struct NDiscPrefix NDiscPrefix;
|
||||
|
||||
struct NDiscPrefix {
|
||||
unsigned n_ref;
|
||||
|
||||
sd_ndisc *nd;
|
||||
|
||||
LIST_FIELDS(NDiscPrefix, prefixes);
|
||||
|
||||
uint8_t len;
|
||||
usec_t valid_until;
|
||||
struct in6_addr addr;
|
||||
};
|
||||
|
||||
struct sd_ndisc {
|
||||
unsigned n_ref;
|
||||
|
||||
enum NDiscState state;
|
||||
int ifindex;
|
||||
int fd;
|
||||
|
||||
sd_event *event;
|
||||
int event_priority;
|
||||
|
||||
struct ether_addr mac_addr;
|
||||
uint32_t mtu;
|
||||
|
||||
LIST_HEAD(NDiscPrefix, prefixes);
|
||||
|
||||
sd_event_source *recv_event_source;
|
||||
sd_event_source *timeout_event_source;
|
||||
|
||||
unsigned nd_sent;
|
||||
|
||||
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;
|
||||
sd_ndisc_callback_t callback;
|
||||
void *userdata;
|
||||
};
|
||||
|
||||
#define log_ndisc_errno(p, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "NDisc CLIENT: " fmt, ##__VA_ARGS__)
|
||||
#define log_ndisc(p, fmt, ...) log_ndisc_errno(p, 0, fmt, ##__VA_ARGS__)
|
||||
|
||||
static NDiscPrefix *ndisc_prefix_unref(NDiscPrefix *prefix) {
|
||||
|
||||
if (!prefix)
|
||||
return NULL;
|
||||
|
||||
assert(prefix->n_ref > 0);
|
||||
prefix->n_ref--;
|
||||
|
||||
if (prefix->n_ref > 0)
|
||||
return NULL;
|
||||
|
||||
if (prefix->nd)
|
||||
LIST_REMOVE(prefixes, prefix->nd->prefixes, prefix);
|
||||
|
||||
free(prefix);
|
||||
|
||||
return NULL;
|
||||
ndisc->callback(ndisc, event, rt, ndisc->userdata);
|
||||
}
|
||||
|
||||
static int ndisc_prefix_new(sd_ndisc *nd, NDiscPrefix **ret) {
|
||||
NDiscPrefix *prefix;
|
||||
|
||||
assert(ret);
|
||||
|
||||
prefix = new0(NDiscPrefix, 1);
|
||||
if (!prefix)
|
||||
return -ENOMEM;
|
||||
|
||||
prefix->n_ref = 1;
|
||||
LIST_INIT(prefixes, prefix);
|
||||
prefix->nd = nd;
|
||||
|
||||
*ret = prefix;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_set_callback(
|
||||
_public_ 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) {
|
||||
|
||||
assert_return(nd, -EINVAL);
|
||||
|
||||
nd->router_callback = router_callback;
|
||||
nd->prefix_onlink_callback = prefix_onlink_callback;
|
||||
nd->prefix_autonomous_callback = prefix_autonomous_callback;
|
||||
nd->callback = callback;
|
||||
nd->userdata = userdata;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_set_ifindex(sd_ndisc *nd, int ifindex) {
|
||||
_public_ int sd_ndisc_set_ifindex(sd_ndisc *nd, int ifindex) {
|
||||
assert_return(nd, -EINVAL);
|
||||
assert_return(ifindex > 0, -EINVAL);
|
||||
assert_return(nd->fd < 0, -EBUSY);
|
||||
|
||||
nd->ifindex = ifindex;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr) {
|
||||
_public_ int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr) {
|
||||
assert_return(nd, -EINVAL);
|
||||
|
||||
if (mac_addr)
|
||||
memcpy(&nd->mac_addr, mac_addr, sizeof(nd->mac_addr));
|
||||
nd->mac_addr = *mac_addr;
|
||||
else
|
||||
zero(nd->mac_addr);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority) {
|
||||
_public_ int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority) {
|
||||
int r;
|
||||
|
||||
assert_return(nd, -EINVAL);
|
||||
assert_return(nd->fd < 0, -EBUSY);
|
||||
assert_return(!nd->event, -EBUSY);
|
||||
|
||||
if (event)
|
||||
@ -193,21 +99,22 @@ int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_detach_event(sd_ndisc *nd) {
|
||||
_public_ int sd_ndisc_detach_event(sd_ndisc *nd) {
|
||||
|
||||
assert_return(nd, -EINVAL);
|
||||
assert_return(nd->fd < 0, -EBUSY);
|
||||
|
||||
nd->event = sd_event_unref(nd->event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sd_event *sd_ndisc_get_event(sd_ndisc *nd) {
|
||||
_public_ sd_event *sd_ndisc_get_event(sd_ndisc *nd) {
|
||||
assert_return(nd, NULL);
|
||||
|
||||
return nd->event;
|
||||
}
|
||||
|
||||
sd_ndisc *sd_ndisc_ref(sd_ndisc *nd) {
|
||||
_public_ sd_ndisc *sd_ndisc_ref(sd_ndisc *nd) {
|
||||
|
||||
if (!nd)
|
||||
return NULL;
|
||||
@ -221,15 +128,14 @@ sd_ndisc *sd_ndisc_ref(sd_ndisc *nd) {
|
||||
static int ndisc_reset(sd_ndisc *nd) {
|
||||
assert(nd);
|
||||
|
||||
nd->recv_event_source = sd_event_source_unref(nd->recv_event_source);
|
||||
nd->fd = asynchronous_close(nd->fd);
|
||||
nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
|
||||
nd->recv_event_source = sd_event_source_unref(nd->recv_event_source);
|
||||
nd->fd = safe_close(nd->fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sd_ndisc *sd_ndisc_unref(sd_ndisc *nd) {
|
||||
NDiscPrefix *prefix, *p;
|
||||
_public_ sd_ndisc *sd_ndisc_unref(sd_ndisc *nd) {
|
||||
|
||||
if (!nd)
|
||||
return NULL;
|
||||
@ -242,16 +148,12 @@ sd_ndisc *sd_ndisc_unref(sd_ndisc *nd) {
|
||||
|
||||
ndisc_reset(nd);
|
||||
sd_ndisc_detach_event(nd);
|
||||
|
||||
LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes)
|
||||
prefix = ndisc_prefix_unref(prefix);
|
||||
|
||||
free(nd);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int sd_ndisc_new(sd_ndisc **ret) {
|
||||
_public_ int sd_ndisc_new(sd_ndisc **ret) {
|
||||
_cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
|
||||
|
||||
assert_return(ret, -EINVAL);
|
||||
@ -261,223 +163,70 @@ int sd_ndisc_new(sd_ndisc **ret) {
|
||||
return -ENOMEM;
|
||||
|
||||
nd->n_ref = 1;
|
||||
nd->ifindex = -1;
|
||||
nd->fd = -1;
|
||||
|
||||
LIST_HEAD_INIT(nd->prefixes);
|
||||
|
||||
*ret = nd;
|
||||
nd = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu) {
|
||||
_public_ int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu) {
|
||||
assert_return(nd, -EINVAL);
|
||||
assert_return(mtu, -EINVAL);
|
||||
|
||||
if (nd->mtu == 0)
|
||||
return -ENOMSG;
|
||||
return -ENODATA;
|
||||
|
||||
*mtu = nd->mtu;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prefix_match(const struct in6_addr *prefix, uint8_t prefixlen,
|
||||
const struct in6_addr *addr,
|
||||
uint8_t addr_prefixlen) {
|
||||
uint8_t bytes, mask, len;
|
||||
_public_ int sd_ndisc_get_hop_limit(sd_ndisc *nd, uint8_t *ret) {
|
||||
assert_return(nd, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
assert(prefix);
|
||||
assert(addr);
|
||||
|
||||
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;
|
||||
if (nd->hop_limit == 0)
|
||||
return -ENODATA;
|
||||
|
||||
*ret = nd->hop_limit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
static int ndisc_handle_datagram(sd_ndisc *nd, sd_ndisc_router *rt) {
|
||||
int r;
|
||||
|
||||
assert(nd);
|
||||
assert(rt);
|
||||
|
||||
r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
|
||||
r = ndisc_router_parse(rt);
|
||||
if (r == -EBADMSG) /* Bad packet */
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
|
||||
if (prefix_match(&prefix->addr, prefix->len, addr, addr_len) >= 0) {
|
||||
*result = prefix;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len,
|
||||
const struct nd_opt_prefix_info *prefix_opt) {
|
||||
NDiscPrefix *prefix;
|
||||
uint32_t lifetime_valid, lifetime_preferred;
|
||||
usec_t time_now;
|
||||
char time_string[FORMAT_TIMESPAN_MAX];
|
||||
int r;
|
||||
|
||||
assert(nd);
|
||||
assert(prefix_opt);
|
||||
|
||||
if (len < prefix_opt->nd_opt_pi_len)
|
||||
return -EBADMSG;
|
||||
|
||||
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)
|
||||
return 0;
|
||||
/* Update global variables we keep */
|
||||
if (rt->mtu > 0)
|
||||
nd->mtu = rt->mtu;
|
||||
if (rt->hop_limit > 0)
|
||||
nd->hop_limit = rt->hop_limit;
|
||||
|
||||
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;
|
||||
|
||||
r = ndisc_prefix_match(nd, &prefix_opt->nd_opt_pi_prefix,
|
||||
prefix_opt->nd_opt_pi_prefix_len, &prefix);
|
||||
if (r < 0) {
|
||||
if (r != -EADDRNOTAVAIL)
|
||||
return r;
|
||||
|
||||
/* if router advertisement prefix valid timeout is zero, the timeout
|
||||
callback will be called immediately to clean up the prefix */
|
||||
|
||||
r = ndisc_prefix_new(nd, &prefix);
|
||||
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));
|
||||
|
||||
log_ndisc(nd, "New prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
|
||||
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));
|
||||
|
||||
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);
|
||||
|
||||
log_ndisc(nd, "Prefix length mismatch %d/%d using %d",
|
||||
prefix->len,
|
||||
prefix_opt->nd_opt_pi_prefix_len,
|
||||
prefixlen);
|
||||
|
||||
prefix->len = prefixlen;
|
||||
}
|
||||
|
||||
log_ndisc(nd, "Update prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
|
||||
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));
|
||||
}
|
||||
|
||||
r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
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);
|
||||
log_ndisc("Received Router Advertisement: flags %s preference %s lifetime %" PRIu16 " sec",
|
||||
rt->flags & ND_RA_FLAG_MANAGED ? "MANAGED" : rt->flags & ND_RA_FLAG_OTHER ? "OTHER" : "none",
|
||||
rt->preference == SD_NDISC_PREFERENCE_HIGH ? "high" : rt->preference == SD_NDISC_PREFERENCE_LOW ? "low" : "medium",
|
||||
rt->lifetime);
|
||||
|
||||
ndisc_callback(nd, SD_NDISC_EVENT_ROUTER, rt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra, size_t len) {
|
||||
struct nd_opt_hdr *opt_hdr;
|
||||
void *opt;
|
||||
|
||||
assert(nd);
|
||||
assert(ra);
|
||||
|
||||
if (len < sizeof(struct nd_router_advert) + NDISC_OPT_LEN_UNITS) {
|
||||
log_ndisc(nd, "Router Advertisement below minimum length");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
len -= sizeof(struct nd_router_advert);
|
||||
opt = ra + 1;
|
||||
opt_hdr = opt;
|
||||
|
||||
while (len != 0 && len >= opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS) {
|
||||
struct nd_opt_mtu *opt_mtu;
|
||||
struct nd_opt_prefix_info *opt_prefix;
|
||||
uint32_t mtu;
|
||||
|
||||
if (opt_hdr->nd_opt_len == 0)
|
||||
return -EBADMSG;
|
||||
|
||||
switch (opt_hdr->nd_opt_type) {
|
||||
|
||||
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);
|
||||
log_ndisc(nd, "Router Advertisement link MTU %d using %d", mtu, nd->mtu);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ND_OPT_PREFIX_INFORMATION:
|
||||
opt_prefix = opt;
|
||||
ndisc_prefix_update(nd, len, opt_prefix);
|
||||
break;
|
||||
}
|
||||
|
||||
len -= opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS;
|
||||
opt = (void*) ((uint8_t*) opt + opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS);
|
||||
opt_hdr = opt;
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
log_ndisc(nd, "Router Advertisement contains %zd bytes of trailing garbage", len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ndisc_router_advertisement_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
_cleanup_free_ struct nd_router_advert *ra = NULL;
|
||||
static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
_cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
|
||||
sd_ndisc *nd = userdata;
|
||||
union {
|
||||
struct cmsghdr cmsghdr;
|
||||
uint8_t buf[CMSG_LEN(sizeof(int))];
|
||||
uint8_t buf[CMSG_SPACE(sizeof(int)) + /* ttl */
|
||||
CMSG_SPACE(sizeof(struct timeval))];
|
||||
} control = {};
|
||||
struct iovec iov = {};
|
||||
union sockaddr_union sa = {};
|
||||
@ -490,10 +239,7 @@ static int ndisc_router_advertisement_recv(sd_event_source *s, int fd, uint32_t
|
||||
.msg_controllen = sizeof(control),
|
||||
};
|
||||
struct cmsghdr *cmsg;
|
||||
struct in6_addr *gw;
|
||||
unsigned lifetime;
|
||||
ssize_t len, buflen;
|
||||
int r, pref, stateful;
|
||||
|
||||
assert(s);
|
||||
assert(nd);
|
||||
@ -501,35 +247,47 @@ static int ndisc_router_advertisement_recv(sd_event_source *s, int fd, uint32_t
|
||||
|
||||
buflen = next_datagram_size_fd(fd);
|
||||
if (buflen < 0)
|
||||
return buflen;
|
||||
return log_ndisc_errno(buflen, "Failed to determine datagram size to read: %m");
|
||||
|
||||
iov.iov_len = buflen;
|
||||
|
||||
ra = malloc(iov.iov_len);
|
||||
if (!ra)
|
||||
rt = ndisc_router_new(buflen);
|
||||
if (!rt)
|
||||
return -ENOMEM;
|
||||
|
||||
iov.iov_base = ra;
|
||||
iov.iov_base = NDISC_ROUTER_RAW(rt);
|
||||
iov.iov_len = rt->raw_size;
|
||||
|
||||
len = recvmsg(fd, &msg, 0);
|
||||
len = recvmsg(fd, &msg, MSG_DONTWAIT);
|
||||
if (len < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
return 0;
|
||||
|
||||
return log_ndisc_errno(nd, errno, "Could not receive message from ICMPv6 socket: %m");
|
||||
}
|
||||
if ((size_t) len < sizeof(struct nd_router_advert)) {
|
||||
log_ndisc(nd, "Too small to be a router advertisement: ignoring");
|
||||
return 0;
|
||||
return log_ndisc_errno(errno, "Could not receive message from ICMPv6 socket: %m");
|
||||
}
|
||||
|
||||
if (msg.msg_namelen == 0)
|
||||
gw = NULL; /* only happens when running the test-suite over a socketpair */
|
||||
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);
|
||||
return 0;
|
||||
} else
|
||||
gw = &sa.in6.sin6_addr;
|
||||
if ((size_t) len != rt->raw_size) {
|
||||
log_ndisc("Packet size mismatch.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (msg.msg_namelen == sizeof(struct sockaddr_in6) &&
|
||||
sa.in6.sin6_family == AF_INET6) {
|
||||
|
||||
if (in_addr_is_link_local(AF_INET6, (union in_addr_union*) &sa.in6.sin6_addr) <= 0) {
|
||||
_cleanup_free_ char *addr = NULL;
|
||||
|
||||
(void) in_addr_to_string(AF_INET6, (union in_addr_union*) &sa.in6.sin6_addr, &addr);
|
||||
log_ndisc("Received RA from non-link-local address %s. Ignoring.", strna(addr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
rt->address = sa.in6.sin6_addr;
|
||||
|
||||
} else if (msg.msg_namelen > 0) {
|
||||
log_ndisc("Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t) msg.msg_namelen);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* namelen == 0 only happens when running the test-suite over a socketpair */
|
||||
|
||||
assert(!(msg.msg_flags & MSG_CTRUNC));
|
||||
assert(!(msg.msg_flags & MSG_TRUNC));
|
||||
@ -538,61 +296,29 @@ static int ndisc_router_advertisement_recv(sd_event_source *s, int fd, uint32_t
|
||||
if (cmsg->cmsg_level == SOL_IPV6 &&
|
||||
cmsg->cmsg_type == IPV6_HOPLIMIT &&
|
||||
cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
|
||||
int hops = *(int*)CMSG_DATA(cmsg);
|
||||
int hops = *(int*) CMSG_DATA(cmsg);
|
||||
|
||||
if (hops != 255) {
|
||||
log_ndisc(nd, "Received RA with invalid hop limit %d. Ignoring.", hops);
|
||||
log_ndisc("Received RA with invalid hop limit %d. Ignoring.", hops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SO_TIMESTAMP &&
|
||||
cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
|
||||
triple_timestamp_from_realtime(&rt->timestamp, timeval_load(CMSG_DATA(cmsg)));
|
||||
}
|
||||
|
||||
if (gw && !in_addr_is_link_local(AF_INET6, (const union in_addr_union*) gw)) {
|
||||
_cleanup_free_ char *addr = NULL;
|
||||
|
||||
(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));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ra->nd_ra_type != ND_ROUTER_ADVERT)
|
||||
return 0;
|
||||
|
||||
if (ra->nd_ra_code != 0)
|
||||
return 0;
|
||||
if (!triple_timestamp_is_set(&rt->timestamp))
|
||||
triple_timestamp_get(&rt->timestamp);
|
||||
|
||||
nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
|
||||
nd->state = NDISC_STATE_ADVERTISEMENT_LISTEN;
|
||||
|
||||
stateful = ra->nd_ra_flags_reserved & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER);
|
||||
pref = (ra->nd_ra_flags_reserved & ND_RA_FLAG_PREF) >> 3;
|
||||
|
||||
if (!IN_SET(pref, ND_RA_FLAG_PREF_LOW, ND_RA_FLAG_PREF_HIGH))
|
||||
pref = ND_RA_FLAG_PREF_MEDIUM;
|
||||
|
||||
lifetime = be16toh(ra->nd_ra_router_lifetime);
|
||||
|
||||
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",
|
||||
pref == ND_RA_FLAG_PREF_HIGH ? "high" : pref == ND_RA_FLAG_PREF_LOW ? "low" : "medium",
|
||||
lifetime);
|
||||
|
||||
r = ndisc_ra_parse(nd, ra, (size_t) len);
|
||||
if (r < 0) {
|
||||
log_ndisc_errno(nd, r, "Could not parse Router Advertisement: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (nd->router_callback)
|
||||
nd->router_callback(nd, stateful, gw, lifetime, pref, nd->userdata);
|
||||
|
||||
return 0;
|
||||
return ndisc_handle_datagram(nd, rt);
|
||||
}
|
||||
|
||||
static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
sd_ndisc *nd = userdata;
|
||||
usec_t time_now, next_timeout;
|
||||
int r;
|
||||
@ -601,43 +327,34 @@ static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec,
|
||||
assert(nd);
|
||||
assert(nd->event);
|
||||
|
||||
nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
|
||||
|
||||
if (nd->nd_sent >= NDISC_MAX_ROUTER_SOLICITATIONS) {
|
||||
if (nd->callback)
|
||||
nd->callback(nd, SD_NDISC_EVENT_TIMEOUT, nd->userdata);
|
||||
nd->state = NDISC_STATE_ADVERTISEMENT_LISTEN;
|
||||
} else {
|
||||
r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr);
|
||||
if (r < 0) {
|
||||
log_ndisc_errno(nd, r, "Error sending Router Solicitation: %m");
|
||||
goto fail;
|
||||
} else {
|
||||
nd->state = NDISC_STATE_SOLICITATION_SENT;
|
||||
log_ndisc(nd, "Sent Router Solicitation");
|
||||
}
|
||||
nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
|
||||
ndisc_callback(nd, SD_NDISC_EVENT_TIMEOUT, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
nd->nd_sent++;
|
||||
r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr);
|
||||
if (r < 0) {
|
||||
log_ndisc_errno(r, "Error sending Router Solicitation: %m");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
|
||||
log_ndisc("Sent Router Solicitation");
|
||||
nd->nd_sent++;
|
||||
|
||||
next_timeout = time_now + NDISC_ROUTER_SOLICITATION_INTERVAL;
|
||||
assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
|
||||
next_timeout = time_now + NDISC_ROUTER_SOLICITATION_INTERVAL;
|
||||
|
||||
r = sd_event_add_time(nd->event, &nd->timeout_event_source, clock_boottime_or_monotonic(),
|
||||
next_timeout, 0,
|
||||
ndisc_router_solicitation_timeout, nd);
|
||||
if (r < 0) {
|
||||
log_ndisc_errno(nd, r, "Failed to allocate timer event: %m");
|
||||
goto fail;
|
||||
}
|
||||
r = sd_event_source_set_time(nd->timeout_event_source, next_timeout);
|
||||
if (r < 0) {
|
||||
log_ndisc_errno(r, "Error updating timer: %m");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = sd_event_source_set_priority(nd->timeout_event_source, nd->event_priority);
|
||||
if (r < 0) {
|
||||
log_ndisc_errno(nd, r, "Cannot set timer priority: %m");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
(void) sd_event_source_set_description(nd->timeout_event_source, "ndisc-timeout");
|
||||
r = sd_event_source_set_enabled(nd->timeout_event_source, SD_EVENT_ONESHOT);
|
||||
if (r < 0) {
|
||||
log_ndisc_errno(r, "Error reenabling timer: %m");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -647,38 +364,36 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_stop(sd_ndisc *nd) {
|
||||
_public_ int sd_ndisc_stop(sd_ndisc *nd) {
|
||||
assert_return(nd, -EINVAL);
|
||||
|
||||
if (nd->state == NDISC_STATE_IDLE)
|
||||
if (nd->fd < 0)
|
||||
return 0;
|
||||
|
||||
log_ndisc(nd, "Stopping IPv6 Router Solicitation client");
|
||||
log_ndisc("Stopping IPv6 Router Solicitation client");
|
||||
|
||||
ndisc_reset(nd);
|
||||
nd->state = NDISC_STATE_IDLE;
|
||||
|
||||
if (nd->callback)
|
||||
nd->callback(nd, SD_NDISC_EVENT_STOP, nd->userdata);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_discovery_start(sd_ndisc *nd) {
|
||||
_public_ int sd_ndisc_start(sd_ndisc *nd) {
|
||||
int r;
|
||||
|
||||
assert_return(nd, -EINVAL);
|
||||
assert_return(nd->event, -EINVAL);
|
||||
assert_return(nd->ifindex > 0, -EINVAL);
|
||||
assert_return(nd->state == NDISC_STATE_IDLE, -EBUSY);
|
||||
|
||||
r = icmp6_bind_router_solicitation(nd->ifindex);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (nd->fd >= 0)
|
||||
return 0;
|
||||
|
||||
nd->fd = r;
|
||||
assert(!nd->recv_event_source);
|
||||
assert(!nd->timeout_event_source);
|
||||
|
||||
r = sd_event_add_io(nd->event, &nd->recv_event_source, nd->fd, EPOLLIN, ndisc_router_advertisement_recv, nd);
|
||||
nd->fd = icmp6_bind_router_solicitation(nd->ifindex);
|
||||
if (nd->fd < 0)
|
||||
return nd->fd;
|
||||
|
||||
r = sd_event_add_io(nd->event, &nd->recv_event_source, nd->fd, EPOLLIN, ndisc_recv, nd);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
@ -688,7 +403,7 @@ int sd_ndisc_router_discovery_start(sd_ndisc *nd) {
|
||||
|
||||
(void) sd_event_source_set_description(nd->recv_event_source, "ndisc-receive-message");
|
||||
|
||||
r = sd_event_add_time(nd->event, &nd->timeout_event_source, clock_boottime_or_monotonic(), 0, 0, ndisc_router_solicitation_timeout, nd);
|
||||
r = sd_event_add_time(nd->event, &nd->timeout_event_source, clock_boottime_or_monotonic(), 0, 0, ndisc_timeout, nd);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
@ -698,8 +413,8 @@ int sd_ndisc_router_discovery_start(sd_ndisc *nd) {
|
||||
|
||||
(void) sd_event_source_set_description(nd->timeout_event_source, "ndisc-timeout");
|
||||
|
||||
log_ndisc(ns, "Started IPv6 Router Solicitation client");
|
||||
return 0;
|
||||
log_ndisc("Started IPv6 Router Solicitation client");
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
ndisc_reset(nd);
|
||||
|
@ -54,11 +54,11 @@ static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n
|
||||
static int start_lldp(sd_lldp **lldp, sd_event *e, sd_lldp_callback_t cb, void *cb_data) {
|
||||
int r;
|
||||
|
||||
r = sd_lldp_new(lldp, 42);
|
||||
r = sd_lldp_new(lldp);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_lldp_attach_event(*lldp, e, 0);
|
||||
r = sd_lldp_set_ifindex(*lldp, 42);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -66,6 +66,10 @@ static int start_lldp(sd_lldp **lldp, sd_event *e, sd_lldp_callback_t cb, void *
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_lldp_attach_event(*lldp, e, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_lldp_start(*lldp);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -18,11 +18,15 @@
|
||||
***/
|
||||
|
||||
#include <netinet/icmp6.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "sd-ndisc.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "icmp6-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
static struct ether_addr mac_addr = {
|
||||
.ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
|
||||
@ -35,6 +39,144 @@ static int test_fd[2];
|
||||
typedef int (*send_ra_t)(uint8_t flags);
|
||||
static send_ra_t send_ra_function;
|
||||
|
||||
static void router_dump(sd_ndisc_router *rt) {
|
||||
struct in6_addr addr;
|
||||
char buf[FORMAT_TIMESTAMP_MAX];
|
||||
uint8_t hop_limit;
|
||||
uint64_t t, flags;
|
||||
uint32_t mtu;
|
||||
uint16_t lifetime;
|
||||
unsigned preference;
|
||||
int r;
|
||||
|
||||
assert_se(rt);
|
||||
|
||||
log_info("--");
|
||||
assert_se(sd_ndisc_router_get_address(rt, &addr) == -ENODATA);
|
||||
|
||||
assert_se(sd_ndisc_router_get_timestamp(rt, CLOCK_REALTIME, &t) >= 0);
|
||||
log_info("Timestamp: %s", format_timestamp(buf, sizeof(buf), t));
|
||||
|
||||
assert_se(sd_ndisc_router_get_timestamp(rt, CLOCK_MONOTONIC, &t) >= 0);
|
||||
log_info("Monotonic: %" PRIu64, t);
|
||||
|
||||
if (sd_ndisc_router_get_hop_limit(rt, &hop_limit) < 0)
|
||||
log_info("No hop limit set");
|
||||
else
|
||||
log_info("Hop limit: %u", hop_limit);
|
||||
|
||||
assert_se(sd_ndisc_router_get_flags(rt, &flags) >= 0);
|
||||
log_info("Flags: <%s|%s>",
|
||||
flags & ND_RA_FLAG_OTHER ? "OTHER" : "",
|
||||
flags & ND_RA_FLAG_MANAGED ? "MANAGED" : "");
|
||||
|
||||
assert_se(sd_ndisc_router_get_preference(rt, &preference) >= 0);
|
||||
log_info("Preference: %s",
|
||||
preference == SD_NDISC_PREFERENCE_LOW ? "low" :
|
||||
preference == SD_NDISC_PREFERENCE_HIGH ? "high" : "medium");
|
||||
|
||||
assert_se(sd_ndisc_router_get_lifetime(rt, &lifetime) >= 0);
|
||||
log_info("Lifetime: %" PRIu16, lifetime);
|
||||
|
||||
if (sd_ndisc_router_get_mtu(rt, &mtu) < 0)
|
||||
log_info("No MTU set");
|
||||
else
|
||||
log_info("MTU: %" PRIu32, mtu);
|
||||
|
||||
r = sd_ndisc_router_option_rewind(rt);
|
||||
for (;;) {
|
||||
uint8_t type;
|
||||
|
||||
assert_se(r >= 0);
|
||||
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
assert_se(sd_ndisc_router_option_get_type(rt, &type) >= 0);
|
||||
|
||||
log_info(">> Option %u", type);
|
||||
|
||||
switch (type) {
|
||||
|
||||
case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
|
||||
case SD_NDISC_OPTION_TARGET_LL_ADDRESS: {
|
||||
_cleanup_free_ char *c = NULL;
|
||||
const void *p;
|
||||
size_t n;
|
||||
|
||||
assert_se(sd_ndisc_router_option_get_raw(rt, &p, &n) >= 0);
|
||||
assert_se(n > 2);
|
||||
assert_se(c = hexmem((uint8_t*) p + 2, n - 2));
|
||||
|
||||
log_info("Address: %s", c);
|
||||
break;
|
||||
}
|
||||
|
||||
case SD_NDISC_OPTION_PREFIX_INFORMATION: {
|
||||
uint32_t lifetime_valid, lifetime_preferred;
|
||||
unsigned prefix_len;
|
||||
uint8_t pfl;
|
||||
struct in6_addr a;
|
||||
char buff[INET6_ADDRSTRLEN];
|
||||
|
||||
assert_se(sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid) >= 0);
|
||||
log_info("Valid Lifetime: %" PRIu32, lifetime_valid);
|
||||
|
||||
assert_se(sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred) >= 0);
|
||||
log_info("Preferred Lifetime: %" PRIu32, lifetime_preferred);
|
||||
|
||||
assert_se(sd_ndisc_router_prefix_get_flags(rt, &pfl) >= 0);
|
||||
log_info("Flags: <%s|%s>",
|
||||
pfl & ND_OPT_PI_FLAG_ONLINK ? "ONLINK" : "",
|
||||
pfl & ND_OPT_PI_FLAG_AUTO ? "AUTO" : "");
|
||||
|
||||
assert_se(sd_ndisc_router_prefix_get_prefixlen(rt, &prefix_len) >= 0);
|
||||
log_info("Prefix Length: %u", prefix_len);
|
||||
|
||||
assert_se(sd_ndisc_router_prefix_get_address(rt, &a) >= 0);
|
||||
log_info("Prefix: %s", inet_ntop(AF_INET6, &a, buff, sizeof(buff)));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SD_NDISC_OPTION_RDNSS: {
|
||||
const struct in6_addr *a;
|
||||
uint32_t lt;
|
||||
int n, i;
|
||||
|
||||
n = sd_ndisc_router_rdnss_get_addresses(rt, &a);
|
||||
assert_se(n > 0);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
char buff[INET6_ADDRSTRLEN];
|
||||
log_info("DNS: %s", inet_ntop(AF_INET6, a + i, buff, sizeof(buff)));
|
||||
}
|
||||
|
||||
assert_se(sd_ndisc_router_rdnss_get_lifetime(rt, <) >= 0);
|
||||
log_info("Lifetime: %" PRIu32, lt);
|
||||
break;
|
||||
}
|
||||
|
||||
case SD_NDISC_OPTION_DNSSL: {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
uint32_t lt;
|
||||
int n, i;
|
||||
|
||||
n = sd_ndisc_router_dnssl_get_domains(rt, &l);
|
||||
assert_se(n > 0);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
log_info("Domain: %s", l[i]);
|
||||
|
||||
assert_se(sd_ndisc_router_dnssl_get_lifetime(rt, <) >= 0);
|
||||
log_info("Lifetime: %" PRIu32, lt);
|
||||
break;
|
||||
}}
|
||||
|
||||
r = sd_ndisc_router_option_next(rt);
|
||||
}
|
||||
}
|
||||
|
||||
static int test_rs_hangcheck(sd_event_source *s, uint64_t usec,
|
||||
void *userdata) {
|
||||
assert_se(false);
|
||||
@ -83,32 +225,39 @@ int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
|
||||
return send_ra_function(0);
|
||||
}
|
||||
|
||||
static void test_rs_done(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata) {
|
||||
static void test_callback(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) {
|
||||
sd_event *e = userdata;
|
||||
static unsigned idx = 0;
|
||||
uint8_t flags_array[] = {
|
||||
uint64_t flags_array[] = {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
ND_RA_FLAG_OTHER,
|
||||
ND_RA_FLAG_MANAGED
|
||||
};
|
||||
uint64_t flags;
|
||||
uint32_t mtu;
|
||||
|
||||
assert_se(nd);
|
||||
|
||||
if (event != SD_NDISC_EVENT_ROUTER)
|
||||
return;
|
||||
|
||||
router_dump(rt);
|
||||
|
||||
assert_se(sd_ndisc_router_get_flags(rt, &flags) >= 0);
|
||||
assert_se(flags == flags_array[idx]);
|
||||
idx++;
|
||||
|
||||
if (verbose)
|
||||
printf(" got event 0x%02x\n", flags);
|
||||
printf(" got event 0x%02" PRIx64 "\n", flags);
|
||||
|
||||
if (idx < ELEMENTSOF(flags_array)) {
|
||||
send_ra(flags_array[idx]);
|
||||
return;
|
||||
}
|
||||
|
||||
assert_se(sd_ndisc_get_mtu(nd, &mtu) == -ENOMSG);
|
||||
assert_se(sd_ndisc_get_mtu(nd, &mtu) == -ENODATA);
|
||||
|
||||
sd_event_exit(e, 0);
|
||||
}
|
||||
@ -132,17 +281,17 @@ static void test_rs(void) {
|
||||
|
||||
assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
|
||||
assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
|
||||
assert_se(sd_ndisc_set_callback(nd, test_rs_done, NULL, NULL, NULL, e) >= 0);
|
||||
assert_se(sd_ndisc_set_callback(nd, test_callback, e) >= 0);
|
||||
|
||||
assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(),
|
||||
time_now + 2 *USEC_PER_SEC, 0,
|
||||
test_rs_hangcheck, NULL) >= 0);
|
||||
|
||||
assert_se(sd_ndisc_stop(nd) >= 0);
|
||||
assert_se(sd_ndisc_router_discovery_start(nd) >= 0);
|
||||
assert_se(sd_ndisc_start(nd) >= 0);
|
||||
assert_se(sd_ndisc_stop(nd) >= 0);
|
||||
|
||||
assert_se(sd_ndisc_router_discovery_start(nd) >= 0);
|
||||
assert_se(sd_ndisc_start(nd) >= 0);
|
||||
|
||||
sd_event_loop(e);
|
||||
|
||||
|
@ -216,8 +216,7 @@ struct sd_event {
|
||||
pid_t original_pid;
|
||||
|
||||
unsigned iteration;
|
||||
dual_timestamp timestamp;
|
||||
usec_t timestamp_boottime;
|
||||
triple_timestamp timestamp;
|
||||
int state;
|
||||
|
||||
bool exit_requested:1;
|
||||
@ -1072,16 +1071,16 @@ _public_ int sd_event_add_time(
|
||||
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
|
||||
if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) &&
|
||||
!clock_boottime_supported())
|
||||
if (!clock_supported(clock)) /* Checks whether the kernel supports the clock */
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
type = clock_to_event_source_type(clock); /* checks whether sd-event supports this clock */
|
||||
if (type < 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!callback)
|
||||
callback = time_exit_callback;
|
||||
|
||||
type = clock_to_event_source_type(clock);
|
||||
assert_return(type >= 0, -EOPNOTSUPP);
|
||||
|
||||
d = event_get_clock_data(e, type);
|
||||
assert(d);
|
||||
|
||||
@ -2530,9 +2529,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
dual_timestamp_get(&e->timestamp);
|
||||
if (clock_boottime_supported())
|
||||
e->timestamp_boottime = now(CLOCK_BOOTTIME);
|
||||
triple_timestamp_get(&e->timestamp);
|
||||
|
||||
for (i = 0; i < m; i++) {
|
||||
|
||||
@ -2573,7 +2570,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = process_timer(e, e->timestamp_boottime, &e->boottime);
|
||||
r = process_timer(e, e->timestamp.boottime, &e->boottime);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
@ -2585,7 +2582,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = process_timer(e, e->timestamp_boottime, &e->boottime_alarm);
|
||||
r = process_timer(e, e->timestamp.boottime, &e->boottime_alarm);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
@ -2759,43 +2756,24 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(usec, -EINVAL);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
assert_return(IN_SET(clock,
|
||||
CLOCK_REALTIME,
|
||||
CLOCK_REALTIME_ALARM,
|
||||
CLOCK_MONOTONIC,
|
||||
CLOCK_BOOTTIME,
|
||||
CLOCK_BOOTTIME_ALARM), -EOPNOTSUPP);
|
||||
|
||||
if (!TRIPLE_TIMESTAMP_HAS_CLOCK(clock))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Generate a clean error in case CLOCK_BOOTTIME is not available. Note that don't use clock_supported() here,
|
||||
* for a reason: there are systems where CLOCK_BOOTTIME is supported, but CLOCK_BOOTTIME_ALARM is not, but for
|
||||
* the purpose of getting the time this doesn't matter. */
|
||||
if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && !clock_boottime_supported())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!dual_timestamp_is_set(&e->timestamp)) {
|
||||
if (!triple_timestamp_is_set(&e->timestamp)) {
|
||||
/* Implicitly fall back to now() if we never ran
|
||||
* before and thus have no cached time. */
|
||||
*usec = now(clock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (clock) {
|
||||
|
||||
case CLOCK_REALTIME:
|
||||
case CLOCK_REALTIME_ALARM:
|
||||
*usec = e->timestamp.realtime;
|
||||
break;
|
||||
|
||||
case CLOCK_MONOTONIC:
|
||||
*usec = e->timestamp.monotonic;
|
||||
break;
|
||||
|
||||
case CLOCK_BOOTTIME:
|
||||
case CLOCK_BOOTTIME_ALARM:
|
||||
*usec = e->timestamp_boottime;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown clock?");
|
||||
}
|
||||
|
||||
*usec = triple_timestamp_by_clock(&e->timestamp, clock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -60,10 +60,15 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dhcp6_address_change(Link *link, struct in6_addr *ip6_addr,
|
||||
uint32_t lifetime_preferred, uint32_t lifetime_valid) {
|
||||
int r;
|
||||
static int dhcp6_address_change(
|
||||
Link *link,
|
||||
struct in6_addr *ip6_addr,
|
||||
uint32_t lifetime_preferred,
|
||||
uint32_t lifetime_valid) {
|
||||
|
||||
_cleanup_address_free_ Address *addr = NULL;
|
||||
char buffer[INET6_ADDRSTRLEN];
|
||||
int r;
|
||||
|
||||
r = address_new(&addr);
|
||||
if (r < 0)
|
||||
@ -79,8 +84,8 @@ static int dhcp6_address_change(Link *link, struct in6_addr *ip6_addr,
|
||||
addr->cinfo.ifa_valid = lifetime_valid;
|
||||
|
||||
log_link_info(link,
|
||||
"DHCPv6 address "SD_NDISC_ADDRESS_FORMAT_STR"/%d timeout preferred %d valid %d",
|
||||
SD_NDISC_ADDRESS_FORMAT_VAL(addr->in_addr.in6),
|
||||
"DHCPv6 address %s/%d timeout preferred %d valid %d",
|
||||
inet_ntop(AF_INET6, &addr->in_addr.in6, buffer, sizeof(buffer)),
|
||||
addr->prefixlen, lifetime_preferred, lifetime_valid);
|
||||
|
||||
r = address_configure(addr, link, dhcp6_address_handler, true);
|
||||
|
@ -28,8 +28,9 @@
|
||||
#include "fileio.h"
|
||||
#include "netlink-util.h"
|
||||
#include "network-internal.h"
|
||||
#include "networkd.h"
|
||||
#include "networkd-lldp-tx.h"
|
||||
#include "networkd-ndisc.h"
|
||||
#include "networkd.h"
|
||||
#include "set.h"
|
||||
#include "socket-util.h"
|
||||
#include "stdio-util.h"
|
||||
@ -504,7 +505,10 @@ static void link_free(Link *link) {
|
||||
|
||||
sd_ipv4ll_unref(link->ipv4ll);
|
||||
sd_dhcp6_client_unref(link->dhcp6_client);
|
||||
sd_ndisc_unref(link->ndisc_router_discovery);
|
||||
sd_ndisc_unref(link->ndisc);
|
||||
|
||||
set_free_free(link->ndisc_rdnss);
|
||||
set_free_free(link->ndisc_dnssl);
|
||||
|
||||
if (link->manager)
|
||||
hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex));
|
||||
@ -616,8 +620,8 @@ static int link_stop_clients(Link *link) {
|
||||
r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m");
|
||||
}
|
||||
|
||||
if (link->ndisc_router_discovery) {
|
||||
k = sd_ndisc_stop(link->ndisc_router_discovery);
|
||||
if (link->ndisc) {
|
||||
k = sd_ndisc_stop(link->ndisc);
|
||||
if (k < 0)
|
||||
r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m");
|
||||
}
|
||||
@ -1453,11 +1457,11 @@ static int link_acquire_ipv6_conf(Link *link) {
|
||||
}
|
||||
|
||||
if (link_ipv6_accept_ra_enabled(link)) {
|
||||
assert(link->ndisc_router_discovery);
|
||||
assert(link->ndisc);
|
||||
|
||||
log_link_debug(link, "Discovering IPv6 routers");
|
||||
|
||||
r = sd_ndisc_router_discovery_start(link->ndisc_router_discovery);
|
||||
r = sd_ndisc_start(link->ndisc);
|
||||
if (r < 0 && r != -EBUSY)
|
||||
return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m");
|
||||
}
|
||||
@ -2364,7 +2368,11 @@ static int link_configure(Link *link) {
|
||||
}
|
||||
|
||||
if (link_lldp_rx_enabled(link)) {
|
||||
r = sd_lldp_new(&link->lldp, link->ifindex);
|
||||
r = sd_lldp_new(&link->lldp);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_lldp_set_ifindex(link->lldp, link->ifindex);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -3083,6 +3091,22 @@ int link_save(Link *link) {
|
||||
if (space)
|
||||
fputc(' ', f);
|
||||
serialize_in6_addrs(f, in6_addrs, r);
|
||||
space = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure to flush out old entries before we use the NDISC data */
|
||||
ndisc_vacuum(link);
|
||||
|
||||
if (link->network->dhcp_use_dns && link->ndisc_rdnss) {
|
||||
NDiscRDNSS *dd;
|
||||
|
||||
SET_FOREACH(dd, link->ndisc_rdnss, i) {
|
||||
if (space)
|
||||
fputc(' ', f);
|
||||
|
||||
serialize_in6_addrs(f, &dd->address, 1);
|
||||
space = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3128,7 +3152,6 @@ int link_save(Link *link) {
|
||||
if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
|
||||
if (link->dhcp_lease)
|
||||
(void) sd_dhcp_lease_get_domainname(link->dhcp_lease, &dhcp_domainname);
|
||||
|
||||
if (dhcp6_lease)
|
||||
(void) sd_dhcp6_lease_get_domains(dhcp6_lease, &dhcp6_domains);
|
||||
}
|
||||
@ -3136,22 +3159,34 @@ int link_save(Link *link) {
|
||||
fputs("DOMAINS=", f);
|
||||
fputstrv(f, link->network->search_domains, NULL, &space);
|
||||
|
||||
if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES && dhcp_domainname)
|
||||
fputs_with_space(f, dhcp_domainname, NULL, &space);
|
||||
if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) {
|
||||
NDiscDNSSL *dd;
|
||||
|
||||
if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES && dhcp6_domains)
|
||||
fputstrv(f, dhcp6_domains, NULL, &space);
|
||||
if (dhcp_domainname)
|
||||
fputs_with_space(f, dhcp_domainname, NULL, &space);
|
||||
if (dhcp6_domains)
|
||||
fputstrv(f, dhcp6_domains, NULL, &space);
|
||||
|
||||
SET_FOREACH(dd, link->ndisc_dnssl, i)
|
||||
fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space);
|
||||
}
|
||||
|
||||
fputc('\n', f);
|
||||
|
||||
fputs("ROUTE_DOMAINS=", f);
|
||||
fputstrv(f, link->network->route_domains, NULL, NULL);
|
||||
|
||||
if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE && dhcp_domainname)
|
||||
fputs_with_space(f, dhcp_domainname, NULL, &space);
|
||||
if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE) {
|
||||
NDiscDNSSL *dd;
|
||||
|
||||
if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE && dhcp6_domains)
|
||||
fputstrv(f, dhcp6_domains, NULL, &space);
|
||||
if (dhcp_domainname)
|
||||
fputs_with_space(f, dhcp_domainname, NULL, &space);
|
||||
if (dhcp6_domains)
|
||||
fputstrv(f, dhcp6_domains, NULL, &space);
|
||||
|
||||
SET_FOREACH(dd, link->ndisc_dnssl, i)
|
||||
fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space);
|
||||
}
|
||||
|
||||
fputc('\n', f);
|
||||
|
||||
|
@ -98,6 +98,7 @@ typedef struct Link {
|
||||
unsigned dhcp4_messages;
|
||||
bool dhcp4_configured;
|
||||
bool dhcp6_configured;
|
||||
|
||||
unsigned ndisc_messages;
|
||||
bool ndisc_configured;
|
||||
|
||||
@ -111,7 +112,10 @@ typedef struct Link {
|
||||
|
||||
sd_dhcp_server *dhcp_server;
|
||||
|
||||
sd_ndisc *ndisc_router_discovery;
|
||||
sd_ndisc *ndisc;
|
||||
Set *ndisc_rdnss;
|
||||
Set *ndisc_dnssl;
|
||||
|
||||
sd_dhcp6_client *dhcp6_client;
|
||||
bool rtnl_extended_attrs;
|
||||
|
||||
@ -161,7 +165,6 @@ int ipv4ll_configure(Link *link);
|
||||
int dhcp4_configure(Link *link);
|
||||
int dhcp6_configure(Link *link);
|
||||
int dhcp6_request_address(Link *link, int ir);
|
||||
int ndisc_configure(Link *link);
|
||||
|
||||
const char* link_state_to_string(LinkState s) _const_;
|
||||
LinkState link_state_from_string(const char *s) _pure_;
|
||||
|
@ -17,14 +17,15 @@
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <netinet/ether.h>
|
||||
#include <netinet/icmp6.h>
|
||||
#include <netinet/in.h>
|
||||
#include <linux/if.h>
|
||||
|
||||
#include "sd-ndisc.h"
|
||||
|
||||
#include "networkd.h"
|
||||
#include "networkd-ndisc.h"
|
||||
|
||||
#define NDISC_DNSSL_MAX 64U
|
||||
#define NDISC_RDNSS_MAX 64U
|
||||
|
||||
static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
|
||||
_cleanup_link_unref_ Link *link = userdata;
|
||||
@ -49,19 +50,92 @@ static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen,
|
||||
unsigned lifetime_preferred, unsigned lifetime_valid, void *userdata) {
|
||||
_cleanup_address_free_ Address *address = NULL;
|
||||
Link *link = userdata;
|
||||
static void ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
|
||||
_cleanup_route_free_ Route *route = NULL;
|
||||
struct in6_addr gateway;
|
||||
uint16_t lifetime;
|
||||
unsigned preference;
|
||||
usec_t time_now;
|
||||
int r;
|
||||
|
||||
assert(nd);
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
assert(rt);
|
||||
|
||||
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
|
||||
r = sd_ndisc_router_get_lifetime(rt, &lifetime);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
|
||||
return;
|
||||
}
|
||||
if (lifetime == 0) /* not a default router */
|
||||
return;
|
||||
|
||||
r = sd_ndisc_router_get_address(rt, &gateway);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = sd_ndisc_router_get_preference(rt, &preference);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = route_new(&route);
|
||||
if (r < 0) {
|
||||
log_link_error_errno(link, r, "Could not allocate route: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
route->family = AF_INET6;
|
||||
route->table = RT_TABLE_MAIN;
|
||||
route->protocol = RTPROT_RA;
|
||||
route->pref = preference;
|
||||
route->gw.in6 = gateway;
|
||||
route->lifetime = time_now + lifetime * USEC_PER_SEC;
|
||||
|
||||
r = route_configure(route, link, ndisc_netlink_handler);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Could not set default route: %m");
|
||||
link_enter_failed(link);
|
||||
return;
|
||||
}
|
||||
|
||||
link->ndisc_messages++;
|
||||
}
|
||||
|
||||
static void ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
|
||||
_cleanup_address_free_ Address *address = NULL;
|
||||
uint32_t lifetime_valid, lifetime_preferred;
|
||||
unsigned prefixlen;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(rt);
|
||||
|
||||
r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
|
||||
if (r < 0) {
|
||||
log_link_error_errno(link, r, "Failed to get prefix length: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid);
|
||||
if (r < 0) {
|
||||
log_link_error_errno(link, r, "Failed to get prefix valid lifetime: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred);
|
||||
if (r < 0) {
|
||||
log_link_error_errno(link, r, "Failed to get prefix preferred lifetime: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = address_new(&address);
|
||||
if (r < 0) {
|
||||
@ -69,10 +143,13 @@ static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr
|
||||
return;
|
||||
}
|
||||
|
||||
assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0);
|
||||
|
||||
address->family = AF_INET6;
|
||||
address->in_addr.in6 = *prefix;
|
||||
r = sd_ndisc_router_prefix_get_address(rt, &address->in_addr.in6);
|
||||
if (r < 0) {
|
||||
log_link_error_errno(link, r, "Failed to get prefix address: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
if (in_addr_is_null(AF_INET6, (const union in_addr_union *) &link->network->ipv6_token) == 0)
|
||||
memcpy(((char *)&address->in_addr.in6) + 8, ((char *)&link->network->ipv6_token) + 8, 8);
|
||||
else {
|
||||
@ -102,17 +179,33 @@ static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr
|
||||
link->ndisc_messages++;
|
||||
}
|
||||
|
||||
static void ndisc_prefix_onlink_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, unsigned lifetime, void *userdata) {
|
||||
static void ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
|
||||
_cleanup_route_free_ Route *route = NULL;
|
||||
Link *link = userdata;
|
||||
usec_t time_now;
|
||||
uint32_t lifetime;
|
||||
unsigned prefixlen;
|
||||
int r;
|
||||
|
||||
assert(nd);
|
||||
assert(link);
|
||||
assert(rt);
|
||||
|
||||
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
|
||||
r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
|
||||
if (r < 0) {
|
||||
log_link_error_errno(link, r, "Failed to get prefix length: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime);
|
||||
if (r < 0) {
|
||||
log_link_error_errno(link, r, "Failed to get prefix lifetime: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = route_new(&route);
|
||||
if (r < 0) {
|
||||
@ -120,16 +213,19 @@ static void ndisc_prefix_onlink_handler(sd_ndisc *nd, const struct in6_addr *pre
|
||||
return;
|
||||
}
|
||||
|
||||
assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0);
|
||||
|
||||
route->family = AF_INET6;
|
||||
route->table = RT_TABLE_MAIN;
|
||||
route->protocol = RTPROT_RA;
|
||||
route->flags = RTM_F_PREFIX;
|
||||
route->dst.in6 = *prefix;
|
||||
route->dst_prefixlen = prefixlen;
|
||||
route->lifetime = time_now + lifetime * USEC_PER_SEC;
|
||||
|
||||
r = sd_ndisc_router_prefix_get_address(rt, &route->dst.in6);
|
||||
if (r < 0) {
|
||||
log_link_error_errno(link, r, "Failed to get prefix address: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = route_configure(route, link, ndisc_netlink_handler);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Could not set prefix route: %m");
|
||||
@ -140,32 +236,47 @@ static void ndisc_prefix_onlink_handler(sd_ndisc *nd, const struct in6_addr *pre
|
||||
link->ndisc_messages++;
|
||||
}
|
||||
|
||||
static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata) {
|
||||
static void ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
|
||||
_cleanup_route_free_ Route *route = NULL;
|
||||
Link *link = userdata;
|
||||
struct in6_addr gateway;
|
||||
uint32_t lifetime;
|
||||
unsigned preference, prefixlen;
|
||||
usec_t time_now;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
assert(link->manager);
|
||||
assert(link->dhcp6_client);
|
||||
assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
|
||||
|
||||
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
|
||||
r = sd_ndisc_router_route_get_lifetime(rt, &lifetime);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
|
||||
return;
|
||||
}
|
||||
if (lifetime == 0)
|
||||
return;
|
||||
|
||||
if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) {
|
||||
/* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
|
||||
r = dhcp6_request_address(link, flags & ND_RA_FLAG_MANAGED ? false : true);
|
||||
if (r < 0 && r != -EBUSY)
|
||||
log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
|
||||
else
|
||||
log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request");
|
||||
r = sd_ndisc_router_get_address(rt, &gateway);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gateway)
|
||||
r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to get route prefix length: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = sd_ndisc_router_route_get_preference(rt, &preference);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = route_new(&route);
|
||||
if (r < 0) {
|
||||
@ -173,18 +284,23 @@ static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_a
|
||||
return;
|
||||
}
|
||||
|
||||
assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0);
|
||||
|
||||
route->family = AF_INET6;
|
||||
route->table = RT_TABLE_MAIN;
|
||||
route->protocol = RTPROT_RA;
|
||||
route->pref = pref;
|
||||
route->gw.in6 = *gateway;
|
||||
route->pref = preference;
|
||||
route->gw.in6 = gateway;
|
||||
route->dst_prefixlen = prefixlen;
|
||||
route->lifetime = time_now + lifetime * USEC_PER_SEC;
|
||||
|
||||
r = sd_ndisc_router_route_get_address(rt, &route->dst.in6);
|
||||
if (r < 0) {
|
||||
log_link_error_errno(link, r, "Failed to get route address: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = route_configure(route, link, ndisc_netlink_handler);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Could not set default route: %m");
|
||||
log_link_warning_errno(link, r, "Could not set additional route: %m");
|
||||
link_enter_failed(link);
|
||||
return;
|
||||
}
|
||||
@ -192,7 +308,290 @@ static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_a
|
||||
link->ndisc_messages++;
|
||||
}
|
||||
|
||||
static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) {
|
||||
static void ndisc_rdnss_hash_func(const void *p, struct siphash *state) {
|
||||
const NDiscRDNSS *x = p;
|
||||
|
||||
siphash24_compress(&x->address, sizeof(x->address), state);
|
||||
}
|
||||
|
||||
static int ndisc_rdnss_compare_func(const void *_a, const void *_b) {
|
||||
const NDiscRDNSS *a = _a, *b = _b;
|
||||
|
||||
return memcmp(&a->address, &b->address, sizeof(a->address));
|
||||
}
|
||||
|
||||
static const struct hash_ops ndisc_rdnss_hash_ops = {
|
||||
.hash = ndisc_rdnss_hash_func,
|
||||
.compare = ndisc_rdnss_compare_func
|
||||
};
|
||||
|
||||
static void ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
|
||||
uint32_t lifetime;
|
||||
const struct in6_addr *a;
|
||||
usec_t time_now;
|
||||
int i, n, r;
|
||||
|
||||
assert(link);
|
||||
assert(rt);
|
||||
|
||||
r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
n = sd_ndisc_router_rdnss_get_addresses(rt, &a);
|
||||
if (n < 0) {
|
||||
log_link_warning_errno(link, n, "Failed to get RDNSS addresses: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
NDiscRDNSS d = {
|
||||
.address = a[i]
|
||||
}, *x;
|
||||
|
||||
if (lifetime == 0) {
|
||||
(void) set_remove(link->ndisc_rdnss, &d);
|
||||
link_dirty(link);
|
||||
continue;
|
||||
}
|
||||
|
||||
x = set_get(link->ndisc_rdnss, &d);
|
||||
if (x) {
|
||||
x->valid_until = time_now + lifetime * USEC_PER_SEC;
|
||||
continue;
|
||||
}
|
||||
|
||||
ndisc_vacuum(link);
|
||||
|
||||
if (set_size(link->ndisc_rdnss) >= NDISC_RDNSS_MAX) {
|
||||
log_link_warning(link, "Too many RDNSS records per link, ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
r = set_ensure_allocated(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops);
|
||||
if (r < 0) {
|
||||
log_oom();
|
||||
return;
|
||||
}
|
||||
|
||||
x = new0(NDiscRDNSS, 1);
|
||||
if (!x) {
|
||||
log_oom();
|
||||
return;
|
||||
}
|
||||
|
||||
x->address = a[i];
|
||||
x->valid_until = time_now + lifetime * USEC_PER_SEC;
|
||||
|
||||
r = set_put(link->ndisc_rdnss, x);
|
||||
if (r < 0) {
|
||||
free(x);
|
||||
log_oom();
|
||||
return;
|
||||
}
|
||||
|
||||
assert(r > 0);
|
||||
link_dirty(link);
|
||||
}
|
||||
}
|
||||
|
||||
static void ndisc_dnssl_hash_func(const void *p, struct siphash *state) {
|
||||
const NDiscDNSSL *x = p;
|
||||
|
||||
siphash24_compress(NDISC_DNSSL_DOMAIN(x), strlen(NDISC_DNSSL_DOMAIN(x)), state);
|
||||
}
|
||||
|
||||
static int ndisc_dnssl_compare_func(const void *_a, const void *_b) {
|
||||
const NDiscDNSSL *a = _a, *b = _b;
|
||||
|
||||
return strcmp(NDISC_DNSSL_DOMAIN(a), NDISC_DNSSL_DOMAIN(b));
|
||||
}
|
||||
|
||||
static const struct hash_ops ndisc_dnssl_hash_ops = {
|
||||
.hash = ndisc_dnssl_hash_func,
|
||||
.compare = ndisc_dnssl_compare_func
|
||||
};
|
||||
|
||||
static void ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
uint32_t lifetime;
|
||||
usec_t time_now;
|
||||
char **i;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(rt);
|
||||
|
||||
r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = sd_ndisc_router_dnssl_get_domains(rt, &l);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to get RDNSS addresses: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
STRV_FOREACH(i, l) {
|
||||
struct {
|
||||
NDiscDNSSL header;
|
||||
char domain[strlen(*i)];
|
||||
} s;
|
||||
NDiscDNSSL *x;
|
||||
|
||||
zero(s.header);
|
||||
strcpy(s.domain, *i);
|
||||
|
||||
if (lifetime == 0) {
|
||||
(void) set_remove(link->ndisc_dnssl, &s);
|
||||
link_dirty(link);
|
||||
continue;
|
||||
}
|
||||
|
||||
x = set_get(link->ndisc_dnssl, &s);
|
||||
if (x) {
|
||||
x->valid_until = time_now + lifetime * USEC_PER_SEC;
|
||||
continue;
|
||||
}
|
||||
|
||||
ndisc_vacuum(link);
|
||||
|
||||
if (set_size(link->ndisc_dnssl) >= NDISC_DNSSL_MAX) {
|
||||
log_link_warning(link, "Too many DNSSL records per link, ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
r = set_ensure_allocated(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops);
|
||||
if (r < 0) {
|
||||
log_oom();
|
||||
return;
|
||||
}
|
||||
|
||||
x = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*i) + 1);
|
||||
if (!x) {
|
||||
log_oom();
|
||||
return;
|
||||
}
|
||||
|
||||
strcpy(NDISC_DNSSL_DOMAIN(x), *i);
|
||||
x->valid_until = time_now + lifetime * USEC_PER_SEC;
|
||||
|
||||
r = set_put(link->ndisc_dnssl, x);
|
||||
if (r < 0) {
|
||||
free(x);
|
||||
log_oom();
|
||||
return;
|
||||
}
|
||||
|
||||
assert(r > 0);
|
||||
link_dirty(link);
|
||||
}
|
||||
}
|
||||
|
||||
static void ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(rt);
|
||||
|
||||
r = sd_ndisc_router_option_rewind(rt);
|
||||
for (;;) {
|
||||
uint8_t type;
|
||||
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to iterate through options: %m");
|
||||
return;
|
||||
}
|
||||
if (r == 0) /* EOF */
|
||||
break;
|
||||
|
||||
r = sd_ndisc_router_option_get_type(rt, &type);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to get RA option type: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
|
||||
case SD_NDISC_OPTION_PREFIX_INFORMATION: {
|
||||
uint8_t flags;
|
||||
|
||||
r = sd_ndisc_router_prefix_get_flags(rt, &flags);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags & ND_OPT_PI_FLAG_ONLINK)
|
||||
ndisc_router_process_onlink_prefix(link, rt);
|
||||
if (flags & ND_OPT_PI_FLAG_AUTO)
|
||||
ndisc_router_process_autonomous_prefix(link, rt);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SD_NDISC_OPTION_ROUTE_INFORMATION:
|
||||
ndisc_router_process_route(link, rt);
|
||||
break;
|
||||
|
||||
case SD_NDISC_OPTION_RDNSS:
|
||||
ndisc_router_process_rdnss(link, rt);
|
||||
break;
|
||||
|
||||
case SD_NDISC_OPTION_DNSSL:
|
||||
ndisc_router_process_dnssl(link, rt);
|
||||
break;
|
||||
}
|
||||
|
||||
r = sd_ndisc_router_option_next(rt);
|
||||
}
|
||||
}
|
||||
|
||||
static void ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
|
||||
uint64_t flags;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
assert(link->manager);
|
||||
assert(rt);
|
||||
|
||||
r = sd_ndisc_router_get_flags(rt, &flags);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to get RA flags: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) {
|
||||
/* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
|
||||
r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED));
|
||||
if (r < 0 && r != -EBUSY)
|
||||
log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
|
||||
else
|
||||
log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request");
|
||||
}
|
||||
|
||||
ndisc_router_process_default(link, rt);
|
||||
ndisc_router_process_options(link, rt);
|
||||
}
|
||||
|
||||
static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) {
|
||||
Link *link = userdata;
|
||||
|
||||
assert(link);
|
||||
@ -201,12 +600,15 @@ static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) {
|
||||
return;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case SD_NDISC_EVENT_ROUTER:
|
||||
ndisc_router_handler(link, rt);
|
||||
break;
|
||||
|
||||
case SD_NDISC_EVENT_TIMEOUT:
|
||||
link->ndisc_configured = true;
|
||||
link_check_ready(link);
|
||||
|
||||
break;
|
||||
case SD_NDISC_EVENT_STOP:
|
||||
break;
|
||||
default:
|
||||
log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event);
|
||||
@ -216,30 +618,52 @@ static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) {
|
||||
int ndisc_configure(Link *link) {
|
||||
int r;
|
||||
|
||||
assert_return(link, -EINVAL);
|
||||
assert(link);
|
||||
|
||||
r = sd_ndisc_new(&link->ndisc_router_discovery);
|
||||
r = sd_ndisc_new(&link->ndisc);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_ndisc_attach_event(link->ndisc_router_discovery, NULL, 0);
|
||||
r = sd_ndisc_attach_event(link->ndisc, NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_ndisc_set_mac(link->ndisc_router_discovery, &link->mac);
|
||||
r = sd_ndisc_set_mac(link->ndisc, &link->mac);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_ndisc_set_ifindex(link->ndisc_router_discovery, link->ifindex);
|
||||
r = sd_ndisc_set_ifindex(link->ndisc, link->ifindex);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_ndisc_set_callback(link->ndisc_router_discovery,
|
||||
ndisc_router_handler,
|
||||
ndisc_prefix_onlink_handler,
|
||||
ndisc_prefix_autonomous_handler,
|
||||
ndisc_handler,
|
||||
link);
|
||||
r = sd_ndisc_set_callback(link->ndisc, ndisc_handler, link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ndisc_vacuum(Link *link) {
|
||||
NDiscRDNSS *r;
|
||||
NDiscDNSSL *d;
|
||||
Iterator i;
|
||||
usec_t time_now;
|
||||
|
||||
assert(link);
|
||||
|
||||
/* Removes all RDNSS and DNSSL entries whose validity time has passed */
|
||||
|
||||
time_now = now(clock_boottime_or_monotonic());
|
||||
|
||||
SET_FOREACH(r, link->ndisc_rdnss, i)
|
||||
if (r->valid_until < time_now) {
|
||||
(void) set_remove(link->ndisc_rdnss, r);
|
||||
link_dirty(link);
|
||||
}
|
||||
|
||||
SET_FOREACH(d, link->ndisc_dnssl, i)
|
||||
if (d->valid_until < time_now) {
|
||||
(void) set_remove(link->ndisc_dnssl, d);
|
||||
link_dirty(link);
|
||||
}
|
||||
}
|
||||
|
39
src/network/networkd-ndisc.h
Normal file
39
src/network/networkd-ndisc.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2013 Tom Gundersen <teg@jklm.no>
|
||||
|
||||
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 "networkd-link.h"
|
||||
|
||||
typedef struct NDiscRDNSS {
|
||||
usec_t valid_until;
|
||||
struct in6_addr address;
|
||||
} NDiscRDNSS;
|
||||
|
||||
typedef struct NDiscDNSSL {
|
||||
usec_t valid_until;
|
||||
/* The domain name follows immediately. */
|
||||
} NDiscDNSSL;
|
||||
|
||||
static inline char* NDISC_DNSSL_DOMAIN(const NDiscDNSSL *n) {
|
||||
return ((char*) n) + ALIGN(sizeof(NDiscDNSSL));
|
||||
}
|
||||
|
||||
int ndisc_configure(Link *link);
|
||||
void ndisc_vacuum(Link *link);
|
@ -89,6 +89,8 @@ DHCP.DUIDRawData, config_parse_duid_rawdata,
|
||||
DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
|
||||
DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
|
||||
DHCP.IAID, config_parse_iaid, 0, offsetof(Network, iaid)
|
||||
IPv6AcceptRouterAdvertisements.UseDNS config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns)
|
||||
IPv6AcceptRouterAdvertisements.UseDomains config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains)
|
||||
DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec)
|
||||
DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec)
|
||||
DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns)
|
||||
|
@ -51,8 +51,8 @@ static int network_load_one(Manager *manager, const char *filename) {
|
||||
if (!file) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
else
|
||||
return -errno;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (null_or_empty_fd(fileno(file))) {
|
||||
@ -134,6 +134,7 @@ static int network_load_one(Manager *manager, const char *filename) {
|
||||
network->ipv6_hop_limit = -1;
|
||||
network->duid.type = _DUID_TYPE_INVALID;
|
||||
network->proxy_arp = -1;
|
||||
network->ipv6_accept_ra_use_dns = true;
|
||||
|
||||
r = config_parse(NULL, filename, file,
|
||||
"Match\0"
|
||||
|
@ -154,6 +154,9 @@ struct Network {
|
||||
int ipv6_hop_limit;
|
||||
int proxy_arp;
|
||||
|
||||
bool ipv6_accept_ra_use_dns;
|
||||
DHCPUseDomains ipv6_accept_ra_use_domains;
|
||||
|
||||
union in_addr_union ipv6_token;
|
||||
IPv6PrivacyExtensions ipv6_privacy_extensions;
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "sd-event.h"
|
||||
|
||||
@ -30,9 +31,6 @@
|
||||
|
||||
_SD_BEGIN_DECLARATIONS;
|
||||
|
||||
typedef struct sd_lldp sd_lldp;
|
||||
typedef struct sd_lldp_neighbor sd_lldp_neighbor;
|
||||
|
||||
/* IEEE 802.3AB Clause 9: TLV Types */
|
||||
enum {
|
||||
SD_LLDP_TYPE_END = 0,
|
||||
@ -111,6 +109,9 @@ enum {
|
||||
SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION = 7,
|
||||
};
|
||||
|
||||
typedef struct sd_lldp sd_lldp;
|
||||
typedef struct sd_lldp_neighbor sd_lldp_neighbor;
|
||||
|
||||
typedef enum sd_lldp_event {
|
||||
SD_LLDP_EVENT_ADDED = 'a',
|
||||
SD_LLDP_EVENT_REMOVED = 'r',
|
||||
@ -120,7 +121,8 @@ typedef enum sd_lldp_event {
|
||||
|
||||
typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata);
|
||||
|
||||
int sd_lldp_new(sd_lldp **ret, int ifindex);
|
||||
int sd_lldp_new(sd_lldp **ret);
|
||||
sd_lldp* sd_lldp_ref(sd_lldp *lldp);
|
||||
sd_lldp* sd_lldp_unref(sd_lldp *lldp);
|
||||
|
||||
int sd_lldp_start(sd_lldp *lldp);
|
||||
@ -128,8 +130,10 @@ int sd_lldp_stop(sd_lldp *lldp);
|
||||
|
||||
int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority);
|
||||
int sd_lldp_detach_event(sd_lldp *lldp);
|
||||
sd_event *sd_lldp_get_event(sd_lldp *lldp);
|
||||
|
||||
int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata);
|
||||
int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex);
|
||||
|
||||
/* Controls how much and what to store in the neighbors database */
|
||||
int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t n);
|
||||
@ -145,6 +149,7 @@ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n);
|
||||
/* Access to LLDP frame metadata */
|
||||
int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address);
|
||||
int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address);
|
||||
int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret);
|
||||
int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size);
|
||||
|
||||
/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */
|
||||
@ -152,7 +157,7 @@ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const vo
|
||||
int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret);
|
||||
int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size);
|
||||
int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret);
|
||||
int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret);
|
||||
int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec);
|
||||
int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret);
|
||||
int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret);
|
||||
int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret);
|
||||
|
@ -22,6 +22,8 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "sd-event.h"
|
||||
|
||||
@ -29,55 +31,99 @@
|
||||
|
||||
_SD_BEGIN_DECLARATIONS;
|
||||
|
||||
/* Neightbor Discovery Options, RFC 4861, Section 4.6 and
|
||||
* https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-5 */
|
||||
enum {
|
||||
SD_NDISC_EVENT_STOP = 0,
|
||||
SD_NDISC_EVENT_TIMEOUT = 1,
|
||||
SD_NDISC_OPTION_SOURCE_LL_ADDRESS = 1,
|
||||
SD_NDISC_OPTION_TARGET_LL_ADDRESS = 2,
|
||||
SD_NDISC_OPTION_PREFIX_INFORMATION = 3,
|
||||
SD_NDISC_OPTION_MTU = 5,
|
||||
SD_NDISC_OPTION_ROUTE_INFORMATION = 24,
|
||||
SD_NDISC_OPTION_RDNSS = 25,
|
||||
SD_NDISC_OPTION_FLAGS_EXTENSION = 26,
|
||||
SD_NDISC_OPTION_DNSSL = 31,
|
||||
SD_NDISC_OPTION_CAPTIVE_PORTAL = 37,
|
||||
};
|
||||
|
||||
/* Route preference, RFC 4191, Section 2.1 */
|
||||
enum {
|
||||
SD_NDISC_PREFERENCE_LOW = 3U,
|
||||
SD_NDISC_PREFERENCE_MEDIUM = 0U,
|
||||
SD_NDISC_PREFERENCE_HIGH = 1U,
|
||||
};
|
||||
|
||||
typedef struct sd_ndisc sd_ndisc;
|
||||
typedef struct sd_ndisc_router sd_ndisc_router;
|
||||
|
||||
typedef void(*sd_ndisc_router_callback_t)(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata);
|
||||
typedef void(*sd_ndisc_prefix_onlink_callback_t)(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen,
|
||||
unsigned lifetime, void *userdata);
|
||||
typedef void(*sd_ndisc_prefix_autonomous_callback_t)(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen,
|
||||
unsigned lifetime_prefered, unsigned lifetime_valid, void *userdata);
|
||||
typedef void(*sd_ndisc_callback_t)(sd_ndisc *nd, int event, void *userdata);
|
||||
typedef enum sd_ndisc_event {
|
||||
SD_NDISC_EVENT_TIMEOUT = 't',
|
||||
SD_NDISC_EVENT_ROUTER = 'r',
|
||||
} sd_ndisc_event;
|
||||
|
||||
int sd_ndisc_set_callback(sd_ndisc *nd,
|
||||
sd_ndisc_router_callback_t rcb,
|
||||
sd_ndisc_prefix_onlink_callback_t plcb,
|
||||
sd_ndisc_prefix_autonomous_callback_t pacb,
|
||||
sd_ndisc_callback_t cb,
|
||||
void *userdata);
|
||||
int sd_ndisc_set_ifindex(sd_ndisc *nd, int interface_index);
|
||||
int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
|
||||
typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata);
|
||||
|
||||
int sd_ndisc_new(sd_ndisc **ret);
|
||||
sd_ndisc *sd_ndisc_ref(sd_ndisc *nd);
|
||||
sd_ndisc *sd_ndisc_unref(sd_ndisc *nd);
|
||||
|
||||
int sd_ndisc_start(sd_ndisc *nd);
|
||||
int sd_ndisc_stop(sd_ndisc *nd);
|
||||
|
||||
int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority);
|
||||
int sd_ndisc_detach_event(sd_ndisc *nd);
|
||||
sd_event *sd_ndisc_get_event(sd_ndisc *nd);
|
||||
|
||||
sd_ndisc *sd_ndisc_ref(sd_ndisc *nd);
|
||||
sd_ndisc *sd_ndisc_unref(sd_ndisc *nd);
|
||||
int sd_ndisc_new(sd_ndisc **ret);
|
||||
int sd_ndisc_set_callback(sd_ndisc *nd, sd_ndisc_callback_t cb, void *userdata);
|
||||
int sd_ndisc_set_ifindex(sd_ndisc *nd, int interface_index);
|
||||
int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
|
||||
|
||||
int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu);
|
||||
int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *ret);
|
||||
int sd_ndisc_get_hop_limit(sd_ndisc *nd, uint8_t *ret);
|
||||
|
||||
int sd_ndisc_stop(sd_ndisc *nd);
|
||||
int sd_ndisc_router_discovery_start(sd_ndisc *nd);
|
||||
int sd_ndisc_router_from_raw(sd_ndisc_router **ret, const void *raw, size_t raw_size);
|
||||
sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt);
|
||||
sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt);
|
||||
|
||||
#define SD_NDISC_ADDRESS_FORMAT_STR "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
|
||||
int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr);
|
||||
int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
|
||||
int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size);
|
||||
|
||||
#define SD_NDISC_ADDRESS_FORMAT_VAL(address) \
|
||||
be16toh((address).s6_addr16[0]), \
|
||||
be16toh((address).s6_addr16[1]), \
|
||||
be16toh((address).s6_addr16[2]), \
|
||||
be16toh((address).s6_addr16[3]), \
|
||||
be16toh((address).s6_addr16[4]), \
|
||||
be16toh((address).s6_addr16[5]), \
|
||||
be16toh((address).s6_addr16[6]), \
|
||||
be16toh((address).s6_addr16[7])
|
||||
int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret);
|
||||
int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret_flags);
|
||||
int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret);
|
||||
int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint16_t *ret_lifetime);
|
||||
int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret);
|
||||
|
||||
/* Generic option access */
|
||||
int sd_ndisc_router_option_rewind(sd_ndisc_router *rt);
|
||||
int sd_ndisc_router_option_next(sd_ndisc_router *rt);
|
||||
int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret);
|
||||
int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type);
|
||||
int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size);
|
||||
|
||||
/* Specific option access: SD_NDISC_OPTION_PREFIX_INFORMATION */
|
||||
int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint32_t *ret);
|
||||
int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint32_t *ret);
|
||||
int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret);
|
||||
int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr);
|
||||
int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *prefixlen);
|
||||
|
||||
/* Specific option access: SD_NDISC_OPTION_ROUTE_INFORMATION */
|
||||
int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
|
||||
int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr);
|
||||
int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *prefixlen);
|
||||
int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret);
|
||||
|
||||
/* Specific option access: SD_NDISC_OPTION_RDNSS */
|
||||
int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret);
|
||||
int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
|
||||
|
||||
/* Specific option access: SD_NDISC_OPTION_DNSSL */
|
||||
int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret);
|
||||
int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
|
||||
|
||||
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref);
|
||||
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router, sd_ndisc_router_unref);
|
||||
|
||||
_SD_END_DECLARATIONS;
|
||||
|
||||
|
@ -159,7 +159,31 @@ static void test_le(void) {
|
||||
assert_se(memcmp(&scratch[7], &data[7], sizeof(uint64_t)) == 0);
|
||||
}
|
||||
|
||||
static void test_ne(void) {
|
||||
uint16_t x = 4711;
|
||||
uint32_t y = 123456;
|
||||
uint64_t z = 9876543210;
|
||||
|
||||
/* Note that we don't bother actually testing alignment issues in this function, after all the _ne() functions
|
||||
* are just aliases for the _le() or _be() implementations, which we test extensively above. Hence, in this
|
||||
* function, just ensure that they map to the right version on the local architecture. */
|
||||
|
||||
assert_se(unaligned_read_ne16(&x) == 4711);
|
||||
assert_se(unaligned_read_ne32(&y) == 123456);
|
||||
assert_se(unaligned_read_ne64(&z) == 9876543210);
|
||||
|
||||
unaligned_write_ne16(&x, 1);
|
||||
unaligned_write_ne32(&y, 2);
|
||||
unaligned_write_ne64(&z, 3);
|
||||
|
||||
assert_se(x == 1);
|
||||
assert_se(y == 2);
|
||||
assert_se(z == 3);
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
test_be();
|
||||
test_le();
|
||||
test_ne();
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user