summaryrefslogtreecommitdiff
path: root/src/libsystemd-network/sd-radv.c
diff options
context:
space:
mode:
authorPatrik Flykt <patrik.flykt@linux.intel.com>2017-05-12 16:48:37 +0300
committerPatrik Flykt <patrik.flykt@linux.intel.com>2017-05-15 14:49:50 +0300
commit77baf5aee68de59b21592c93d05ddb75e3fe65c2 (patch)
tree6221730da73214024873dd7ee78633958115644b /src/libsystemd-network/sd-radv.c
parente2e8122838565670b34f0dbed320c3b2a949f135 (diff)
downloadsystemd-77baf5aee68de59b21592c93d05ddb75e3fe65c2.tar.gz
systemd-77baf5aee68de59b21592c93d05ddb75e3fe65c2.tar.bz2
systemd-77baf5aee68de59b21592c93d05ddb75e3fe65c2.zip
sd-radv: Send Router Advertisments
Create and remove the ICMPv6 Router Advertisement socket file descriptor and implement Router Advertisment sending. As not all options are mandatory, use IO vectors to point to the included options and the prefix information.
Diffstat (limited to 'src/libsystemd-network/sd-radv.c')
-rw-r--r--src/libsystemd-network/sd-radv.c75
1 files changed, 74 insertions, 1 deletions
diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c
index 71054eb5fe..1b60af0dd7 100644
--- a/src/libsystemd-network/sd-radv.c
+++ b/src/libsystemd-network/sd-radv.c
@@ -20,6 +20,7 @@
#include <netinet/icmp6.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <linux/in6.h>
#include "sd-radv.h"
@@ -44,6 +45,7 @@ _public_ int sd_radv_new(sd_radv **ret) {
return -ENOMEM;
ra->n_ref = 1;
+ ra->fd = -1;
LIST_HEAD_INIT(ra->prefixes);
@@ -129,8 +131,71 @@ _public_ sd_radv *sd_radv_unref(sd_radv *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)
+ 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 -ENOSYS;
+ return 0;
}
static usec_t radv_compute_timeout(usec_t min, usec_t max) {
@@ -213,6 +278,7 @@ _public_ int sd_radv_stop(sd_radv *ra) {
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;
@@ -242,6 +308,13 @@ _public_ int sd_radv_start(sd_radv *ra) {
(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 = 0;
+
ra->state = SD_RADV_STATE_ADVERTISING;
log_radv("Started IPv6 Router Advertisement daemon");