mirror of
https://github.com/systemd/systemd.git
synced 2025-01-11 09:18:07 +03:00
Merge pull request #5598 from pfl/ndisc_prefix_delegation
Initial Router Advertisment implementation
This commit is contained in:
commit
b2d0c14214
16
Makefile.am
16
Makefile.am
@ -3644,6 +3644,7 @@ libsystemd_network_la_SOURCES = \
|
||||
src/systemd/sd-ipv4ll.h \
|
||||
src/systemd/sd-ipv4acd.h \
|
||||
src/systemd/sd-ndisc.h \
|
||||
src/systemd/sd-radv.h \
|
||||
src/systemd/sd-dhcp6-client.h \
|
||||
src/systemd/sd-dhcp6-lease.h \
|
||||
src/systemd/sd-lldp.h \
|
||||
@ -3667,6 +3668,8 @@ libsystemd_network_la_SOURCES = \
|
||||
src/libsystemd-network/ndisc-internal.h \
|
||||
src/libsystemd-network/ndisc-router.h \
|
||||
src/libsystemd-network/ndisc-router.c \
|
||||
src/libsystemd-network/sd-radv.c \
|
||||
src/libsystemd-network/radv-internal.h \
|
||||
src/libsystemd-network/icmp6-util.h \
|
||||
src/libsystemd-network/icmp6-util.c \
|
||||
src/libsystemd-network/sd-dhcp6-client.c \
|
||||
@ -3760,6 +3763,16 @@ test_ndisc_rs_LDADD = \
|
||||
libudev.la \
|
||||
libsystemd-shared.la
|
||||
|
||||
test_ndisc_ra_SOURCES = \
|
||||
src/systemd/sd-ndisc.h \
|
||||
src/libsystemd-network/icmp6-util.h \
|
||||
src/libsystemd-network/test-ndisc-ra.c
|
||||
|
||||
test_ndisc_ra_LDADD = \
|
||||
libsystemd-network.la \
|
||||
libudev.la \
|
||||
libsystemd-shared.la
|
||||
|
||||
test_dhcp6_client_SOURCES = \
|
||||
src/systemd/sd-dhcp6-client.h \
|
||||
src/libsystemd-network/dhcp6-internal.h \
|
||||
@ -3786,6 +3799,7 @@ tests += \
|
||||
test-sd-dhcp-lease \
|
||||
test-ipv4ll \
|
||||
test-ndisc-rs \
|
||||
test-ndisc-ra \
|
||||
test-dhcp6-client \
|
||||
test-lldp
|
||||
|
||||
@ -5864,6 +5878,8 @@ libnetworkd_core_la_SOURCES = \
|
||||
src/network/networkd-dhcp4.c \
|
||||
src/network/networkd-dhcp6.c \
|
||||
src/network/networkd-ndisc.h \
|
||||
src/network/networkd-radv.c \
|
||||
src/network/networkd-radv.h \
|
||||
src/network/networkd-ndisc.c \
|
||||
src/network/networkd-network.h \
|
||||
src/network/networkd-network.c \
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "fd-util.h"
|
||||
#include "icmp6-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "in-addr-util.h"
|
||||
|
||||
#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
|
||||
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||
@ -41,12 +42,9 @@
|
||||
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
|
||||
|
||||
int icmp6_bind_router_solicitation(int index) {
|
||||
struct icmp6_filter filter = { };
|
||||
struct ipv6_mreq mreq = {
|
||||
.ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
|
||||
.ipv6mr_interface = index,
|
||||
};
|
||||
static int icmp6_bind_router_message(const struct icmp6_filter *filter,
|
||||
const struct ipv6_mreq *mreq) {
|
||||
int index = mreq->ipv6mr_interface;
|
||||
_cleanup_close_ int s = -1;
|
||||
char ifname[IF_NAMESIZE] = "";
|
||||
static const int zero = 0, one = 1, hops = 255;
|
||||
@ -56,9 +54,11 @@ int icmp6_bind_router_solicitation(int index) {
|
||||
if (s < 0)
|
||||
return -errno;
|
||||
|
||||
ICMP6_FILTER_SETBLOCKALL(&filter);
|
||||
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
|
||||
r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter));
|
||||
r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, filter, sizeof(*filter));
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq, sizeof(*mreq));
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
@ -78,7 +78,7 @@ int icmp6_bind_router_solicitation(int index) {
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
|
||||
r = setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, sizeof(hops));
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
@ -102,6 +102,32 @@ int icmp6_bind_router_solicitation(int index) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int icmp6_bind_router_solicitation(int index) {
|
||||
struct icmp6_filter filter = {};
|
||||
struct ipv6_mreq mreq = {
|
||||
.ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
|
||||
.ipv6mr_interface = index,
|
||||
};
|
||||
|
||||
ICMP6_FILTER_SETBLOCKALL(&filter);
|
||||
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
|
||||
|
||||
return icmp6_bind_router_message(&filter, &mreq);
|
||||
}
|
||||
|
||||
int icmp6_bind_router_advertisement(int index) {
|
||||
struct icmp6_filter filter = {};
|
||||
struct ipv6_mreq mreq = {
|
||||
.ipv6mr_multiaddr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
|
||||
.ipv6mr_interface = index,
|
||||
};
|
||||
|
||||
ICMP6_FILTER_SETBLOCKALL(&filter);
|
||||
ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
|
||||
|
||||
return icmp6_bind_router_message(&filter, &mreq);
|
||||
}
|
||||
|
||||
int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
|
||||
struct sockaddr_in6 dst = {
|
||||
.sin6_family = AF_INET6,
|
||||
@ -139,3 +165,74 @@ int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *dst,
|
||||
triple_timestamp *timestamp) {
|
||||
union {
|
||||
struct cmsghdr cmsghdr;
|
||||
uint8_t buf[CMSG_SPACE(sizeof(int)) + /* ttl */
|
||||
CMSG_SPACE(sizeof(struct timeval))];
|
||||
} control = {};
|
||||
struct iovec iov = {};
|
||||
union sockaddr_union sa = {};
|
||||
struct msghdr msg = {
|
||||
.msg_name = &sa.sa,
|
||||
.msg_namelen = sizeof(sa),
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = &control,
|
||||
.msg_controllen = sizeof(control),
|
||||
};
|
||||
struct cmsghdr *cmsg;
|
||||
ssize_t len;
|
||||
|
||||
iov.iov_base = buffer;
|
||||
iov.iov_len = size;
|
||||
|
||||
len = recvmsg(fd, &msg, MSG_DONTWAIT);
|
||||
if (len < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
return 0;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if ((size_t) len != size)
|
||||
return -EINVAL;
|
||||
|
||||
if (msg.msg_namelen == sizeof(struct sockaddr_in6) &&
|
||||
sa.in6.sin6_family == AF_INET6) {
|
||||
|
||||
*dst = sa.in6.sin6_addr;
|
||||
if (in_addr_is_link_local(AF_INET6, (union in_addr_union*) dst) <= 0)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
} else if (msg.msg_namelen > 0)
|
||||
return -EPFNOSUPPORT;
|
||||
|
||||
/* namelen == 0 only happens when running the test-suite over a socketpair */
|
||||
|
||||
assert(!(msg.msg_flags & MSG_CTRUNC));
|
||||
assert(!(msg.msg_flags & MSG_TRUNC));
|
||||
|
||||
CMSG_FOREACH(cmsg, &msg) {
|
||||
if (cmsg->cmsg_level == SOL_IPV6 &&
|
||||
cmsg->cmsg_type == IPV6_HOPLIMIT &&
|
||||
cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
|
||||
int hops = *(int*) CMSG_DATA(cmsg);
|
||||
|
||||
if (hops != 255)
|
||||
return -EMULTIHOP;
|
||||
}
|
||||
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SO_TIMESTAMP &&
|
||||
cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
|
||||
triple_timestamp_from_realtime(timestamp, timeval_load((struct timeval*) CMSG_DATA(cmsg)));
|
||||
}
|
||||
|
||||
if (!triple_timestamp_is_set(timestamp))
|
||||
triple_timestamp_get(timestamp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -21,5 +21,18 @@
|
||||
|
||||
#include <net/ethernet.h>
|
||||
|
||||
#include "time-util.h"
|
||||
|
||||
#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
|
||||
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } }
|
||||
|
||||
#define IN6ADDR_ALL_NODES_MULTICAST_INIT \
|
||||
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
|
||||
|
||||
int icmp6_bind_router_solicitation(int index);
|
||||
int icmp6_bind_router_advertisement(int index);
|
||||
int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr);
|
||||
int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *dst,
|
||||
triple_timestamp *timestamp);
|
||||
|
@ -18,6 +18,8 @@ sources = files('''
|
||||
ndisc-internal.h
|
||||
ndisc-router.h
|
||||
ndisc-router.c
|
||||
sd-radv.c
|
||||
radv-internal.h
|
||||
icmp6-util.h
|
||||
icmp6-util.c
|
||||
sd-dhcp6-client.c
|
||||
|
88
src/libsystemd-network/radv-internal.h
Normal file
88
src/libsystemd-network/radv-internal.h
Normal file
@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright (C) 2017 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-radv.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "list.h"
|
||||
#include "sparse-endian.h"
|
||||
|
||||
#define SD_RADV_DEFAULT_MIN_TIMEOUT_USEC (200*USEC_PER_SEC)
|
||||
#define SD_RADV_DEFAULT_MAX_TIMEOUT_USEC (600*USEC_PER_SEC)
|
||||
assert_cc(SD_RADV_DEFAULT_MIN_TIMEOUT_USEC <= SD_RADV_DEFAULT_MAX_TIMEOUT_USEC)
|
||||
|
||||
#define SD_RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC (16*USEC_PER_SEC)
|
||||
#define SD_RADV_MAX_INITIAL_RTR_ADVERTISEMENTS 3
|
||||
#define SD_RADV_MAX_FINAL_RTR_ADVERTISEMENTS 3
|
||||
#define SD_RADV_MIN_DELAY_BETWEEN_RAS 3
|
||||
#define SD_RADV_MAX_RA_DELAY_TIME_USEC (500*USEC_PER_MSEC)
|
||||
|
||||
enum RAdvState {
|
||||
SD_RADV_STATE_IDLE = 0,
|
||||
SD_RADV_STATE_ADVERTISING = 1,
|
||||
};
|
||||
typedef enum RAdvState RAdvState;
|
||||
|
||||
struct sd_radv {
|
||||
unsigned n_ref;
|
||||
RAdvState state;
|
||||
|
||||
int ifindex;
|
||||
|
||||
sd_event *event;
|
||||
int event_priority;
|
||||
|
||||
struct ether_addr mac_addr;
|
||||
uint8_t hop_limit;
|
||||
uint8_t flags;
|
||||
uint32_t mtu;
|
||||
uint16_t lifetime;
|
||||
|
||||
int fd;
|
||||
unsigned ra_sent;
|
||||
sd_event_source *recv_event_source;
|
||||
sd_event_source *timeout_event_source;
|
||||
|
||||
unsigned n_prefixes;
|
||||
LIST_HEAD(sd_radv_prefix, prefixes);
|
||||
};
|
||||
|
||||
struct sd_radv_prefix {
|
||||
unsigned n_ref;
|
||||
|
||||
struct {
|
||||
uint8_t type;
|
||||
uint8_t length;
|
||||
uint8_t prefixlen;
|
||||
uint8_t flags;
|
||||
be32_t valid_lifetime;
|
||||
be32_t preferred_lifetime;
|
||||
uint32_t reserved;
|
||||
struct in6_addr in6_addr;
|
||||
} _packed_ opt;
|
||||
|
||||
LIST_FIELDS(struct sd_radv_prefix, prefix);
|
||||
};
|
||||
|
||||
#define log_radv_full(level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "RADV: " fmt, ##__VA_ARGS__)
|
||||
#define log_radv_errno(error, fmt, ...) log_radv_full(LOG_DEBUG, error, fmt, ##__VA_ARGS__)
|
||||
#define log_radv_warning_errno(error, fmt, ...) log_radv_full(LOG_WARNING, error, fmt, ##__VA_ARGS__)
|
||||
#define log_radv(fmt, ...) log_radv_errno(0, fmt, ##__VA_ARGS__)
|
@ -222,23 +222,9 @@ static int ndisc_handle_datagram(sd_ndisc *nd, sd_ndisc_router *rt) {
|
||||
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_SPACE(sizeof(int)) + /* ttl */
|
||||
CMSG_SPACE(sizeof(struct timeval))];
|
||||
} control = {};
|
||||
struct iovec iov = {};
|
||||
union sockaddr_union sa = {};
|
||||
struct msghdr msg = {
|
||||
.msg_name = &sa.sa,
|
||||
.msg_namelen = sizeof(sa),
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = &control,
|
||||
.msg_controllen = sizeof(control),
|
||||
};
|
||||
struct cmsghdr *cmsg;
|
||||
ssize_t len, buflen;
|
||||
ssize_t buflen;
|
||||
int r;
|
||||
_cleanup_free_ char *addr = NULL;
|
||||
|
||||
assert(s);
|
||||
assert(nd);
|
||||
@ -252,66 +238,27 @@ static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userda
|
||||
if (!rt)
|
||||
return -ENOMEM;
|
||||
|
||||
iov.iov_base = NDISC_ROUTER_RAW(rt);
|
||||
iov.iov_len = rt->raw_size;
|
||||
r = icmp6_receive(fd, NDISC_ROUTER_RAW(rt), rt->raw_size, &rt->address,
|
||||
&rt->timestamp);
|
||||
if (r < 0) {
|
||||
switch (r) {
|
||||
case -EADDRNOTAVAIL:
|
||||
(void) in_addr_to_string(AF_INET6, (union in_addr_union*) &rt->address, &addr);
|
||||
log_ndisc("Received RA from non-link-local address %s. Ignoring", addr);
|
||||
break;
|
||||
|
||||
len = recvmsg(fd, &msg, MSG_DONTWAIT);
|
||||
if (len < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
return 0;
|
||||
case -EMULTIHOP:
|
||||
log_ndisc("Received RA with invalid hop limit. Ignoring.");
|
||||
break;
|
||||
|
||||
return log_ndisc_errno(errno, "Could not receive message from ICMPv6 socket: %m");
|
||||
}
|
||||
|
||||
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;
|
||||
case -EPFNOSUPPORT:
|
||||
log_ndisc("Received invalid source address from ICMPv6 socket.");
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* namelen == 0 only happens when running the test-suite over a socketpair */
|
||||
|
||||
assert(!(msg.msg_flags & MSG_CTRUNC));
|
||||
assert(!(msg.msg_flags & MSG_TRUNC));
|
||||
|
||||
CMSG_FOREACH(cmsg, &msg) {
|
||||
if (cmsg->cmsg_level == SOL_IPV6 &&
|
||||
cmsg->cmsg_type == IPV6_HOPLIMIT &&
|
||||
cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
|
||||
int hops = *(int*) CMSG_DATA(cmsg);
|
||||
|
||||
if (hops != 255) {
|
||||
log_ndisc("Received RA with invalid hop limit %d. Ignoring.", hops);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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((struct timeval*) CMSG_DATA(cmsg)));
|
||||
}
|
||||
|
||||
if (!triple_timestamp_is_set(&rt->timestamp))
|
||||
triple_timestamp_get(&rt->timestamp);
|
||||
|
||||
nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
|
||||
|
||||
return ndisc_handle_datagram(nd, rt);
|
||||
|
653
src/libsystemd-network/sd-radv.c
Normal file
653
src/libsystemd-network/sd-radv.c
Normal file
@ -0,0 +1,653 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright (C) 2017 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 <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/in6.h>
|
||||
|
||||
#include "sd-radv.h"
|
||||
|
||||
#include "macro.h"
|
||||
#include "alloc-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "icmp6-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "radv-internal.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-util.h"
|
||||
#include "util.h"
|
||||
#include "random-util.h"
|
||||
|
||||
_public_ int sd_radv_new(sd_radv **ret) {
|
||||
_cleanup_(sd_radv_unrefp) sd_radv *ra = NULL;
|
||||
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
ra = new0(sd_radv, 1);
|
||||
if (!ra)
|
||||
return -ENOMEM;
|
||||
|
||||
ra->n_ref = 1;
|
||||
ra->fd = -1;
|
||||
|
||||
LIST_HEAD_INIT(ra->prefixes);
|
||||
|
||||
*ret = ra;
|
||||
ra = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_attach_event(sd_radv *ra, sd_event *event, int64_t priority) {
|
||||
int r;
|
||||
|
||||
assert_return(ra, -EINVAL);
|
||||
assert_return(!ra->event, -EBUSY);
|
||||
|
||||
if (event)
|
||||
ra->event = sd_event_ref(event);
|
||||
else {
|
||||
r = sd_event_default(&ra->event);
|
||||
if (r < 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ra->event_priority = priority;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_detach_event(sd_radv *ra) {
|
||||
|
||||
assert_return(ra, -EINVAL);
|
||||
|
||||
ra->event = sd_event_unref(ra->event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ sd_event *sd_radv_get_event(sd_radv *ra) {
|
||||
assert_return(ra, NULL);
|
||||
|
||||
return ra->event;
|
||||
}
|
||||
|
||||
static void radv_reset(sd_radv *ra) {
|
||||
|
||||
ra->timeout_event_source =
|
||||
sd_event_source_unref(ra->timeout_event_source);
|
||||
|
||||
ra->recv_event_source =
|
||||
sd_event_source_unref(ra->recv_event_source);
|
||||
|
||||
ra->ra_sent = 0;
|
||||
}
|
||||
|
||||
_public_ sd_radv *sd_radv_ref(sd_radv *ra) {
|
||||
if (!ra)
|
||||
return NULL;
|
||||
|
||||
assert(ra->n_ref > 0);
|
||||
ra->n_ref++;
|
||||
|
||||
return ra;
|
||||
}
|
||||
|
||||
_public_ sd_radv *sd_radv_unref(sd_radv *ra) {
|
||||
if (!ra)
|
||||
return NULL;
|
||||
|
||||
assert(ra->n_ref > 0);
|
||||
ra->n_ref--;
|
||||
|
||||
if (ra->n_ref > 0)
|
||||
return NULL;
|
||||
|
||||
while (ra->prefixes) {
|
||||
sd_radv_prefix *p = ra->prefixes;
|
||||
|
||||
LIST_REMOVE(prefix, ra->prefixes, p);
|
||||
sd_radv_prefix_unref(p);
|
||||
}
|
||||
|
||||
radv_reset(ra);
|
||||
|
||||
sd_radv_detach_event(ra);
|
||||
return mfree(ra);
|
||||
}
|
||||
|
||||
static int radv_send(sd_radv *ra, const struct in6_addr *dst,
|
||||
const uint32_t router_lifetime) {
|
||||
static const struct ether_addr mac_zero = {};
|
||||
sd_radv_prefix *p;
|
||||
struct sockaddr_in6 dst_addr = {
|
||||
.sin6_family = AF_INET6,
|
||||
.sin6_addr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
|
||||
};
|
||||
struct nd_router_advert adv = {};
|
||||
struct {
|
||||
struct nd_opt_hdr opthdr;
|
||||
struct ether_addr slladdr;
|
||||
} _packed_ opt_mac = {
|
||||
.opthdr = {
|
||||
.nd_opt_type = ND_OPT_SOURCE_LINKADDR,
|
||||
.nd_opt_len = (sizeof(struct nd_opt_hdr) +
|
||||
sizeof(struct ether_addr) - 1) /8 + 1,
|
||||
},
|
||||
};
|
||||
struct nd_opt_mtu opt_mtu = {
|
||||
.nd_opt_mtu_type = ND_OPT_MTU,
|
||||
.nd_opt_mtu_len = 1,
|
||||
};
|
||||
/* Reserve iov space for RA header, linkaddr, MTU + N prefixes */
|
||||
struct iovec iov[3 + ra->n_prefixes];
|
||||
struct msghdr msg = {
|
||||
.msg_name = &dst_addr,
|
||||
.msg_namelen = sizeof(dst_addr),
|
||||
.msg_iov = iov,
|
||||
};
|
||||
|
||||
if (dst && !in_addr_is_null(AF_INET6, (union in_addr_union*) dst))
|
||||
dst_addr.sin6_addr = *dst;
|
||||
|
||||
adv.nd_ra_type = ND_ROUTER_ADVERT;
|
||||
adv.nd_ra_curhoplimit = ra->hop_limit;
|
||||
adv.nd_ra_flags_reserved = ra->flags;
|
||||
adv.nd_ra_router_lifetime = htobe16(router_lifetime);
|
||||
iov[msg.msg_iovlen].iov_base = &adv;
|
||||
iov[msg.msg_iovlen].iov_len = sizeof(adv);
|
||||
msg.msg_iovlen++;
|
||||
|
||||
/* MAC address is optional, either because the link does not use L2
|
||||
addresses or load sharing is desired. See RFC 4861, Section 4.2 */
|
||||
if (memcmp(&mac_zero, &ra->mac_addr, sizeof(mac_zero))) {
|
||||
opt_mac.slladdr = ra->mac_addr;
|
||||
iov[msg.msg_iovlen].iov_base = &opt_mac;
|
||||
iov[msg.msg_iovlen].iov_len = sizeof(opt_mac);
|
||||
msg.msg_iovlen++;
|
||||
}
|
||||
|
||||
if (ra->mtu) {
|
||||
opt_mtu.nd_opt_mtu_mtu = htobe32(ra->mtu);
|
||||
iov[msg.msg_iovlen].iov_base = &opt_mtu;
|
||||
iov[msg.msg_iovlen].iov_len = sizeof(opt_mtu);
|
||||
msg.msg_iovlen++;
|
||||
}
|
||||
|
||||
LIST_FOREACH(prefix, p, ra->prefixes) {
|
||||
iov[msg.msg_iovlen].iov_base = &p->opt;
|
||||
iov[msg.msg_iovlen].iov_len = sizeof(p->opt);
|
||||
msg.msg_iovlen++;
|
||||
}
|
||||
|
||||
if (sendmsg(ra->fd, &msg, 0) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
sd_radv *ra = userdata;
|
||||
_cleanup_free_ char *addr = NULL;
|
||||
struct in6_addr src;
|
||||
triple_timestamp timestamp;
|
||||
int r;
|
||||
ssize_t buflen;
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
|
||||
assert(s);
|
||||
assert(ra);
|
||||
assert(ra->event);
|
||||
|
||||
buflen = next_datagram_size_fd(fd);
|
||||
|
||||
if ((unsigned) buflen < sizeof(struct nd_router_solicit))
|
||||
return log_radv("Too short packet received");
|
||||
|
||||
buf = new0(char, buflen);
|
||||
if (!buf)
|
||||
return 0;
|
||||
|
||||
r = icmp6_receive(fd, buf, buflen, &src, ×tamp);
|
||||
if (r < 0) {
|
||||
switch (r) {
|
||||
case -EADDRNOTAVAIL:
|
||||
(void) in_addr_to_string(AF_INET6, (union in_addr_union*) &src, &addr);
|
||||
log_radv("Received RS from non-link-local address %s. Ignoring", addr);
|
||||
break;
|
||||
|
||||
case -EMULTIHOP:
|
||||
log_radv("Received RS with invalid hop limit. Ignoring.");
|
||||
break;
|
||||
|
||||
case -EPFNOSUPPORT:
|
||||
log_radv("Received invalid source address from ICMPv6 socket. Ignoring.");
|
||||
break;
|
||||
|
||||
default:
|
||||
log_radv_warning_errno(r, "Error receiving from ICMPv6 socket: %m");
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
(void) in_addr_to_string(AF_INET6, (union in_addr_union*) &src, &addr);
|
||||
|
||||
r = radv_send(ra, &src, ra->lifetime);
|
||||
if (r < 0)
|
||||
log_radv_warning_errno(r, "Unable to send solicited Router Advertisment to %s: %m", addr);
|
||||
else
|
||||
log_radv("Sent solicited Router Advertisement to %s", addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static usec_t radv_compute_timeout(usec_t min, usec_t max) {
|
||||
assert_return(min <= max, SD_RADV_DEFAULT_MIN_TIMEOUT_USEC);
|
||||
|
||||
return min + (random_u32() % (max - min));
|
||||
}
|
||||
|
||||
static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
int r;
|
||||
sd_radv *ra = userdata;
|
||||
usec_t min_timeout = SD_RADV_DEFAULT_MIN_TIMEOUT_USEC;
|
||||
usec_t max_timeout = SD_RADV_DEFAULT_MAX_TIMEOUT_USEC;
|
||||
usec_t time_now, timeout;
|
||||
char time_string[FORMAT_TIMESPAN_MAX];
|
||||
|
||||
assert(s);
|
||||
assert(ra);
|
||||
assert(ra->event);
|
||||
|
||||
ra->timeout_event_source = sd_event_source_unref(ra->timeout_event_source);
|
||||
|
||||
r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = radv_send(ra, NULL, ra->lifetime);
|
||||
if (r < 0)
|
||||
log_radv_warning_errno(r, "Unable to send Router Advertisement: %m");
|
||||
|
||||
/* RFC 4861, Section 6.2.4, sending initial Router Advertisements */
|
||||
if (ra->ra_sent < SD_RADV_MAX_INITIAL_RTR_ADVERTISEMENTS) {
|
||||
max_timeout = SD_RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC;
|
||||
min_timeout = SD_RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC / 3;
|
||||
}
|
||||
|
||||
timeout = radv_compute_timeout(min_timeout, max_timeout);
|
||||
|
||||
log_radv("Next Router Advertisement in %s",
|
||||
format_timespan(time_string, FORMAT_TIMESPAN_MAX,
|
||||
timeout, USEC_PER_SEC));
|
||||
|
||||
r = sd_event_add_time(ra->event, &ra->timeout_event_source,
|
||||
clock_boottime_or_monotonic(),
|
||||
time_now + timeout, MSEC_PER_SEC,
|
||||
radv_timeout, ra);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = sd_event_source_set_priority(ra->timeout_event_source,
|
||||
ra->event_priority);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = sd_event_source_set_description(ra->timeout_event_source,
|
||||
"radv-timeout");
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
ra->ra_sent++;
|
||||
|
||||
fail:
|
||||
if (r < 0)
|
||||
sd_radv_stop(ra);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_stop(sd_radv *ra) {
|
||||
int r;
|
||||
|
||||
assert_return(ra, -EINVAL);
|
||||
|
||||
log_radv("Stopping IPv6 Router Advertisement daemon");
|
||||
|
||||
/* RFC 4861, Section 6.2.5, send at least one Router Advertisement
|
||||
with zero lifetime */
|
||||
r = radv_send(ra, NULL, 0);
|
||||
if (r < 0)
|
||||
log_radv_warning_errno(r, "Unable to send last Router Advertisement with router lifetime set to zero: %m");
|
||||
|
||||
radv_reset(ra);
|
||||
ra->fd = safe_close(ra->fd);
|
||||
ra->state = SD_RADV_STATE_IDLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_start(sd_radv *ra) {
|
||||
int r = 0;
|
||||
|
||||
assert_return(ra, -EINVAL);
|
||||
assert_return(ra->event, -EINVAL);
|
||||
assert_return(ra->ifindex > 0, -EINVAL);
|
||||
|
||||
if (ra->state != SD_RADV_STATE_IDLE)
|
||||
return 0;
|
||||
|
||||
r = sd_event_add_time(ra->event, &ra->timeout_event_source,
|
||||
clock_boottime_or_monotonic(), 0, 0,
|
||||
radv_timeout, ra);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = sd_event_source_set_priority(ra->timeout_event_source,
|
||||
ra->event_priority);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
(void) sd_event_source_set_description(ra->timeout_event_source,
|
||||
"radv-timeout");
|
||||
|
||||
r = icmp6_bind_router_advertisement(ra->ifindex);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
ra->fd = r;
|
||||
|
||||
r = sd_event_add_io(ra->event, &ra->recv_event_source, ra->fd, EPOLLIN, radv_recv, ra);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = sd_event_source_set_priority(ra->recv_event_source, ra->event_priority);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
(void) sd_event_source_set_description(ra->recv_event_source, "radv-receive-message");
|
||||
|
||||
ra->state = SD_RADV_STATE_ADVERTISING;
|
||||
|
||||
log_radv("Started IPv6 Router Advertisement daemon");
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
radv_reset(ra);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_set_ifindex(sd_radv *ra, int ifindex) {
|
||||
assert_return(ra, -EINVAL);
|
||||
assert_return(ifindex >= -1, -EINVAL);
|
||||
|
||||
if (ra->state != SD_RADV_STATE_IDLE)
|
||||
return -EBUSY;
|
||||
|
||||
ra->ifindex = ifindex;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
|
||||
assert_return(ra, -EINVAL);
|
||||
|
||||
if (ra->state != SD_RADV_STATE_IDLE)
|
||||
return -EBUSY;
|
||||
|
||||
if (mac_addr)
|
||||
ra->mac_addr = *mac_addr;
|
||||
else
|
||||
zero(ra->mac_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) {
|
||||
assert_return(ra, -EINVAL);
|
||||
assert_return(mtu >= 1280, -EINVAL);
|
||||
|
||||
if (ra->state != SD_RADV_STATE_IDLE)
|
||||
return -EBUSY;
|
||||
|
||||
ra->mtu = mtu;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit) {
|
||||
assert_return(ra, -EINVAL);
|
||||
|
||||
if (ra->state != SD_RADV_STATE_IDLE)
|
||||
return -EBUSY;
|
||||
|
||||
ra->hop_limit = hop_limit;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_set_router_lifetime(sd_radv *ra, uint32_t router_lifetime) {
|
||||
assert_return(ra, -EINVAL);
|
||||
|
||||
if (ra->state != SD_RADV_STATE_IDLE)
|
||||
return -EBUSY;
|
||||
|
||||
/* RFC 4191, Section 2.2, "...If the Router Lifetime is zero, the
|
||||
preference value MUST be set to (00) by the sender..." */
|
||||
if (router_lifetime == 0 &&
|
||||
(ra->flags & (0x3 << 3)) != (SD_NDISC_PREFERENCE_MEDIUM << 3))
|
||||
return -ETIME;
|
||||
|
||||
ra->lifetime = router_lifetime;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_set_managed_information(sd_radv *ra, int managed) {
|
||||
assert_return(ra, -EINVAL);
|
||||
|
||||
if (ra->state != SD_RADV_STATE_IDLE)
|
||||
return -EBUSY;
|
||||
|
||||
SET_FLAG(ra->flags, ND_RA_FLAG_MANAGED, managed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_set_other_information(sd_radv *ra, int other) {
|
||||
assert_return(ra, -EINVAL);
|
||||
|
||||
if (ra->state != SD_RADV_STATE_IDLE)
|
||||
return -EBUSY;
|
||||
|
||||
SET_FLAG(ra->flags, ND_RA_FLAG_OTHER, other);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_set_preference(sd_radv *ra, unsigned preference) {
|
||||
int r = 0;
|
||||
|
||||
assert_return(ra, -EINVAL);
|
||||
assert_return(IN_SET(preference,
|
||||
SD_NDISC_PREFERENCE_LOW,
|
||||
SD_NDISC_PREFERENCE_MEDIUM,
|
||||
SD_NDISC_PREFERENCE_HIGH), -EINVAL);
|
||||
|
||||
ra->flags = (ra->flags & ~(0x3 << 3)) | (preference << 3);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
|
||||
sd_radv_prefix *cur;
|
||||
_cleanup_free_ char *addr_p = NULL;
|
||||
|
||||
assert_return(ra, -EINVAL);
|
||||
|
||||
if (!p)
|
||||
return -EINVAL;
|
||||
|
||||
LIST_FOREACH(prefix, cur, ra->prefixes) {
|
||||
int r;
|
||||
|
||||
r = in_addr_prefix_intersect(AF_INET6,
|
||||
(union in_addr_union*) &cur->opt.in6_addr,
|
||||
cur->opt.prefixlen,
|
||||
(union in_addr_union*) &p->opt.in6_addr,
|
||||
p->opt.prefixlen);
|
||||
if (r > 0) {
|
||||
_cleanup_free_ char *addr_cur = NULL;
|
||||
|
||||
(void) in_addr_to_string(AF_INET6,
|
||||
(union in_addr_union*) &cur->opt.in6_addr,
|
||||
&addr_cur);
|
||||
(void) in_addr_to_string(AF_INET6,
|
||||
(union in_addr_union*) &p->opt.in6_addr,
|
||||
&addr_p);
|
||||
|
||||
log_radv("IPv6 prefix %s/%u already configured, ignoring %s/%u",
|
||||
addr_cur, cur->opt.prefixlen,
|
||||
addr_p, p->opt.prefixlen);
|
||||
|
||||
return -EEXIST;
|
||||
}
|
||||
}
|
||||
|
||||
p = sd_radv_prefix_ref(p);
|
||||
|
||||
LIST_APPEND(prefix, ra->prefixes, p);
|
||||
|
||||
ra->n_prefixes++;
|
||||
|
||||
(void) in_addr_to_string(AF_INET6, (union in_addr_union*) &p->opt.in6_addr, &addr_p);
|
||||
log_radv("Added prefix %s/%d", addr_p, p->opt.prefixlen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_prefix_new(sd_radv_prefix **ret) {
|
||||
_cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
|
||||
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
p = new0(sd_radv_prefix, 1);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
p->n_ref = 1;
|
||||
|
||||
p->opt.type = ND_OPT_PREFIX_INFORMATION;
|
||||
p->opt.length = (sizeof(p->opt) - 1) /8 + 1;
|
||||
|
||||
p->opt.prefixlen = 64;
|
||||
|
||||
/* RFC 4861, Section 6.2.1 */
|
||||
SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_ONLINK, true);
|
||||
SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_AUTO, true);
|
||||
p->opt.preferred_lifetime = htobe32(604800);
|
||||
p->opt.valid_lifetime = htobe32(2592000);
|
||||
|
||||
LIST_INIT(prefix, p);
|
||||
|
||||
*ret = p;
|
||||
p = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ sd_radv_prefix *sd_radv_prefix_ref(sd_radv_prefix *p) {
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
assert(p->n_ref > 0);
|
||||
p->n_ref++;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
_public_ sd_radv_prefix *sd_radv_prefix_unref(sd_radv_prefix *p) {
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
assert(p->n_ref > 0);
|
||||
p->n_ref--;
|
||||
|
||||
if (p->n_ref > 0)
|
||||
return NULL;
|
||||
|
||||
return mfree(p);
|
||||
}
|
||||
|
||||
_public_ int sd_radv_prefix_set_prefix(sd_radv_prefix *p, struct in6_addr *in6_addr,
|
||||
unsigned char prefixlen) {
|
||||
assert_return(p, -EINVAL);
|
||||
assert_return(in6_addr, -EINVAL);
|
||||
|
||||
if (prefixlen < 3 || prefixlen > 128)
|
||||
return -EINVAL;
|
||||
|
||||
if (prefixlen > 64)
|
||||
/* unusual but allowed, log it */
|
||||
log_radv("Unusual prefix length %d greater than 64", prefixlen);
|
||||
|
||||
p->opt.in6_addr = *in6_addr;
|
||||
p->opt.prefixlen = prefixlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink) {
|
||||
assert_return(p, -EINVAL);
|
||||
|
||||
SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_ONLINK, onlink);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p,
|
||||
int address_autoconfiguration) {
|
||||
assert_return(p, -EINVAL);
|
||||
|
||||
SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_AUTO, address_autoconfiguration);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix *p,
|
||||
uint32_t valid_lifetime) {
|
||||
assert_return(p, -EINVAL);
|
||||
|
||||
p->opt.valid_lifetime = htobe32(valid_lifetime);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p,
|
||||
uint32_t preferred_lifetime) {
|
||||
assert_return(p, -EINVAL);
|
||||
|
||||
p->opt.preferred_lifetime = htobe32(preferred_lifetime);
|
||||
|
||||
return 0;
|
||||
}
|
359
src/libsystemd-network/test-ndisc-ra.c
Normal file
359
src/libsystemd-network/test-ndisc-ra.c
Normal file
@ -0,0 +1,359 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright (C) 2017 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 <arpa/inet.h>
|
||||
|
||||
#include "sd-radv.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 = { 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53 }
|
||||
};
|
||||
|
||||
static uint8_t advertisement[] = {
|
||||
/* ICMPv6 Router Advertisement, no checksum */
|
||||
0x86, 0x00, 0x00, 0x00, 0x40, 0xc0, 0x00, 0xb4,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* Source Link Layer Address Option */
|
||||
0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53,
|
||||
/* Prefix Information Option */
|
||||
0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4,
|
||||
0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00,
|
||||
0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* Prefix Information Option */
|
||||
0x03, 0x04, 0x40, 0xc0, 0x00, 0x27, 0x8d, 0x00,
|
||||
0x00, 0x09, 0x3a, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* Prefix Information Option */
|
||||
0x03, 0x04, 0x30, 0xc0, 0x00, 0x27, 0x8d, 0x00,
|
||||
0x00, 0x09, 0x3a, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x01, 0x0d, 0xad,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* Recursive DNS Server Option - not yet supported */
|
||||
0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
|
||||
0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
/* DNS Search List Option - not yet supported */
|
||||
0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
|
||||
0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
|
||||
0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static sd_event_source *test_hangcheck;
|
||||
static bool test_stopped;
|
||||
static int test_fd[2];
|
||||
static sd_event_source *recv_router_advertisement;
|
||||
static struct {
|
||||
struct in6_addr address;
|
||||
unsigned char prefixlen;
|
||||
uint32_t valid;
|
||||
uint32_t preferred;
|
||||
bool succesful;
|
||||
} prefix[] = {
|
||||
{ { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, 64,
|
||||
500, 440, true },
|
||||
{ { { { 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, 64,
|
||||
/* indicate default valid and preferred lifetimes for the test code */
|
||||
0, 0, true },
|
||||
{ { { { 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, 58,
|
||||
0, 0,
|
||||
/* indicate that this prefix already exists */
|
||||
false },
|
||||
{ { { { 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, 120,
|
||||
0, 0,
|
||||
/* indicate that this prefix already exists */
|
||||
false },
|
||||
{ { { { 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, 12,
|
||||
0, 0,
|
||||
/* indicate that this prefix already exists */
|
||||
false },
|
||||
{ { { { 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x01, 0x0d, 0xad,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, 48,
|
||||
0, 0, true },
|
||||
{ { { { 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x01, 0x0d, 0xad,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, 60,
|
||||
0, 0,
|
||||
/* indicate that this prefix already exists */
|
||||
false },
|
||||
};
|
||||
|
||||
static int test_rs_hangcheck(sd_event_source *s, uint64_t usec,
|
||||
void *userdata) {
|
||||
assert_se(false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_radv_prefix(void) {
|
||||
sd_radv_prefix *p;
|
||||
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
|
||||
assert_se(sd_radv_prefix_new(&p) >= 0);
|
||||
|
||||
assert_se(sd_radv_prefix_set_onlink(NULL, true) < 0);
|
||||
assert_se(sd_radv_prefix_set_onlink(p, true) >= 0);
|
||||
assert_se(sd_radv_prefix_set_onlink(p, false) >= 0);
|
||||
|
||||
assert_se(sd_radv_prefix_set_address_autoconfiguration(NULL, true) < 0);
|
||||
assert_se(sd_radv_prefix_set_address_autoconfiguration(p, true) >= 0);
|
||||
assert_se(sd_radv_prefix_set_address_autoconfiguration(p, false) >= 0);
|
||||
|
||||
assert_se(sd_radv_prefix_set_valid_lifetime(NULL, true) < 0);
|
||||
assert_se(sd_radv_prefix_set_valid_lifetime(p, ~0) >= 0);
|
||||
assert_se(sd_radv_prefix_set_valid_lifetime(p, 42) >= 0);
|
||||
assert_se(sd_radv_prefix_set_valid_lifetime(p, 0) >= 0);
|
||||
|
||||
assert_se(sd_radv_prefix_set_preferred_lifetime(NULL, true) < 0);
|
||||
assert_se(sd_radv_prefix_set_preferred_lifetime(p, ~0) >= 0);
|
||||
assert_se(sd_radv_prefix_set_preferred_lifetime(p, 42) >= 0);
|
||||
assert_se(sd_radv_prefix_set_preferred_lifetime(p, 0) >= 0);
|
||||
|
||||
assert_se(sd_radv_prefix_set_prefix(NULL, NULL, 0) < 0);
|
||||
assert_se(sd_radv_prefix_set_prefix(p, NULL, 0) < 0);
|
||||
|
||||
assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 64) >= 0);
|
||||
assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 0) < 0);
|
||||
assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 1) < 0);
|
||||
assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 2) < 0);
|
||||
assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 3) >= 0);
|
||||
assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 125) >= 0);
|
||||
assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 128) >= 0);
|
||||
assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 129) < 0);
|
||||
assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 255) < 0);
|
||||
|
||||
p = sd_radv_prefix_unref(p);
|
||||
assert_se(!p);
|
||||
}
|
||||
|
||||
static void test_radv(void) {
|
||||
sd_radv *ra;
|
||||
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
|
||||
assert_se(sd_radv_new(&ra) >= 0);
|
||||
assert_se(ra);
|
||||
|
||||
assert_se(sd_radv_set_ifindex(NULL, 0) < 0);
|
||||
assert_se(sd_radv_set_ifindex(ra, 0) >= 0);
|
||||
assert_se(sd_radv_set_ifindex(ra, -1) >= 0);
|
||||
assert_se(sd_radv_set_ifindex(ra, -2) < 0);
|
||||
assert_se(sd_radv_set_ifindex(ra, 42) >= 0);
|
||||
|
||||
assert_se(sd_radv_set_mac(NULL, NULL) < 0);
|
||||
assert_se(sd_radv_set_mac(ra, NULL) >= 0);
|
||||
assert_se(sd_radv_set_mac(ra, &mac_addr) >= 0);
|
||||
|
||||
assert_se(sd_radv_set_mtu(NULL, 0) < 0);
|
||||
assert_se(sd_radv_set_mtu(ra, 0) < 0);
|
||||
assert_se(sd_radv_set_mtu(ra, 1279) < 0);
|
||||
assert_se(sd_radv_set_mtu(ra, 1280) >= 0);
|
||||
assert_se(sd_radv_set_mtu(ra, ~0) >= 0);
|
||||
|
||||
assert_se(sd_radv_set_hop_limit(NULL, 0) < 0);
|
||||
assert_se(sd_radv_set_hop_limit(ra, 0) >= 0);
|
||||
assert_se(sd_radv_set_hop_limit(ra, ~0) >= 0);
|
||||
|
||||
assert_se(sd_radv_set_router_lifetime(NULL, 0) < 0);
|
||||
assert_se(sd_radv_set_router_lifetime(ra, 0) >= 0);
|
||||
assert_se(sd_radv_set_router_lifetime(ra, ~0) >= 0);
|
||||
|
||||
assert_se(sd_radv_set_preference(NULL, 0) < 0);
|
||||
assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_LOW) >= 0);
|
||||
assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_MEDIUM) >= 0);
|
||||
assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_HIGH) >= 0);
|
||||
assert_se(sd_radv_set_preference(ra, ~0) < 0);
|
||||
|
||||
assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_HIGH) >= 0);
|
||||
assert_se(sd_radv_set_router_lifetime(ra, 42000) >= 0);
|
||||
assert_se(sd_radv_set_router_lifetime(ra, 0) < 0);
|
||||
assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_MEDIUM) >= 0);
|
||||
assert_se(sd_radv_set_router_lifetime(ra, 0) >= 0);
|
||||
|
||||
assert_se(sd_radv_set_managed_information(NULL, true) < 0);
|
||||
assert_se(sd_radv_set_managed_information(ra, true) >= 0);
|
||||
assert_se(sd_radv_set_managed_information(ra, false) >= 0);
|
||||
|
||||
assert_se(sd_radv_set_other_information(NULL, true) < 0);
|
||||
assert_se(sd_radv_set_other_information(ra, true) >= 0);
|
||||
assert_se(sd_radv_set_other_information(ra, false) >= 0);
|
||||
|
||||
ra = sd_radv_unref(ra);
|
||||
assert_se(!ra);
|
||||
}
|
||||
|
||||
int icmp6_bind_router_solicitation(int index) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int icmp6_bind_router_advertisement(int index) {
|
||||
assert_se(index == 42);
|
||||
|
||||
return test_fd[1];
|
||||
}
|
||||
|
||||
int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int icmp6_receive(int fd, void *iov_base, size_t iov_len,
|
||||
struct in6_addr *dst, triple_timestamp *timestamp) {
|
||||
assert_se(read (fd, iov_base, iov_len) == (ssize_t)iov_len);
|
||||
|
||||
if (timestamp)
|
||||
triple_timestamp_get(timestamp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
sd_radv *ra = userdata;
|
||||
unsigned char buf[120];
|
||||
size_t i;
|
||||
|
||||
read(test_fd[0], &buf, sizeof(buf));
|
||||
|
||||
/* router lifetime must be zero when test is stopped */
|
||||
if (test_stopped) {
|
||||
advertisement[6] = 0x00;
|
||||
advertisement[7] = 0x00;
|
||||
}
|
||||
|
||||
printf ("Received Router Advertisement with lifetime %u\n",
|
||||
(advertisement[6] << 8) + advertisement[7]);
|
||||
|
||||
/* test only up to buf size, rest is not yet implemented */
|
||||
for (i = 0; i < sizeof(buf); i++) {
|
||||
printf("0x%02x", buf[i]);
|
||||
|
||||
assert_se(buf[i] == advertisement[i]);
|
||||
|
||||
if ((i + 1) % 8)
|
||||
printf(", ");
|
||||
else
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (test_stopped) {
|
||||
sd_event *e;
|
||||
|
||||
e = sd_radv_get_event(ra);
|
||||
sd_event_exit(e, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert_se(sd_radv_stop(ra) >= 0);
|
||||
test_stopped = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_ra(void) {
|
||||
sd_event *e;
|
||||
sd_radv *ra;
|
||||
usec_t time_now = now(clock_boottime_or_monotonic());
|
||||
unsigned int i;
|
||||
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
|
||||
assert_se(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, test_fd) >= 0);
|
||||
|
||||
assert_se(sd_event_new(&e) >= 0);
|
||||
|
||||
assert_se(sd_radv_new(&ra) >= 0);
|
||||
assert_se(ra);
|
||||
|
||||
assert_se(sd_radv_attach_event(ra, e, 0) >= 0);
|
||||
|
||||
assert_se(sd_radv_set_ifindex(ra, 42) >= 0);
|
||||
assert_se(sd_radv_set_mac(ra, &mac_addr) >= 0);
|
||||
assert_se(sd_radv_set_router_lifetime(ra, 180) >= 0);
|
||||
assert_se(sd_radv_set_hop_limit(ra, 64) >= 0);
|
||||
assert_se(sd_radv_set_managed_information(ra, true) >= 0);
|
||||
assert_se(sd_radv_set_other_information(ra, true) >= 0);
|
||||
|
||||
for (i = 0; i < ELEMENTSOF(prefix); i++) {
|
||||
sd_radv_prefix *p;
|
||||
|
||||
printf("Test prefix %u\n", i);
|
||||
assert_se(sd_radv_prefix_new(&p) >= 0);
|
||||
|
||||
assert_se(sd_radv_prefix_set_prefix(p, &prefix[i].address,
|
||||
prefix[i].prefixlen) >= 0);
|
||||
if (prefix[i].valid)
|
||||
assert_se(sd_radv_prefix_set_valid_lifetime(p, prefix[i].valid) >= 0);
|
||||
if (prefix[i].preferred)
|
||||
assert_se(sd_radv_prefix_set_preferred_lifetime(p, prefix[i].preferred) >= 0);
|
||||
|
||||
assert_se((sd_radv_add_prefix(ra, p) >= 0) == prefix[i].succesful);
|
||||
assert_se(sd_radv_add_prefix(ra, p) < 0);
|
||||
|
||||
p = sd_radv_prefix_unref(p);
|
||||
assert_se(!p);
|
||||
}
|
||||
|
||||
assert_se(sd_event_add_io(e, &recv_router_advertisement, test_fd[0],
|
||||
EPOLLIN, radv_recv, ra) >= 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_radv_start(ra) >= 0);
|
||||
|
||||
sd_event_loop(e);
|
||||
|
||||
test_hangcheck = sd_event_source_unref(test_hangcheck);
|
||||
|
||||
ra = sd_radv_unref(ra);
|
||||
assert_se(!ra);
|
||||
|
||||
close(test_fd[0]);
|
||||
|
||||
sd_event_unref(e);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
log_set_max_level(LOG_DEBUG);
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
test_radv_prefix();
|
||||
test_radv();
|
||||
test_ra();
|
||||
|
||||
printf("* done\n");
|
||||
return 0;
|
||||
}
|
@ -193,6 +193,21 @@ int icmp6_bind_router_solicitation(int index) {
|
||||
return test_fd[0];
|
||||
}
|
||||
|
||||
int icmp6_bind_router_advertisement(int index) {
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int icmp6_receive(int fd, void *iov_base, size_t iov_len,
|
||||
struct in6_addr *dst, triple_timestamp *timestamp) {
|
||||
assert (read (fd, iov_base, iov_len) == (ssize_t)iov_len);
|
||||
|
||||
if (timestamp)
|
||||
triple_timestamp_get(timestamp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_ra(uint8_t flags) {
|
||||
uint8_t advertisement[] = {
|
||||
0x86, 0x00, 0xde, 0x83, 0x40, 0xc0, 0x00, 0xb4,
|
||||
|
@ -54,6 +54,8 @@ sources = files('''
|
||||
networkd-manager.h
|
||||
networkd-ndisc.c
|
||||
networkd-ndisc.h
|
||||
networkd-radv.c
|
||||
networkd-radv.h
|
||||
networkd-network-bus.c
|
||||
networkd-network.c
|
||||
networkd-network.h
|
||||
|
@ -932,3 +932,252 @@ bool address_is_ready(const Address *a) {
|
||||
|
||||
return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED));
|
||||
}
|
||||
|
||||
int config_parse_router_preference(const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
Network *network = userdata;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (streq(rvalue, "high"))
|
||||
network->router_preference = SD_NDISC_PREFERENCE_HIGH;
|
||||
else if (STR_IN_SET(rvalue, "medium", "normal", "default"))
|
||||
network->router_preference = SD_NDISC_PREFERENCE_MEDIUM;
|
||||
else if (streq(rvalue, "low"))
|
||||
network->router_preference = SD_NDISC_PREFERENCE_LOW;
|
||||
else
|
||||
log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router preference '%s' is invalid, ignoring assignment: %m", rvalue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void prefix_free(Prefix *prefix) {
|
||||
if (!prefix)
|
||||
return;
|
||||
|
||||
if (prefix->network) {
|
||||
LIST_REMOVE(prefixes, prefix->network->static_prefixes, prefix);
|
||||
assert(prefix->network->n_static_prefixes > 0);
|
||||
prefix->network->n_static_prefixes--;
|
||||
|
||||
if (prefix->section)
|
||||
hashmap_remove(prefix->network->prefixes_by_section,
|
||||
prefix->section);
|
||||
}
|
||||
|
||||
prefix->radv_prefix = sd_radv_prefix_unref(prefix->radv_prefix);
|
||||
|
||||
free(prefix);
|
||||
}
|
||||
|
||||
int prefix_new(Prefix **ret) {
|
||||
Prefix *prefix = NULL;
|
||||
|
||||
prefix = new0(Prefix, 1);
|
||||
if (!prefix)
|
||||
return -ENOMEM;
|
||||
|
||||
if (sd_radv_prefix_new(&prefix->radv_prefix) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = prefix;
|
||||
prefix = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prefix_new_static(Network *network, const char *filename,
|
||||
unsigned section_line, Prefix **ret) {
|
||||
_cleanup_network_config_section_free_ NetworkConfigSection *n = NULL;
|
||||
_cleanup_prefix_free_ Prefix *prefix = NULL;
|
||||
int r;
|
||||
|
||||
assert(network);
|
||||
assert(ret);
|
||||
assert(!!filename == (section_line > 0));
|
||||
|
||||
if (filename) {
|
||||
r = network_config_section_new(filename, section_line, &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (section_line) {
|
||||
prefix = hashmap_get(network->prefixes_by_section, n);
|
||||
if (prefix) {
|
||||
*ret = prefix;
|
||||
prefix = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r = prefix_new(&prefix);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (filename) {
|
||||
prefix->section = n;
|
||||
n = NULL;
|
||||
|
||||
r = hashmap_put(network->prefixes_by_section, prefix->section,
|
||||
prefix);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
prefix->network = network;
|
||||
LIST_APPEND(prefixes, network->static_prefixes, prefix);
|
||||
network->n_static_prefixes++;
|
||||
|
||||
*ret = prefix;
|
||||
prefix = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_prefix(const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
Network *network = userdata;
|
||||
_cleanup_prefix_free_ Prefix *p = NULL;
|
||||
uint8_t prefixlen = 64;
|
||||
union in_addr_union in6addr;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
r = prefix_new_static(network, filename, section_line, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen) < 0)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
log_syntax(unit, LOG_INFO, filename, line, r, "Found prefix %s", rvalue);
|
||||
|
||||
p = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_prefix_flags(const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
Network *network = userdata;
|
||||
_cleanup_prefix_free_ Prefix *p = NULL;
|
||||
int r, val;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
r = prefix_new_static(network, filename, section_line, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = parse_boolean(rvalue);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address flag, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
val = r;
|
||||
|
||||
if (streq(lvalue, "OnLink"))
|
||||
r = sd_radv_prefix_set_onlink(p->radv_prefix, val);
|
||||
else if (streq(lvalue, "AddressAutoconfiguration"))
|
||||
r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, val);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_prefix_lifetime(const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
Network *network = userdata;
|
||||
_cleanup_prefix_free_ Prefix *p = NULL;
|
||||
usec_t usec;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
r = prefix_new_static(network, filename, section_line, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = parse_sec(rvalue, &usec);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* a value of 0xffffffff represents infinity, 0x0 means this host is
|
||||
not a router */
|
||||
if (streq(lvalue, "PreferredLifetimeSec"))
|
||||
r = sd_radv_prefix_set_preferred_lifetime(p->radv_prefix,
|
||||
(usec + USEC_PER_SEC - 1) / USEC_PER_SEC);
|
||||
else if (streq(lvalue, "ValidLifetimeSec"))
|
||||
r = sd_radv_prefix_set_valid_lifetime(p->radv_prefix,
|
||||
(usec + USEC_PER_SEC - 1) / USEC_PER_SEC);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = NULL;
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "in-addr-util.h"
|
||||
|
||||
typedef struct Address Address;
|
||||
typedef struct Prefix Prefix;
|
||||
|
||||
#include "networkd-link.h"
|
||||
#include "networkd-network.h"
|
||||
@ -35,6 +36,15 @@ typedef struct Network Network;
|
||||
typedef struct Link Link;
|
||||
typedef struct NetworkConfigSection NetworkConfigSection;
|
||||
|
||||
struct Prefix {
|
||||
Network *network;
|
||||
NetworkConfigSection *section;
|
||||
|
||||
sd_radv_prefix *radv_prefix;
|
||||
|
||||
LIST_FIELDS(Prefix, prefixes);
|
||||
};
|
||||
|
||||
struct Address {
|
||||
Network *network;
|
||||
NetworkConfigSection *section;
|
||||
@ -79,8 +89,20 @@ bool address_is_ready(const Address *a);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free);
|
||||
#define _cleanup_address_free_ _cleanup_(address_freep)
|
||||
|
||||
int prefix_new(Prefix **ret);
|
||||
void prefix_free(Prefix *prefix);
|
||||
int prefix_new_static(Network *network, const char *filename, unsigned section,
|
||||
Prefix **ret);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Prefix*, prefix_free);
|
||||
#define _cleanup_prefix_free_ _cleanup_(prefix_freep)
|
||||
|
||||
int config_parse_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_broadcast(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_label(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_address_flags(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_router_preference(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_prefix(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_prefix_flags(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_prefix_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "networkd-lldp-tx.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-ndisc.h"
|
||||
#include "networkd-radv.h"
|
||||
#include "set.h"
|
||||
#include "socket-util.h"
|
||||
#include "stdio-util.h"
|
||||
@ -119,6 +120,15 @@ static bool link_ipv6_enabled(Link *link) {
|
||||
return link_ipv6ll_enabled(link) || network_has_static_ipv6_addresses(link->network);
|
||||
}
|
||||
|
||||
static bool link_radv_enabled(Link *link) {
|
||||
assert(link);
|
||||
|
||||
if (!link_ipv6ll_enabled(link))
|
||||
return false;
|
||||
|
||||
return link->network->router_prefix_delegation;
|
||||
}
|
||||
|
||||
static bool link_lldp_rx_enabled(Link *link) {
|
||||
assert(link);
|
||||
|
||||
@ -521,6 +531,7 @@ static void link_free(Link *link) {
|
||||
sd_ipv4ll_unref(link->ipv4ll);
|
||||
sd_dhcp6_client_unref(link->dhcp6_client);
|
||||
sd_ndisc_unref(link->ndisc);
|
||||
sd_radv_unref(link->radv);
|
||||
|
||||
if (link->manager)
|
||||
hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex));
|
||||
@ -640,6 +651,12 @@ static int link_stop_clients(Link *link) {
|
||||
r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m");
|
||||
}
|
||||
|
||||
if (link->radv) {
|
||||
k = sd_radv_stop(link->radv);
|
||||
if (k < 0)
|
||||
r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Advertisement: %m");
|
||||
}
|
||||
|
||||
link_lldp_emit_stop(link);
|
||||
return r;
|
||||
}
|
||||
@ -1554,6 +1571,17 @@ static int link_acquire_ipv6_conf(Link *link) {
|
||||
return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m");
|
||||
}
|
||||
|
||||
if (link_radv_enabled(link)) {
|
||||
assert(link->radv);
|
||||
assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
|
||||
|
||||
log_link_debug(link, "Starting IPv6 Router Advertisements");
|
||||
|
||||
r = sd_radv_start(link->radv);
|
||||
if (r < 0 && r != -EBUSY)
|
||||
return log_link_warning_errno(link, r, "Could not start IPv6 Router Advertisement: %m");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2562,6 +2590,12 @@ static int link_configure(Link *link) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if (link_radv_enabled(link)) {
|
||||
r = radv_configure(link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (link_lldp_rx_enabled(link)) {
|
||||
r = sd_lldp_new(&link->lldp);
|
||||
if (r < 0)
|
||||
@ -3098,6 +3132,12 @@ int link_update(Link *link, sd_netlink_message *m) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (link->radv) {
|
||||
r = sd_radv_set_mtu(link->radv, link->mtu);
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Could not set MTU for Router Advertisement: %m");
|
||||
}
|
||||
}
|
||||
|
||||
/* The kernel may broadcast NEWLINK messages without the MAC address
|
||||
@ -3166,6 +3206,12 @@ int link_update(Link *link, sd_netlink_message *m) {
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Could not update DHCPv6 DUID: %m");
|
||||
}
|
||||
|
||||
if (link->radv) {
|
||||
r = sd_radv_set_mac(link->radv, &link->mac);
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Could not update MAC for Router Advertisement: %m");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "sd-ipv4ll.h"
|
||||
#include "sd-lldp.h"
|
||||
#include "sd-ndisc.h"
|
||||
#include "sd-radv.h"
|
||||
#include "sd-netlink.h"
|
||||
|
||||
#include "list.h"
|
||||
@ -117,6 +118,8 @@ typedef struct Link {
|
||||
Set *ndisc_rdnss;
|
||||
Set *ndisc_dnssl;
|
||||
|
||||
sd_radv *radv;
|
||||
|
||||
sd_dhcp6_client *dhcp6_client;
|
||||
bool rtnl_extended_attrs;
|
||||
|
||||
|
@ -137,6 +137,16 @@ BridgeFDB.VLANId, config_parse_fdb_vlan_id,
|
||||
BridgeVLAN.PVID, config_parse_brvlan_pvid, 0, 0
|
||||
BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0
|
||||
BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0
|
||||
Network.IPv6PrefixDelegation, config_parse_bool, 0, offsetof(Network, router_prefix_delegation)
|
||||
IPv6PrefixDelegation.RouterLifetimeSec, config_parse_sec, 0, offsetof(Network, router_lifetime_usec)
|
||||
IPv6PrefixDelegation.Managed, config_parse_bool, 0, offsetof(Network, router_managed)
|
||||
IPv6PrefixDelegation.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information)
|
||||
IPv6PrefixDelegation.RouterPreference, config_parse_router_preference, 0, 0
|
||||
IPv6Prefix.Prefix, config_parse_prefix, 0, 0
|
||||
IPv6Prefix.OnLink, config_parse_prefix_flags, 0, 0
|
||||
IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0
|
||||
IPv6Prefix.ValidLifetimeSec, config_parse_prefix_lifetime, 0, 0
|
||||
IPv6Prefix.PreferredLifetimeSec, config_parse_prefix_lifetime, 0, 0
|
||||
/* backwards compatibility: do not add new entries to this section */
|
||||
Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local)
|
||||
DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
|
||||
|
@ -115,6 +115,7 @@ static int network_load_one(Manager *manager, const char *filename) {
|
||||
LIST_HEAD_INIT(network->static_fdb_entries);
|
||||
LIST_HEAD_INIT(network->ipv6_proxy_ndp_addresses);
|
||||
LIST_HEAD_INIT(network->address_labels);
|
||||
LIST_HEAD_INIT(network->static_prefixes);
|
||||
|
||||
network->stacked_netdevs = hashmap_new(&string_hash_ops);
|
||||
if (!network->stacked_netdevs)
|
||||
@ -134,6 +135,10 @@ static int network_load_one(Manager *manager, const char *filename) {
|
||||
|
||||
network->address_labels_by_section = hashmap_new(&network_config_hash_ops);
|
||||
if (!network->address_labels_by_section)
|
||||
log_oom();
|
||||
|
||||
network->prefixes_by_section = hashmap_new(&network_config_hash_ops);
|
||||
if (!network->prefixes_by_section)
|
||||
return log_oom();
|
||||
|
||||
network->filename = strdup(filename);
|
||||
@ -207,7 +212,9 @@ static int network_load_one(Manager *manager, const char *filename) {
|
||||
"IPv6NDPProxyAddress\0"
|
||||
"Bridge\0"
|
||||
"BridgeFDB\0"
|
||||
"BridgeVLAN\0",
|
||||
"BridgeVLAN\0"
|
||||
"IPv6PrefixDelegation\0"
|
||||
"IPv6Prefix\0",
|
||||
config_item_perf_lookup, network_network_gperf_lookup,
|
||||
false, network);
|
||||
if (r < 0)
|
||||
@ -279,6 +286,7 @@ void network_free(Network *network) {
|
||||
FdbEntry *fdb_entry;
|
||||
IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
|
||||
AddressLabel *label;
|
||||
Prefix *prefix;
|
||||
Iterator i;
|
||||
|
||||
if (!network)
|
||||
@ -329,10 +337,14 @@ void network_free(Network *network) {
|
||||
while ((label = network->address_labels))
|
||||
address_label_free(label);
|
||||
|
||||
while ((prefix = network->static_prefixes))
|
||||
prefix_free(prefix);
|
||||
|
||||
hashmap_free(network->addresses_by_section);
|
||||
hashmap_free(network->routes_by_section);
|
||||
hashmap_free(network->fdb_entries_by_section);
|
||||
hashmap_free(network->address_labels_by_section);
|
||||
hashmap_free(network->prefixes_by_section);
|
||||
|
||||
if (network->manager) {
|
||||
if (network->manager->networks)
|
||||
|
@ -158,6 +158,13 @@ struct Network {
|
||||
AddressFamilyBoolean link_local;
|
||||
bool ipv4ll_route;
|
||||
|
||||
/* IPv6 prefix delegation support */
|
||||
bool router_prefix_delegation;
|
||||
usec_t router_lifetime_usec;
|
||||
uint8_t router_preference;
|
||||
bool router_managed;
|
||||
bool router_other_information;
|
||||
|
||||
/* Bridge Support */
|
||||
bool use_bpdu;
|
||||
bool hairpin;
|
||||
@ -205,17 +212,20 @@ struct Network {
|
||||
LIST_HEAD(FdbEntry, static_fdb_entries);
|
||||
LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses);
|
||||
LIST_HEAD(AddressLabel, address_labels);
|
||||
LIST_HEAD(Prefix, static_prefixes);
|
||||
|
||||
unsigned n_static_addresses;
|
||||
unsigned n_static_routes;
|
||||
unsigned n_static_fdb_entries;
|
||||
unsigned n_ipv6_proxy_ndp_addresses;
|
||||
unsigned n_address_labels;
|
||||
unsigned n_static_prefixes;
|
||||
|
||||
Hashmap *addresses_by_section;
|
||||
Hashmap *routes_by_section;
|
||||
Hashmap *fdb_entries_by_section;
|
||||
Hashmap *address_labels_by_section;
|
||||
Hashmap *prefixes_by_section;
|
||||
|
||||
struct in_addr_data *dns;
|
||||
unsigned n_dns;
|
||||
|
77
src/network/networkd-radv.c
Normal file
77
src/network/networkd-radv.c
Normal file
@ -0,0 +1,77 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright (C) 2017 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 <arpa/inet.h>
|
||||
|
||||
#include "networkd-address.h"
|
||||
#include "networkd-radv.h"
|
||||
#include "sd-radv.h"
|
||||
|
||||
int radv_configure(Link *link) {
|
||||
int r;
|
||||
Prefix *p;
|
||||
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
|
||||
r = sd_radv_new(&link->radv);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_radv_attach_event(link->radv, NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_radv_set_mac(link->radv, &link->mac);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_radv_set_ifindex(link->radv, link->ifindex);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_radv_set_managed_information(link->radv, link->network->router_managed);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_radv_set_other_information(link->radv, link->network->router_other_information);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_radv_set_router_lifetime(link->radv,
|
||||
link->network->router_lifetime_usec);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (link->network->router_lifetime_usec > 0) {
|
||||
r = sd_radv_set_preference(link->radv,
|
||||
link->network->router_preference);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
LIST_FOREACH(prefixes, p, link->network->static_prefixes) {
|
||||
r = sd_radv_add_prefix(link->radv, p->radv_prefix);
|
||||
if (r != -EEXIST && r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
24
src/network/networkd-radv.h
Normal file
24
src/network/networkd-radv.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2017 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 "networkd-link.h"
|
||||
|
||||
int radv_configure(Link *link);
|
81
src/systemd/sd-radv.h
Normal file
81
src/systemd/sd-radv.h
Normal file
@ -0,0 +1,81 @@
|
||||
#ifndef foosdradvfoo
|
||||
#define foosdradvfoo
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright (C) 2017 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 <inttypes.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "sd-ndisc.h"
|
||||
|
||||
#include "sd-event.h"
|
||||
|
||||
#include "_sd-common.h"
|
||||
|
||||
_SD_BEGIN_DECLARATIONS;
|
||||
|
||||
typedef struct sd_radv sd_radv;
|
||||
typedef struct sd_radv_prefix sd_radv_prefix;
|
||||
|
||||
/* Router Advertisment */
|
||||
int sd_radv_new(sd_radv **ret);
|
||||
sd_radv *sd_radv_ref(sd_radv *ra);
|
||||
sd_radv *sd_radv_unref(sd_radv *ra);
|
||||
|
||||
int sd_radv_attach_event(sd_radv *ra, sd_event *event, int64_t priority);
|
||||
int sd_radv_detach_event(sd_radv *nd);
|
||||
sd_event *sd_radv_get_event(sd_radv *ra);
|
||||
|
||||
int sd_radv_start(sd_radv *ra);
|
||||
int sd_radv_stop(sd_radv *ra);
|
||||
|
||||
int sd_radv_set_ifindex(sd_radv *ra, int interface_index);
|
||||
int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr);
|
||||
int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu);
|
||||
int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit);
|
||||
int sd_radv_set_router_lifetime(sd_radv *ra, uint32_t router_lifetime);
|
||||
int sd_radv_set_managed_information(sd_radv *ra, int managed);
|
||||
int sd_radv_set_other_information(sd_radv *ra, int other);
|
||||
int sd_radv_set_preference(sd_radv *ra, unsigned preference);
|
||||
int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p);
|
||||
|
||||
/* Advertised prefixes */
|
||||
int sd_radv_prefix_new(sd_radv_prefix **ret);
|
||||
sd_radv_prefix *sd_radv_prefix_ref(sd_radv_prefix *ra);
|
||||
sd_radv_prefix *sd_radv_prefix_unref(sd_radv_prefix *ra);
|
||||
|
||||
int sd_radv_prefix_set_prefix(sd_radv_prefix *p, struct in6_addr *in6_addr,
|
||||
unsigned char prefixlen);
|
||||
int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink);
|
||||
int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p,
|
||||
int address_autoconfiguration);
|
||||
int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix *p,
|
||||
uint32_t valid_lifetime);
|
||||
int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p,
|
||||
uint32_t preferred_lifetime);
|
||||
|
||||
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv, sd_radv_unref);
|
||||
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv_prefix, sd_radv_prefix_unref);
|
||||
|
||||
_SD_END_DECLARATIONS;
|
||||
|
||||
#endif
|
@ -862,6 +862,13 @@ tests += [
|
||||
libsystemd_network],
|
||||
[]],
|
||||
|
||||
[['src/libsystemd-network/test-ndisc-ra.c',
|
||||
'src/libsystemd-network/icmp6-util.h',
|
||||
'src/systemd/sd-ndisc.h'],
|
||||
[libshared,
|
||||
libsystemd_network],
|
||||
[]],
|
||||
|
||||
[['src/libsystemd-network/test-dhcp6-client.c',
|
||||
'src/libsystemd-network/dhcp-identifier.h',
|
||||
'src/libsystemd-network/dhcp-identifier.c',
|
||||
|
Loading…
Reference in New Issue
Block a user