From 77baf5aee68de59b21592c93d05ddb75e3fe65c2 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Fri, 12 May 2017 16:48:37 +0300 Subject: [PATCH] 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. --- src/libsystemd-network/radv-internal.h | 1 + src/libsystemd-network/sd-radv.c | 75 +++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h index 7dcc3e0451..536923d7df 100644 --- a/src/libsystemd-network/radv-internal.h +++ b/src/libsystemd-network/radv-internal.h @@ -56,6 +56,7 @@ struct sd_radv { uint32_t mtu; uint16_t lifetime; + int fd; unsigned ra_sent; sd_event_source *timeout_event_source; 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 #include #include +#include #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, + }; - return -ENOSYS; + 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 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");