]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-radv: Add Router Advertisement functionality
authorPatrik Flykt <patrik.flykt@linux.intel.com>
Fri, 12 May 2017 13:48:31 +0000 (16:48 +0300)
committerPatrik Flykt <patrik.flykt@linux.intel.com>
Mon, 15 May 2017 11:49:50 +0000 (14:49 +0300)
Add Router Advertisement header files, data structures and core
functionality that is quite similar to other parts of networkd.

src/libsystemd-network/radv-internal.h
src/libsystemd-network/sd-radv.c
src/systemd/sd-radv.h

index 0f92c8b55ce14d4a100ab7f77285a4384c638fd1..40a5506b953523874f94cea1115135dc1208bb2f 100644 (file)
 #include "list.h"
 #include "sparse-endian.h"
 
+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;
+
+        unsigned n_prefixes;
+        LIST_HEAD(sd_radv_prefix, prefixes);
+};
+
 struct sd_radv_prefix {
         unsigned n_ref;
 
index 54f126e0f6580aac21b8d54b73fb91122da281ff..9336132e1951c48fae7d92c91f21b5d96a6e5e5c 100644 (file)
 
 #include <netinet/icmp6.h>
 #include <netinet/in.h>
+#include <arpa/inet.h>
 
 #include "sd-radv.h"
 
+#include "macro.h"
 #include "alloc-util.h"
 #include "fd-util.h"
 #include "icmp6-util.h"
 #include "string-util.h"
 #include "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;
+
+        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;
+}
+
+_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);
+        }
+
+        sd_radv_detach_event(ra);
+        return mfree(ra);
+}
+
+_public_ int sd_radv_stop(sd_radv *ra) {
+        assert_return(ra, -EINVAL);
+
+        log_radv("Stopping IPv6 Router Advertisement daemon");
+
+        ra->state = SD_RADV_STATE_IDLE;
+
+        return 0;
+}
+
+_public_ int sd_radv_start(sd_radv *ra) {
+        assert_return(ra, -EINVAL);
+        assert_return(ra->event, -EINVAL);
+        assert_return(ra->ifindex > 0, -EINVAL);
+
+        if (ra->state != SD_RADV_STATE_IDLE)
+                return 0;
+
+        ra->state = SD_RADV_STATE_ADVERTISING;
+
+        log_radv("Started IPv6 Router Advertisement daemon");
+
+        return 0;
+}
+
+_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;
 
@@ -53,6 +312,8 @@ _public_ int sd_radv_prefix_new(sd_radv_prefix **ret) {
         p->opt.preferred_lifetime = htobe32(604800);
         p->opt.valid_lifetime = htobe32(2592000);
 
+        LIST_INIT(prefix, p);
+
         *ret = p;
         p = NULL;
 
index c9933674bd6cfd58abee99c10bd5c2a91d1f0c05..4cbd80db686e52e07d5924c44252093b9aba38c9 100644 (file)
 #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);