diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h index 0f92c8b55c..40a5506b95 100644 --- a/src/libsystemd-network/radv-internal.h +++ b/src/libsystemd-network/radv-internal.h @@ -25,6 +25,31 @@ #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; diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index 54f126e0f6..9336132e19 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -19,9 +19,11 @@ #include #include +#include #include "sd-radv.h" +#include "macro.h" #include "alloc-util.h" #include "fd-util.h" #include "icmp6-util.h" @@ -31,6 +33,263 @@ #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; diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h index c9933674bd..4cbd80db68 100644 --- a/src/systemd/sd-radv.h +++ b/src/systemd/sd-radv.h @@ -25,14 +25,39 @@ #include #include +#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);