]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-radv: Send Router Advertisments
authorPatrik Flykt <patrik.flykt@linux.intel.com>
Fri, 12 May 2017 13:48:37 +0000 (16:48 +0300)
committerPatrik Flykt <patrik.flykt@linux.intel.com>
Mon, 15 May 2017 11:49:50 +0000 (14:49 +0300)
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
src/libsystemd-network/sd-radv.c

index 7dcc3e045129801a32cc7e3b5d99a392c63c28c1..536923d7dfb9ccd540aed82400ae64a615b34f39 100644 (file)
@@ -56,6 +56,7 @@ struct sd_radv {
         uint32_t mtu;
         uint16_t lifetime;
 
+        int fd;
         unsigned ra_sent;
         sd_event_source *timeout_event_source;
 
index 71054eb5fedb1ca730019407cba23a8f63f69c63..1b60af0dd7607fc2ae1862699d77e20c0cf895a4 100644 (file)
@@ -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");