No effective functional change, just refactoring.
* The maximum value corresponds to 18.2 hours. 0 MUST NOT be used. */
#define RADV_HOME_AGENT_MAX_LIFETIME_USEC (UINT16_MAX * USEC_PER_SEC)
-#define RADV_OPT_ROUTE_INFORMATION 24
-#define RADV_OPT_RDNSS 25
-#define RADV_OPT_DNSSL 31
-/* Pref64 option type (RFC8781, section 4) */
-#define RADV_OPT_PREF64 38
-
typedef enum RAdvState {
RADV_STATE_IDLE = 0,
RADV_STATE_ADVERTISING = 1,
} RAdvState;
-struct sd_radv_opt_dns {
- uint8_t type;
- uint8_t length;
- uint16_t reserved;
- be32_t lifetime;
-} _packed_;
-
struct sd_radv {
unsigned n_ref;
RAdvState state;
sd_event *event;
int event_priority;
- struct ether_addr mac_addr;
uint8_t hop_limit;
uint8_t flags;
uint8_t preference;
- uint32_t mtu;
usec_t reachable_usec;
usec_t retransmit_usec;
usec_t lifetime_usec; /* timespan */
+ Set *options;
+
int fd;
unsigned ra_sent;
sd_event_source *recv_event_source;
sd_event_source *timeout_event_source;
-
- unsigned n_prefixes;
- LIST_HEAD(sd_radv_prefix, prefixes);
-
- unsigned n_route_prefixes;
- LIST_HEAD(sd_radv_route_prefix, route_prefixes);
-
- unsigned n_pref64_prefixes;
- LIST_HEAD(sd_radv_pref64_prefix, pref64_prefixes);
-
- size_t n_rdnss;
- struct sd_radv_opt_dns *rdnss;
- struct sd_radv_opt_dns *dnssl;
-
- /* Mobile IPv6 extension: Home Agent Info. */
- struct nd_opt_home_agent_info home_agent;
-};
-
-#define radv_prefix_opt__contents { \
- uint8_t type; \
- uint8_t length; \
- uint8_t prefixlen; \
- uint8_t flags; \
- be32_t lifetime_valid; \
- be32_t lifetime_preferred; \
- uint32_t reserved; \
- struct in6_addr in6_addr; \
-}
-
-struct radv_prefix_opt radv_prefix_opt__contents;
-
-/* We need the opt substructure to be packed, because we use it in send(). But
- * if we use _packed_, this means that the structure cannot be used directly in
- * normal code in general, because the fields might not be properly aligned.
- * But in this particular case, the structure is defined in a way that gives
- * proper alignment, even without the explicit _packed_ attribute. To appease
- * the compiler we use the "unpacked" structure, but we also verify that
- * structure contains no holes, so offsets are the same when _packed_ is used.
- */
-struct radv_prefix_opt__packed radv_prefix_opt__contents _packed_;
-assert_cc(sizeof(struct radv_prefix_opt) == sizeof(struct radv_prefix_opt__packed));
-
-struct sd_radv_prefix {
- unsigned n_ref;
-
- struct radv_prefix_opt opt;
-
- LIST_FIELDS(struct sd_radv_prefix, prefix);
-
- /* These are timespans, NOT points in time. */
- usec_t lifetime_valid_usec;
- usec_t lifetime_preferred_usec;
- /* These are points in time specified with clock_boottime_or_monotonic(), NOT timespans. */
- usec_t valid_until;
- usec_t preferred_until;
-};
-
-#define radv_route_prefix_opt__contents { \
- uint8_t type; \
- uint8_t length; \
- uint8_t prefixlen; \
- uint8_t flags_reserved; \
- be32_t lifetime; \
- struct in6_addr in6_addr; \
-}
-
-struct radv_route_prefix_opt radv_route_prefix_opt__contents;
-
-struct radv_route_prefix_opt__packed radv_route_prefix_opt__contents _packed_;
-assert_cc(sizeof(struct radv_route_prefix_opt) == sizeof(struct radv_route_prefix_opt__packed));
-
-struct sd_radv_route_prefix {
- unsigned n_ref;
-
- struct radv_route_prefix_opt opt;
-
- LIST_FIELDS(struct sd_radv_route_prefix, prefix);
-
- /* This is a timespan, NOT a point in time. */
- usec_t lifetime_usec;
- /* This is a point in time specified with clock_boottime_or_monotonic(), NOT a timespan. */
- usec_t valid_until;
-};
-
-struct sd_radv_pref64_prefix {
- unsigned n_ref;
-
- struct nd_opt_prefix64_info opt;
-
- struct in6_addr in6_addr;
- uint8_t prefixlen;
-
- usec_t lifetime_usec;
-
- LIST_FIELDS(struct sd_radv_pref64_prefix, prefix);
};
#define log_radv_errno(radv, error, fmt, ...) \
Copyright © 2017 Intel Corporation. All rights reserved.
***/
+#include <linux/ipv6.h>
#include <netinet/icmp6.h>
#include <netinet/in.h>
#include <arpa/inet.h>
}
int sd_radv_detach_event(sd_radv *ra) {
-
assert_return(ra, -EINVAL);
ra->event = sd_event_unref(ra->event);
if (!ra)
return NULL;
- LIST_CLEAR(prefix, ra->prefixes, sd_radv_prefix_unref);
- LIST_CLEAR(prefix, ra->route_prefixes, sd_radv_route_prefix_unref);
- LIST_CLEAR(prefix, ra->pref64_prefixes, sd_radv_pref64_prefix_unref);
-
- free(ra->rdnss);
- free(ra->dnssl);
-
radv_reset(ra);
sd_event_source_unref(ra->timeout_event_source);
ra->fd = safe_close(ra->fd);
free(ra->ifname);
+ set_free(ra->options);
+
return mfree(ra);
}
};
_cleanup_set_free_ Set *options = NULL;
+ struct ether_addr mac_addr;
usec_t time_now;
int r;
if (r < 0)
return r;
- if (!ether_addr_is_null(&ra->mac_addr)) {
- r = ndisc_option_set_link_layer_address(&options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &ra->mac_addr);
+ /* On stop, we only send source link-layer address option. */
+ if (ndisc_option_get_mac(ra->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &mac_addr) >= 0) {
+ r = ndisc_option_set_link_layer_address(&options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &mac_addr);
if (r < 0)
return r;
}
static int radv_send_router(sd_radv *ra, const struct in6_addr *dst) {
assert(ra);
- struct sockaddr_in6 dst_addr = {
- .sin6_family = AF_INET6,
- .sin6_addr = IN6_ADDR_ALL_NODES_MULTICAST,
- };
struct nd_router_advert adv = {
.nd_ra_type = ND_ROUTER_ADVERT,
.nd_ra_router_lifetime = usec_to_be16_sec(ra->lifetime_usec),
.nd_ra_reachable = usec_to_be32_msec(ra->reachable_usec),
.nd_ra_retransmit = usec_to_be32_msec(ra->retransmit_usec),
};
- struct {
- struct nd_opt_hdr opthdr;
- struct ether_addr slladdr;
- } _packed_ opt_mac = {
- .opthdr = {
- .nd_opt_type = ND_OPT_SOURCE_LINKADDR,
- .nd_opt_len = DIV_ROUND_UP(sizeof(struct nd_opt_hdr) + sizeof(struct ether_addr), 8),
- },
- .slladdr = ra->mac_addr,
- };
- struct nd_opt_mtu opt_mtu = {
- .nd_opt_mtu_type = ND_OPT_MTU,
- .nd_opt_mtu_len = 1,
- .nd_opt_mtu_mtu = htobe32(ra->mtu),
- };
- /* Reserve iov space for RA header, linkaddr, MTU, N prefixes, N routes, N pref64 prefixes, RDNSS,
- * DNSSL, and home agent. */
- struct iovec iov[6 + ra->n_prefixes + ra->n_route_prefixes + ra->n_pref64_prefixes];
- struct msghdr msg = {
- .msg_name = &dst_addr,
- .msg_namelen = sizeof(dst_addr),
- .msg_iov = iov,
- };
usec_t time_now;
int r;
if (r < 0)
return r;
- if (dst && in6_addr_is_set(dst))
- dst_addr.sin6_addr = *dst;
-
/* The nd_ra_curhoplimit and nd_ra_flags_reserved fields cannot specified with nd_ra_router_lifetime
* simultaneously in the structured initializer in the above. */
adv.nd_ra_curhoplimit = ra->hop_limit;
/* RFC 4191, Section 2.2,
* "...If the Router Lifetime is zero, the preference value MUST be set to (00) by the sender..." */
adv.nd_ra_flags_reserved = ra->flags | (ra->lifetime_usec > 0 ? (ra->preference << 3) : 0);
- iov[msg.msg_iovlen++] = IOVEC_MAKE(&adv, sizeof(adv));
-
- /* 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 (!ether_addr_is_null(&ra->mac_addr))
- iov[msg.msg_iovlen++] = IOVEC_MAKE(&opt_mac, sizeof(opt_mac));
-
- if (ra->mtu > 0)
- iov[msg.msg_iovlen++] = IOVEC_MAKE(&opt_mtu, sizeof(opt_mtu));
-
- LIST_FOREACH(prefix, p, ra->prefixes) {
- usec_t lifetime_valid_usec, lifetime_preferred_usec;
-
- lifetime_valid_usec = MIN(usec_sub_unsigned(p->valid_until, time_now),
- p->lifetime_valid_usec);
-
- lifetime_preferred_usec = MIN3(usec_sub_unsigned(p->preferred_until, time_now),
- p->lifetime_preferred_usec,
- lifetime_valid_usec);
-
- p->opt.lifetime_valid = usec_to_be32_sec(lifetime_valid_usec);
- p->opt.lifetime_preferred = usec_to_be32_sec(lifetime_preferred_usec);
- iov[msg.msg_iovlen++] = IOVEC_MAKE(&p->opt, sizeof(p->opt));
- }
-
- LIST_FOREACH(prefix, rt, ra->route_prefixes) {
- rt->opt.lifetime = usec_to_be32_sec(MIN(usec_sub_unsigned(rt->valid_until, time_now),
- rt->lifetime_usec));
-
- iov[msg.msg_iovlen++] = IOVEC_MAKE(&rt->opt, sizeof(rt->opt));
- }
-
- LIST_FOREACH(prefix, p, ra->pref64_prefixes)
- iov[msg.msg_iovlen++] = IOVEC_MAKE(&p->opt, sizeof(p->opt));
-
- if (ra->rdnss)
- iov[msg.msg_iovlen++] = IOVEC_MAKE(ra->rdnss, ra->rdnss->length * 8);
-
- if (ra->dnssl)
- iov[msg.msg_iovlen++] = IOVEC_MAKE(ra->dnssl, ra->dnssl->length * 8);
-
- if (FLAGS_SET(ra->flags, ND_RA_FLAG_HOME_AGENT)) {
- ra->home_agent.nd_opt_home_agent_info_type = ND_OPT_HOME_AGENT_INFO;
- ra->home_agent.nd_opt_home_agent_info_len = 1;
-
- /* 0 means to place the current Router Lifetime value */
- if (ra->home_agent.nd_opt_home_agent_info_lifetime == 0)
- ra->home_agent.nd_opt_home_agent_info_lifetime = adv.nd_ra_router_lifetime;
-
- iov[msg.msg_iovlen++] = IOVEC_MAKE(&ra->home_agent, sizeof(ra->home_agent));
- }
-
- if (sendmsg(ra->fd, &msg, 0) < 0)
- return -errno;
-
- return 0;
+ return ndisc_send(ra->fd,
+ (dst && in6_addr_is_set(dst)) ? dst : &IN6_ADDR_ALL_NODES_MULTICAST,
+ &adv.nd_ra_hdr, ra->options, time_now);
}
static int radv_process_packet(sd_radv *ra, ICMP6Packet *packet) {
return 0;
}
-int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
- assert_return(ra, -EINVAL);
-
- if (mac_addr)
- ra->mac_addr = *mac_addr;
- else
- zero(ra->mac_addr);
-
- return 0;
-}
-
-int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) {
- assert_return(ra, -EINVAL);
- assert_return(mtu >= 1280, -EINVAL);
-
- ra->mtu = mtu;
-
- return 0;
-}
+/* Managing RA header. */
int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit) {
assert_return(ra, -EINVAL);
return 0;
}
-int sd_radv_set_home_agent_information(sd_radv *ra, int home_agent) {
- assert_return(ra, -EINVAL);
+/* Managing options. */
- SET_FLAG(ra->flags, ND_RA_FLAG_HOME_AGENT, home_agent);
- return 0;
-}
-
-int sd_radv_set_home_agent_preference(sd_radv *ra, uint16_t preference) {
+int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
assert_return(ra, -EINVAL);
- ra->home_agent.nd_opt_home_agent_info_preference = htobe16(preference);
- return 0;
+ return ndisc_option_set_link_layer_address(&ra->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, mac_addr);
}
-int sd_radv_set_home_agent_lifetime(sd_radv *ra, uint64_t lifetime_usec) {
- assert_return(ra, -EINVAL);
-
- if (lifetime_usec > RADV_HOME_AGENT_MAX_LIFETIME_USEC)
- return -EINVAL;
+void sd_radv_unset_mac(sd_radv *ra) {
+ if (!ra)
+ return;
- ra->home_agent.nd_opt_home_agent_info_lifetime = usec_to_be16_sec(lifetime_usec);
- return 0;
+ ndisc_option_remove_by_type(ra->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS);
}
-int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
- sd_radv_prefix *found = NULL;
+int sd_radv_add_prefix(
+ sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen,
+ uint8_t flags,
+ uint64_t valid_lifetime_usec,
+ uint64_t preferred_lifetime_usec,
+ uint64_t valid_until,
+ uint64_t preferred_until) {
assert_return(ra, -EINVAL);
- assert_return(p, -EINVAL);
-
- /* Refuse prefixes that don't have a prefix set */
- if (in6_addr_is_null(&p->opt.in6_addr))
- return -ENOEXEC;
+ assert_return(prefix, -EINVAL);
- const char *addr_p = IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen);
+ sd_ndisc_option *opt;
+ SET_FOREACH(opt, ra->options) {
+ if (opt->type != SD_NDISC_OPTION_PREFIX_INFORMATION)
+ continue;
- LIST_FOREACH(prefix, cur, ra->prefixes) {
- if (!in6_addr_prefix_intersect(&cur->opt.in6_addr, cur->opt.prefixlen,
- &p->opt.in6_addr, p->opt.prefixlen))
+ if (!in6_addr_prefix_intersect(&opt->prefix.address, opt->prefix.prefixlen, prefix, prefixlen))
continue; /* no intersection */
- if (cur->opt.prefixlen == p->opt.prefixlen) {
- found = cur;
+ if (opt->prefix.prefixlen == prefixlen)
break; /* same prefix */
- }
return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST),
"IPv6 prefix %s conflicts with %s, ignoring.",
- addr_p,
- IN6_ADDR_PREFIX_TO_STRING(&cur->opt.in6_addr, cur->opt.prefixlen));
+ IN6_ADDR_PREFIX_TO_STRING(prefix, prefixlen),
+ IN6_ADDR_PREFIX_TO_STRING(&opt->prefix.address, opt->prefix.prefixlen));
}
- if (found) {
- /* p and cur may be equivalent. First increment the reference counter. */
- sd_radv_prefix_ref(p);
-
- /* Then, remove the old entry. */
- LIST_REMOVE(prefix, ra->prefixes, found);
- sd_radv_prefix_unref(found);
-
- /* Finally, add the new entry. */
- LIST_APPEND(prefix, ra->prefixes, p);
-
- log_radv(ra, "Updated/replaced IPv6 prefix %s (preferred: %s, valid: %s)",
- addr_p,
- FORMAT_TIMESPAN(p->lifetime_preferred_usec, USEC_PER_SEC),
- FORMAT_TIMESPAN(p->lifetime_valid_usec, USEC_PER_SEC));
- } else {
- /* The prefix is new. Let's simply add it. */
-
- sd_radv_prefix_ref(p);
- LIST_APPEND(prefix, ra->prefixes, p);
- ra->n_prefixes++;
-
- log_radv(ra, "Added prefix %s", addr_p);
- }
-
- return 0;
+ return ndisc_option_set_prefix(
+ &ra->options,
+ flags,
+ prefixlen,
+ prefix,
+ valid_lifetime_usec,
+ preferred_lifetime_usec,
+ valid_until,
+ preferred_until);
}
void sd_radv_remove_prefix(
sd_radv *ra,
const struct in6_addr *prefix,
- unsigned char prefixlen) {
+ uint8_t prefixlen) {
- if (!ra)
+ if (!ra || !prefix)
return;
- if (!prefix)
- return;
-
- LIST_FOREACH(prefix, cur, ra->prefixes) {
- if (prefixlen != cur->opt.prefixlen)
- continue;
-
- if (!in6_addr_equal(prefix, &cur->opt.in6_addr))
- continue;
-
- LIST_REMOVE(prefix, ra->prefixes, cur);
- ra->n_prefixes--;
- sd_radv_prefix_unref(cur);
- return;
- }
+ ndisc_option_remove(ra->options,
+ &(sd_ndisc_option) {
+ .type = SD_NDISC_OPTION_PREFIX_INFORMATION,
+ .prefix.prefixlen = prefixlen,
+ .prefix.address = *prefix,
+ });
}
-int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) {
- sd_radv_route_prefix *found = NULL;
-
+int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) {
assert_return(ra, -EINVAL);
- assert_return(p, -EINVAL);
-
- LIST_FOREACH(prefix, cur, ra->route_prefixes)
- if (cur->opt.prefixlen == p->opt.prefixlen &&
- in6_addr_equal(&cur->opt.in6_addr, &p->opt.in6_addr)) {
- found = cur;
- break;
- }
-
- if (found) {
- /* p and cur may be equivalent. First increment the reference counter. */
- sd_radv_route_prefix_ref(p);
-
- /* Then, remove the old entry. */
- LIST_REMOVE(prefix, ra->route_prefixes, found);
- sd_radv_route_prefix_unref(found);
-
- /* Finally, add the new entry. */
- LIST_APPEND(prefix, ra->route_prefixes, p);
-
- log_radv(ra, "Updated/replaced IPv6 route prefix %s (lifetime: %s)",
- IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen),
- FORMAT_TIMESPAN(p->lifetime_usec, USEC_PER_SEC));
- } else {
- /* The route prefix is new. Let's simply add it. */
-
- sd_radv_route_prefix_ref(p);
- LIST_APPEND(prefix, ra->route_prefixes, p);
- ra->n_route_prefixes++;
-
- log_radv(ra, "Added route prefix %s",
- IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen));
- }
+ assert_return(mtu >= IPV6_MIN_MTU, -EINVAL);
- return 0;
+ return ndisc_option_set_mtu(&ra->options, mtu);
}
-int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p) {
- sd_radv_pref64_prefix *found = NULL;
-
- assert_return(ra, -EINVAL);
- assert_return(p, -EINVAL);
-
- LIST_FOREACH(prefix, cur, ra->pref64_prefixes)
- if (cur->prefixlen == p->prefixlen &&
- in6_addr_equal(&cur->in6_addr, &p->in6_addr)) {
- found = cur;
- break;
- }
-
- if (found) {
- /* p and cur may be equivalent. First increment the reference counter. */
- sd_radv_pref64_prefix_ref(p);
-
- /* Then, remove the old entry. */
- LIST_REMOVE(prefix, ra->pref64_prefixes, found);
- sd_radv_pref64_prefix_unref(found);
-
- /* Finally, add the new entry. */
- LIST_APPEND(prefix, ra->pref64_prefixes, p);
-
- log_radv(ra, "Updated/replaced IPv6 PREF64 prefix %s (lifetime: %s)",
- IN6_ADDR_PREFIX_TO_STRING(&p->in6_addr, p->prefixlen),
- FORMAT_TIMESPAN(p->lifetime_usec, USEC_PER_SEC));
- } else {
- /* The route prefix is new. Let's simply add it. */
-
- sd_radv_pref64_prefix_ref(p);
- LIST_APPEND(prefix, ra->pref64_prefixes, p);
- ra->n_pref64_prefixes++;
-
- log_radv(ra, "Added PREF64 prefix %s",
- IN6_ADDR_PREFIX_TO_STRING(&p->in6_addr, p->prefixlen));
- }
+void sd_radv_unset_mtu(sd_radv *ra) {
+ if (!ra)
+ return;
- return 0;
+ ndisc_option_remove_by_type(ra->options, SD_NDISC_OPTION_MTU);
}
-int sd_radv_set_rdnss(
- sd_radv *ra,
- uint64_t lifetime_usec,
- const struct in6_addr *dns,
- size_t n_dns) {
-
- _cleanup_free_ struct sd_radv_opt_dns *opt_rdnss = NULL;
- size_t len;
-
+int sd_radv_set_home_agent(sd_radv *ra, uint16_t preference, uint64_t lifetime_usec, uint64_t valid_until) {
assert_return(ra, -EINVAL);
- assert_return(n_dns < 128, -EINVAL);
-
- if (lifetime_usec > RADV_RDNSS_MAX_LIFETIME_USEC)
- return -EINVAL;
-
- if (!dns || n_dns == 0) {
- ra->rdnss = mfree(ra->rdnss);
- ra->n_rdnss = 0;
-
- return 0;
- }
-
- len = sizeof(struct sd_radv_opt_dns) + sizeof(struct in6_addr) * n_dns;
-
- opt_rdnss = malloc0(len);
- if (!opt_rdnss)
- return -ENOMEM;
-
- opt_rdnss->type = RADV_OPT_RDNSS;
- opt_rdnss->length = len / 8;
- opt_rdnss->lifetime = usec_to_be32_sec(lifetime_usec);
- memcpy(opt_rdnss + 1, dns, n_dns * sizeof(struct in6_addr));
-
- free_and_replace(ra->rdnss, opt_rdnss);
+ ra->flags |= ND_RA_FLAG_HOME_AGENT;
+ return ndisc_option_set_home_agent(&ra->options, preference, lifetime_usec, valid_until);
+}
- ra->n_rdnss = n_dns;
+void sd_radv_unset_home_agent(sd_radv *ra) {
+ if (!ra)
+ return;
- return 0;
+ ra->flags &= ~ND_RA_FLAG_HOME_AGENT;
+ ndisc_option_remove_by_type(ra->options, SD_NDISC_OPTION_HOME_AGENT);
}
-int sd_radv_set_dnssl(
+int sd_radv_add_route(
sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen,
+ uint8_t preference,
uint64_t lifetime_usec,
- char **search_list) {
-
- _cleanup_free_ struct sd_radv_opt_dns *opt_dnssl = NULL;
- size_t len = 0;
- uint8_t *p;
+ uint64_t valid_until) {
assert_return(ra, -EINVAL);
+ assert_return(prefix, -EINVAL);
- if (lifetime_usec > RADV_DNSSL_MAX_LIFETIME_USEC)
- return -EINVAL;
-
- if (strv_isempty(search_list)) {
- ra->dnssl = mfree(ra->dnssl);
- return 0;
- }
-
- STRV_FOREACH(s, search_list)
- len += strlen(*s) + 2;
-
- len = (sizeof(struct sd_radv_opt_dns) + len + 7) & ~0x7;
-
- opt_dnssl = malloc0(len);
- if (!opt_dnssl)
- return -ENOMEM;
-
- opt_dnssl->type = RADV_OPT_DNSSL;
- opt_dnssl->length = len / 8;
- opt_dnssl->lifetime = usec_to_be32_sec(lifetime_usec);
-
- p = (uint8_t *)(opt_dnssl + 1);
- len -= sizeof(struct sd_radv_opt_dns);
-
- STRV_FOREACH(s, search_list) {
- int r;
-
- r = dns_name_to_wire_format(*s, p, len, false);
- if (r < 0)
- return r;
-
- if (len < (size_t)r)
- return -ENOBUFS;
-
- p += r;
- len -= r;
- }
-
- free_and_replace(ra->dnssl, opt_dnssl);
-
- return 0;
-}
-
-int sd_radv_prefix_new(sd_radv_prefix **ret) {
- sd_radv_prefix *p;
-
- assert_return(ret, -EINVAL);
-
- p = new(sd_radv_prefix, 1);
- if (!p)
- return -ENOMEM;
-
- *p = (sd_radv_prefix) {
- .n_ref = 1,
-
- .opt.type = ND_OPT_PREFIX_INFORMATION,
- .opt.length = (sizeof(p->opt) - 1)/8 + 1,
- .opt.prefixlen = 64,
-
- /* RFC 4861, Section 6.2.1 */
- .opt.flags = ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO,
-
- .lifetime_valid_usec = RADV_DEFAULT_VALID_LIFETIME_USEC,
- .lifetime_preferred_usec = RADV_DEFAULT_PREFERRED_LIFETIME_USEC,
- .valid_until = USEC_INFINITY,
- .preferred_until = USEC_INFINITY,
- };
-
- *ret = p;
- return 0;
-}
-
-DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_prefix, sd_radv_prefix, mfree);
-
-int sd_radv_prefix_set_prefix(
- sd_radv_prefix *p,
- const struct in6_addr *in6_addr,
- unsigned char prefixlen) {
-
- assert_return(p, -EINVAL);
- assert_return(in6_addr, -EINVAL);
-
- if (prefixlen < 3 || prefixlen > 128)
- return -EINVAL;
-
- if (prefixlen > 64)
- /* unusual but allowed, log it */
- log_radv(NULL, "Unusual prefix length %d greater than 64", prefixlen);
-
- p->opt.in6_addr = *in6_addr;
- p->opt.prefixlen = prefixlen;
-
- return 0;
-}
-
-int sd_radv_prefix_get_prefix(
- sd_radv_prefix *p,
- struct in6_addr *ret_in6_addr,
- unsigned char *ret_prefixlen) {
-
- assert_return(p, -EINVAL);
- assert_return(ret_in6_addr, -EINVAL);
- assert_return(ret_prefixlen, -EINVAL);
-
- *ret_in6_addr = p->opt.in6_addr;
- *ret_prefixlen = p->opt.prefixlen;
-
- return 0;
-}
-
-int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink) {
- assert_return(p, -EINVAL);
-
- SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_ONLINK, onlink);
-
- return 0;
+ return ndisc_option_set_route(
+ &ra->options,
+ preference,
+ prefixlen,
+ prefix,
+ lifetime_usec,
+ valid_until);
}
-int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p, int address_autoconfiguration) {
- assert_return(p, -EINVAL);
+void sd_radv_remove_route(
+ sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen) {
- SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_AUTO, address_autoconfiguration);
+ if (!ra || !prefix)
+ return;
- return 0;
+ ndisc_option_remove(ra->options,
+ &(sd_ndisc_option) {
+ .type = SD_NDISC_OPTION_ROUTE_INFORMATION,
+ .route.prefixlen = prefixlen,
+ .route.address = *prefix,
+ });
}
-int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix *p, uint64_t lifetime_usec, uint64_t valid_until) {
- assert_return(p, -EINVAL);
+int sd_radv_add_rdnss(
+ sd_radv *ra,
+ size_t n_dns,
+ const struct in6_addr *dns,
+ uint64_t lifetime_usec,
+ uint64_t valid_until) {
- p->lifetime_valid_usec = lifetime_usec;
- p->valid_until = valid_until;
+ assert_return(ra, -EINVAL);
+ assert_return(dns, -EINVAL);
- return 0;
+ return ndisc_option_set_rdnss(
+ &ra->options,
+ n_dns,
+ dns,
+ lifetime_usec,
+ valid_until);
}
-int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p, uint64_t lifetime_usec, uint64_t valid_until) {
- assert_return(p, -EINVAL);
-
- p->lifetime_preferred_usec = lifetime_usec;
- p->preferred_until = valid_until;
+void sd_radv_clear_rdnss(sd_radv *ra) {
+ if (!ra)
+ return;
- return 0;
+ sd_ndisc_option *opt;
+ SET_FOREACH(opt, ra->options)
+ if (opt->type == SD_NDISC_OPTION_RDNSS)
+ ndisc_option_remove(ra->options, opt);
}
-int sd_radv_route_prefix_new(sd_radv_route_prefix **ret) {
- sd_radv_route_prefix *p;
-
- assert_return(ret, -EINVAL);
-
- p = new(sd_radv_route_prefix, 1);
- if (!p)
- return -ENOMEM;
-
- *p = (sd_radv_route_prefix) {
- .n_ref = 1,
-
- .opt.type = RADV_OPT_ROUTE_INFORMATION,
- .opt.length = DIV_ROUND_UP(sizeof(p->opt), 8),
- .opt.prefixlen = 64,
+int sd_radv_add_dnssl(
+ sd_radv *ra,
+ char * const *domains,
+ uint64_t lifetime_usec,
+ uint64_t valid_until) {
- .lifetime_usec = RADV_DEFAULT_VALID_LIFETIME_USEC,
- .valid_until = USEC_INFINITY,
- };
+ assert_return(ra, -EINVAL);
- *ret = p;
- return 0;
+ return ndisc_option_set_dnssl(
+ &ra->options,
+ domains,
+ lifetime_usec,
+ valid_until);
}
-DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_route_prefix, sd_radv_route_prefix, mfree);
-
-int sd_radv_route_prefix_set_prefix(
- sd_radv_route_prefix *p,
- const struct in6_addr *in6_addr,
- unsigned char prefixlen) {
-
- assert_return(p, -EINVAL);
- assert_return(in6_addr, -EINVAL);
-
- if (prefixlen > 128)
- return -EINVAL;
-
- if (prefixlen > 64)
- /* unusual but allowed, log it */
- log_radv(NULL, "Unusual prefix length %u greater than 64", prefixlen);
-
- p->opt.in6_addr = *in6_addr;
- p->opt.prefixlen = prefixlen;
+void sd_radv_clear_dnssl(sd_radv *ra) {
+ if (!ra)
+ return;
- return 0;
+ sd_ndisc_option *opt;
+ SET_FOREACH(opt, ra->options)
+ if (opt->type == SD_NDISC_OPTION_DNSSL)
+ ndisc_option_remove(ra->options, opt);
}
-int sd_radv_route_prefix_set_lifetime(sd_radv_route_prefix *p, uint64_t lifetime_usec, uint64_t valid_until) {
- assert_return(p, -EINVAL);
-
- p->lifetime_usec = lifetime_usec;
- p->valid_until = valid_until;
+int sd_radv_set_captive_portal(sd_radv *ra, const char *portal) {
+ assert_return(ra, -EINVAL);
+ assert_return(portal, -EINVAL);
- return 0;
+ return ndisc_option_set_captive_portal(&ra->options, portal);
}
-int sd_radv_pref64_prefix_new(sd_radv_pref64_prefix **ret) {
- sd_radv_pref64_prefix *p;
-
- assert_return(ret, -EINVAL);
-
- p = new(sd_radv_pref64_prefix, 1);
- if (!p)
- return -ENOMEM;
-
- *p = (sd_radv_pref64_prefix) {
- .n_ref = 1,
-
- .opt.type = RADV_OPT_PREF64,
- .opt.length = 2,
- };
+void sd_radv_unset_captive_portal(sd_radv *ra) {
+ if (!ra)
+ return;
- *ret = p;
- return 0;
+ ndisc_option_remove_by_type(ra->options, SD_NDISC_OPTION_CAPTIVE_PORTAL);
}
-DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_pref64_prefix, sd_radv_pref64_prefix, mfree);
-
-int sd_radv_pref64_prefix_set_prefix(
- sd_radv_pref64_prefix *p,
+int sd_radv_add_prefix64(
+ sd_radv *ra,
const struct in6_addr *prefix,
uint8_t prefixlen,
- uint64_t lifetime_usec) {
-
- uint16_t pref64_lifetime;
- uint8_t prefixlen_code;
- int r;
+ uint64_t lifetime_usec,
+ uint64_t valid_until) {
- assert_return(p, -EINVAL);
+ assert_return(ra, -EINVAL);
assert_return(prefix, -EINVAL);
- r = pref64_prefix_length_to_plc(prefixlen, &prefixlen_code);
- if (r < 0)
- return log_radv_errno(NULL, r,
- "Unsupported PREF64 prefix length %u. Valid lengths are 32, 40, 48, 56, 64 and 96", prefixlen);
-
- if (lifetime_usec > PREF64_MAX_LIFETIME_USEC)
- return -EINVAL;
-
- /* RFC 8781 - 4.1 rounding up lifetime to multiply of 8 */
- pref64_lifetime = DIV_ROUND_UP(lifetime_usec, 8 * USEC_PER_SEC) << 3;
- pref64_lifetime |= prefixlen_code;
+ return ndisc_option_set_prefix64(
+ &ra->options,
+ prefixlen,
+ prefix,
+ lifetime_usec,
+ valid_until);
+}
- unaligned_write_be16(&p->opt.lifetime_and_plc, pref64_lifetime);
- memcpy(&p->opt.prefix, prefix, sizeof(p->opt.prefix));
+void sd_radv_remove_prefix64(
+ sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen) {
- p->in6_addr = *prefix;
- p->prefixlen = prefixlen;
+ if (!ra || !prefix)
+ return;
- return 0;
+ ndisc_option_remove(ra->options,
+ &(sd_ndisc_option) {
+ .type = SD_NDISC_OPTION_PREF64,
+ .prefix64.prefixlen = prefixlen,
+ .prefix64.prefix = *prefix,
+ });
}
#include "alloc-util.h"
#include "hexdecoct.h"
#include "icmp6-test-util.h"
+#include "radv-internal.h"
#include "socket-util.h"
#include "strv.h"
#include "tests.h"
static const char *test_dnssl[] = { "lab.intra",
NULL };
-TEST(radv_prefix) {
- sd_radv_prefix *p;
-
- assert_se(sd_radv_prefix_new(&p) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_onlink(NULL, true) < 0);
- assert_se(sd_radv_prefix_set_onlink(p, true) >= 0);
- assert_se(sd_radv_prefix_set_onlink(p, false) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_address_autoconfiguration(NULL, true) < 0);
- assert_se(sd_radv_prefix_set_address_autoconfiguration(p, true) >= 0);
- assert_se(sd_radv_prefix_set_address_autoconfiguration(p, false) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_valid_lifetime(NULL, 1, 1) < 0);
- assert_se(sd_radv_prefix_set_valid_lifetime(p, 0, 0) >= 0);
- assert_se(sd_radv_prefix_set_valid_lifetime(p, 300 * USEC_PER_SEC, USEC_INFINITY) >= 0);
- assert_se(sd_radv_prefix_set_valid_lifetime(p, 300 * USEC_PER_SEC, USEC_PER_YEAR) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_preferred_lifetime(NULL, 1, 1) < 0);
- assert_se(sd_radv_prefix_set_preferred_lifetime(p, 0, 0) >= 0);
- assert_se(sd_radv_prefix_set_preferred_lifetime(p, 300 * USEC_PER_SEC, USEC_INFINITY) >= 0);
- assert_se(sd_radv_prefix_set_preferred_lifetime(p, 300 * USEC_PER_SEC, USEC_PER_YEAR) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_prefix(NULL, NULL, 0) < 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_prefix(p, NULL, 0) < 0);
-
- assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 64) >= 0);
- assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 0) < 0);
- assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 1) < 0);
- assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 2) < 0);
- assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 3) >= 0);
- assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 125) >= 0);
- assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 128) >= 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_prefix(p, &prefix[0].address, 129) < 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_prefix(p, &prefix[0].address, 255) < 0);
-
- assert_se(!sd_radv_prefix_unref(p));
-}
-
-TEST(radv_route_prefix) {
- sd_radv_route_prefix *p;
-
- assert_se(sd_radv_route_prefix_new(&p) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_route_prefix_set_lifetime(NULL, 1, 1) < 0);
- assert_se(sd_radv_route_prefix_set_lifetime(p, 0, 0) >= 0);
- assert_se(sd_radv_route_prefix_set_lifetime(p, 300 * USEC_PER_SEC, USEC_INFINITY) >= 0);
- assert_se(sd_radv_route_prefix_set_lifetime(p, 300 * USEC_PER_SEC, USEC_PER_YEAR) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_route_prefix_set_prefix(NULL, NULL, 0) < 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_route_prefix_set_prefix(p, NULL, 0) < 0);
-
- assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 64) >= 0);
- assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 0) >= 0);
- assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 1) >= 0);
- assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 2) >= 0);
- assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 3) >= 0);
- assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 125) >= 0);
- assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 128) >= 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 129) < 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 255) < 0);
-
- assert_se(!sd_radv_route_prefix_unref(p));
-}
-
-TEST(radv_pref64_prefix) {
- sd_radv_pref64_prefix *p;
-
- assert_se(sd_radv_pref64_prefix_new(&p) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_pref64_prefix_set_prefix(NULL, NULL, 0, 0) < 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_pref64_prefix_set_prefix(p, NULL, 0, 0) < 0);
-
- assert_se(sd_radv_pref64_prefix_set_prefix(p, &prefix[0].address, 32, 300 * USEC_PER_SEC) >= 0);
- assert_se(sd_radv_pref64_prefix_set_prefix(p, &prefix[0].address, 40, 300 * USEC_PER_SEC) >= 0);
- assert_se(sd_radv_pref64_prefix_set_prefix(p, &prefix[0].address, 48, 300 * USEC_PER_SEC) >= 0);
- assert_se(sd_radv_pref64_prefix_set_prefix(p, &prefix[0].address, 56, 300 * USEC_PER_SEC) >= 0);
- assert_se(sd_radv_pref64_prefix_set_prefix(p, &prefix[0].address, 64, 300 * USEC_PER_SEC) >= 0);
- assert_se(sd_radv_pref64_prefix_set_prefix(p, &prefix[0].address, 96, 300 * USEC_PER_SEC) >= 0);
-
- assert_se(sd_radv_pref64_prefix_set_prefix(p, &prefix[0].address, 80, 300 * USEC_PER_SEC) < 0);
- assert_se(sd_radv_pref64_prefix_set_prefix(p, &prefix[0].address, 80, USEC_PER_DAY) < 0);
-
- assert_se(!sd_radv_pref64_prefix_unref(p));
-}
-
TEST(radv) {
sd_radv *ra;
ASSERT_RETURN_EXPECTED_SE(sd_radv_set_ifindex(ra, -2) < 0);
assert_se(sd_radv_set_ifindex(ra, 42) >= 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mac(NULL, NULL) < 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mac(ra, NULL) >= 0);
- assert_se(sd_radv_set_mac(ra, &mac_addr) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mtu(NULL, 0) < 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mtu(ra, 0) < 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mtu(ra, 1279) < 0);
- assert_se(sd_radv_set_mtu(ra, 1280) >= 0);
- assert_se(sd_radv_set_mtu(ra, ~0) >= 0);
-
+ /* header */
ASSERT_RETURN_EXPECTED_SE(sd_radv_set_hop_limit(NULL, 0) < 0);
assert_se(sd_radv_set_hop_limit(ra, 0) >= 0);
assert_se(sd_radv_set_hop_limit(ra, ~0) >= 0);
assert_se(sd_radv_set_retransmit(ra, 0) >= 0);
assert_se(sd_radv_set_retransmit(ra, USEC_INFINITY) >= 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_rdnss(NULL, 0, NULL, 0) < 0);
- assert_se(sd_radv_set_rdnss(ra, 0, NULL, 0) >= 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_rdnss(ra, 0, NULL, 128) < 0);
- assert_se(sd_radv_set_rdnss(ra, 600 * USEC_PER_SEC, &test_rdnss, 0) >= 0);
- assert_se(sd_radv_set_rdnss(ra, 600 * USEC_PER_SEC, &test_rdnss, 1) >= 0);
- assert_se(sd_radv_set_rdnss(ra, 0, &test_rdnss, 1) >= 0);
- assert_se(sd_radv_set_rdnss(ra, 0, NULL, 0) >= 0);
-
- assert_se(sd_radv_set_dnssl(ra, 0, NULL) >= 0);
- assert_se(sd_radv_set_dnssl(ra, 600 * USEC_PER_SEC, NULL) >= 0);
- assert_se(sd_radv_set_dnssl(ra, 0, (char **)test_dnssl) >= 0);
- assert_se(sd_radv_set_dnssl(ra, 600 * USEC_PER_SEC, (char **)test_dnssl) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_home_agent_information(NULL, true) < 0);
- assert_se(sd_radv_set_home_agent_information(ra, true) >= 0);
- assert_se(sd_radv_set_home_agent_information(ra, false) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_home_agent_preference(NULL, 10) < 0);
- assert_se(sd_radv_set_home_agent_preference(ra, 10) >= 0);
- assert_se(sd_radv_set_home_agent_preference(ra, 0) >= 0);
+ /* options */
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mac(NULL, NULL) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mac(ra, NULL) >= 0);
+ assert_se(sd_radv_set_mac(ra, &mac_addr) >= 0);
+ sd_radv_unset_mac(ra);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_home_agent_lifetime(NULL, 300 * USEC_PER_SEC) < 0);
- assert_se(sd_radv_set_home_agent_lifetime(ra, 300 * USEC_PER_SEC) >= 0);
- assert_se(sd_radv_set_home_agent_lifetime(ra, 0) >= 0);
- assert_se(sd_radv_set_home_agent_lifetime(ra, USEC_PER_DAY) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mtu(NULL, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mtu(ra, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mtu(ra, 1279) < 0);
+ assert_se(sd_radv_set_mtu(ra, 1280) >= 0);
+ assert_se(sd_radv_set_mtu(ra, 9999) >= 0);
+ sd_radv_unset_mtu(ra);
+
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_add_rdnss(NULL, 0, NULL, 0, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_add_rdnss(ra, 0, NULL, 0, 0) < 0);
+ assert_se(sd_radv_add_rdnss(ra, 0, &test_rdnss, 600 * USEC_PER_SEC, USEC_INFINITY) < 0);
+ assert_se(sd_radv_add_rdnss(ra, 1, &test_rdnss, 600 * USEC_PER_SEC, USEC_INFINITY) >= 0);
+ assert_se(sd_radv_add_rdnss(ra, 1, &test_rdnss, 0, 0) >= 0);
+ sd_radv_clear_rdnss(ra);
+
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_add_dnssl(NULL, NULL, 0, 0) < 0);
+ assert_se(sd_radv_add_dnssl(ra, NULL, 0, 0) < 0);
+ assert_se(sd_radv_add_dnssl(ra, NULL, 600 * USEC_PER_SEC, USEC_INFINITY) < 0);
+ assert_se(sd_radv_add_dnssl(ra, (char**) test_dnssl, 600 * USEC_PER_SEC, USEC_INFINITY) >= 0);
+ assert_se(sd_radv_add_dnssl(ra, (char**) test_dnssl, 0, 0) >= 0);
+ sd_radv_clear_dnssl(ra);
+
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_home_agent(NULL, 0, 0, 0) < 0);
+ assert_se(sd_radv_set_home_agent(ra, 0, 0, 0) >= 0);
+ assert_se(sd_radv_set_home_agent(ra, 10, 300 * USEC_PER_SEC, USEC_INFINITY) >= 0);
+ sd_radv_unset_home_agent(ra);
ra = sd_radv_unref(ra);
assert_se(!ra);
/* Source Link Layer Address Option */
0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53,
/* Prefix Information Option */
- 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4,
- 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
+ 0x03, 0x04, 0x30, 0xc0, 0x00, 0x00, 0x0e, 0x10,
+ 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* Prefix Information Option */
0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x0e, 0x10,
0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* Prefix Information Option */
- 0x03, 0x04, 0x30, 0xc0, 0x00, 0x00, 0x0e, 0x10,
- 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x01, 0x0d, 0xad,
+ 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4,
+ 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* Recursive DNS Server Option */
0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
assert_se(sd_radv_attach_event(ra, e, 0) >= 0);
assert_se(sd_radv_set_ifindex(ra, 42) >= 0);
- assert_se(sd_radv_set_mac(ra, &mac_addr) >= 0);
assert_se(sd_radv_set_router_lifetime(ra, 180 * USEC_PER_SEC) >= 0);
assert_se(sd_radv_set_hop_limit(ra, 64) >= 0);
assert_se(sd_radv_set_managed_information(ra, true) >= 0);
assert_se(sd_radv_set_other_information(ra, true) >= 0);
- assert_se(sd_radv_set_rdnss(ra, 60 * USEC_PER_SEC, &test_rdnss, 1) >= 0);
- assert_se(sd_radv_set_dnssl(ra, 60 * USEC_PER_SEC, (char **)test_dnssl) >= 0);
-
- for (unsigned i = 0; i < ELEMENTSOF(prefix); i++) {
- sd_radv_prefix *p;
-
- printf("Test prefix %u\n", i);
- assert_se(sd_radv_prefix_new(&p) >= 0);
-
- assert_se(sd_radv_prefix_set_prefix(p, &prefix[i].address,
- prefix[i].prefixlen) >= 0);
- if (prefix[i].valid > 0)
- assert_se(sd_radv_prefix_set_valid_lifetime(p, prefix[i].valid * USEC_PER_SEC, USEC_INFINITY) >= 0);
- if (prefix[i].preferred > 0)
- assert_se(sd_radv_prefix_set_preferred_lifetime(p, prefix[i].preferred * USEC_PER_SEC, USEC_INFINITY) >= 0);
-
- assert_se((sd_radv_add_prefix(ra, p) >= 0) == prefix[i].successful);
+ assert_se(sd_radv_set_mac(ra, &mac_addr) >= 0);
+ assert_se(sd_radv_add_rdnss(ra, 1, &test_rdnss, 60 * USEC_PER_SEC, USEC_INFINITY) >= 0);
+ assert_se(sd_radv_add_dnssl(ra, (char**) test_dnssl, 60 * USEC_PER_SEC, USEC_INFINITY) >= 0);
+
+ FOREACH_ARRAY(p, prefix, ELEMENTSOF(prefix)) {
+ printf("Test prefix %s\n", IN6_ADDR_PREFIX_TO_STRING(&p->address, p->prefixlen));
+ assert_se((sd_radv_add_prefix(
+ ra,
+ &p->address,
+ p->prefixlen,
+ ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO,
+ p->valid > 0 ? p->valid * USEC_PER_SEC : RADV_DEFAULT_VALID_LIFETIME_USEC,
+ p->preferred > 0 ? p->preferred * USEC_PER_SEC : RADV_DEFAULT_PREFERRED_LIFETIME_USEC,
+ USEC_INFINITY,
+ USEC_INFINITY) >= 0) == p->successful);
/* If the previous sd_radv_add_prefix() succeeds, then also the second call should also succeed. */
- assert_se((sd_radv_add_prefix(ra, p) >= 0) == prefix[i].successful);
-
- p = sd_radv_prefix_unref(p);
- assert_se(!p);
+ assert_se((sd_radv_add_prefix(
+ ra,
+ &p->address,
+ p->prefixlen,
+ ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO,
+ p->valid > 0 ? p->valid * USEC_PER_SEC : RADV_DEFAULT_VALID_LIFETIME_USEC,
+ p->preferred > 0 ? p->preferred * USEC_PER_SEC : RADV_DEFAULT_PREFERRED_LIFETIME_USEC,
+ USEC_INFINITY,
+ USEC_INFINITY) >= 0) == p->successful);
}
assert_se(sd_event_add_io(e, &recv_router_advertisement, test_fd[0], EPOLLIN, radv_recv, ra) >= 0);
return 0;
}
-static int radv_set_prefix(Link *link, Prefix *prefix) {
- _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
- int r;
-
- assert(link);
- assert(link->radv);
- assert(prefix);
-
- r = sd_radv_prefix_new(&p);
- if (r < 0)
- return r;
-
- r = sd_radv_prefix_set_prefix(p, &prefix->prefix.address, prefix->prefix.prefixlen);
- if (r < 0)
- return r;
-
- r = sd_radv_prefix_set_preferred_lifetime(p, prefix->prefix.preferred_lifetime, prefix->prefix.preferred_until);
- if (r < 0)
- return r;
-
- r = sd_radv_prefix_set_valid_lifetime(p, prefix->prefix.valid_lifetime, prefix->prefix.valid_until);
- if (r < 0)
- return r;
-
- r = sd_radv_prefix_set_onlink(p, FLAGS_SET(prefix->prefix.flags, ND_OPT_PI_FLAG_ONLINK));
- if (r < 0)
- return r;
-
- r = sd_radv_prefix_set_address_autoconfiguration(p, FLAGS_SET(prefix->prefix.flags, ND_OPT_PI_FLAG_AUTO));
- if (r < 0)
- return r;
-
- return sd_radv_add_prefix(link->radv, p);
-}
-
-static int radv_set_route_prefix(Link *link, RoutePrefix *prefix) {
- _cleanup_(sd_radv_route_prefix_unrefp) sd_radv_route_prefix *p = NULL;
- int r;
-
- assert(link);
- assert(link->radv);
- assert(prefix);
-
- r = sd_radv_route_prefix_new(&p);
- if (r < 0)
- return r;
-
- r = sd_radv_route_prefix_set_prefix(p, &prefix->route.address, prefix->route.prefixlen);
- if (r < 0)
- return r;
-
- r = sd_radv_route_prefix_set_lifetime(p, prefix->route.lifetime, prefix->route.valid_until);
- if (r < 0)
- return r;
-
- return sd_radv_add_route_prefix(link->radv, p);
-}
-
-static int radv_set_pref64_prefix(Link *link, Prefix64 *prefix) {
- _cleanup_(sd_radv_pref64_prefix_unrefp) sd_radv_pref64_prefix *p = NULL;
- int r;
-
- assert(link);
- assert(link->radv);
- assert(prefix);
-
- r = sd_radv_pref64_prefix_new(&p);
- if (r < 0)
- return r;
-
- r = sd_radv_pref64_prefix_set_prefix(p, &prefix->prefix64.prefix, prefix->prefix64.prefixlen, prefix->prefix64.lifetime);
- if (r < 0)
- return r;
-
- return sd_radv_add_pref64_prefix(link->radv, p);
-}
-
static int network_get_ipv6_dns(Network *network, struct in6_addr **ret_addresses, size_t *ret_size) {
_cleanup_free_ struct in6_addr *addresses = NULL;
size_t n_addresses = 0;
return 0;
set_dns:
- return sd_radv_set_rdnss(link->radv,
- link->network->router_dns_lifetime_usec,
- dns, n_dns);
+ return sd_radv_add_rdnss(
+ link->radv,
+ n_dns,
+ dns,
+ link->network->router_dns_lifetime_usec,
+ /* valid_until = */ USEC_INFINITY);
}
static int radv_set_domains(Link *link, Link *uplink) {
if (!s)
return log_oom();
- return sd_radv_set_dnssl(link->radv,
- link->network->router_dns_lifetime_usec,
- s);
+ return sd_radv_add_dnssl(
+ link->radv,
+ s,
+ link->network->router_dns_lifetime_usec,
+ /* valid_until = */ USEC_INFINITY);
}
Prefix *p;
HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
- r = radv_set_prefix(link, p);
+ r = sd_radv_add_prefix(
+ link->radv,
+ &p->prefix.address,
+ p->prefix.prefixlen,
+ p->prefix.flags,
+ p->prefix.valid_lifetime,
+ p->prefix.preferred_lifetime,
+ p->prefix.valid_until,
+ p->prefix.preferred_until);
if (r < 0 && r != -EEXIST)
return r;
}
RoutePrefix *q;
HASHMAP_FOREACH(q, link->network->route_prefixes_by_section) {
- r = radv_set_route_prefix(link, q);
+ r = sd_radv_add_route(
+ link->radv,
+ &q->route.address,
+ q->route.prefixlen,
+ q->route.preference,
+ q->route.lifetime,
+ q->route.valid_until);
if (r < 0 && r != -EEXIST)
return r;
}
Prefix64 *n;
HASHMAP_FOREACH(n, link->network->pref64_prefixes_by_section) {
- r = radv_set_pref64_prefix(link, n);
+ r = sd_radv_add_prefix64(
+ link->radv,
+ &n->prefix64.prefix,
+ n->prefix64.prefixlen,
+ n->prefix64.lifetime,
+ n->prefix64.valid_until);
if (r < 0 && r != -EEXIST)
return r;
}
if (r < 0)
return log_link_debug_errno(link, r, "Could not set RA Domains: %m");
- r = sd_radv_set_home_agent_information(link->radv, link->network->router_home_agent_information);
- if (r < 0)
- return r;
-
- r = sd_radv_set_home_agent_preference(link->radv, link->network->router_home_agent_preference);
- if (r < 0)
- return r;
-
- r = sd_radv_set_home_agent_lifetime(link->radv, link->network->home_agent_lifetime_usec);
- if (r < 0)
- return r;
+ if (link->network->router_home_agent_information) {
+ r = sd_radv_set_home_agent(
+ link->radv,
+ link->network->router_home_agent_preference,
+ link->network->home_agent_lifetime_usec,
+ /* valid_until = */ USEC_INFINITY);
+ if (r < 0)
+ return r;
+ }
return 0;
}
usec_t lifetime_preferred_usec,
usec_t lifetime_valid_usec) {
- _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
int r;
assert(link);
+ assert(prefix);
if (!link->radv)
return 0;
- r = sd_radv_prefix_new(&p);
- if (r < 0)
- return r;
-
- r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
- if (r < 0)
- return r;
-
- r = sd_radv_prefix_set_preferred_lifetime(p, RADV_DEFAULT_PREFERRED_LIFETIME_USEC, lifetime_preferred_usec);
- if (r < 0)
- return r;
-
- r = sd_radv_prefix_set_valid_lifetime(p, RADV_DEFAULT_VALID_LIFETIME_USEC, lifetime_valid_usec);
- if (r < 0)
- return r;
-
- r = sd_radv_add_prefix(link->radv, p);
+ r = sd_radv_add_prefix(
+ link->radv,
+ prefix,
+ prefix_len,
+ ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO,
+ RADV_DEFAULT_VALID_LIFETIME_USEC,
+ RADV_DEFAULT_PREFERRED_LIFETIME_USEC,
+ lifetime_valid_usec,
+ lifetime_preferred_usec);
if (r == -EEXIST)
return 0;
if (r < 0)
_SD_BEGIN_DECLARATIONS;
typedef struct sd_radv sd_radv;
-typedef struct sd_radv_prefix sd_radv_prefix;
-typedef struct sd_radv_route_prefix sd_radv_route_prefix;
-typedef struct sd_radv_pref64_prefix sd_radv_pref64_prefix;
-/* Router Advertisement */
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_set_ifname(sd_radv *ra, const char *interface_name);
int sd_radv_get_ifname(sd_radv *ra, const char **ret);
int sd_radv_set_link_local_address(sd_radv *ra, const struct in6_addr *addr);
-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);
+
+/* RA header */
int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit);
int sd_radv_set_reachable_time(sd_radv *ra, uint64_t usec);
int sd_radv_set_retransmit(sd_radv *ra, uint64_t usec);
int sd_radv_set_managed_information(sd_radv *ra, int b);
int sd_radv_set_other_information(sd_radv *ra, int b);
int sd_radv_set_preference(sd_radv *ra, uint8_t preference);
-int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p);
-int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p);
-int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p);
-void sd_radv_remove_prefix(sd_radv *ra, const struct in6_addr *prefix, unsigned char prefixlen);
-int sd_radv_set_rdnss(sd_radv *ra, uint64_t lifetime_usec,
- const struct in6_addr *dns, size_t n_dns);
-int sd_radv_set_dnssl(sd_radv *ra, uint64_t lifetime_usec, char **search_list);
-
-/* Advertised prefixes */
-int sd_radv_prefix_new(sd_radv_prefix **ret);
-sd_radv_prefix *sd_radv_prefix_ref(sd_radv_prefix *ra);
-sd_radv_prefix *sd_radv_prefix_unref(sd_radv_prefix *ra);
-
-int sd_radv_prefix_set_prefix(sd_radv_prefix *p, const struct in6_addr *in6_addr,
- unsigned char prefixlen);
-int sd_radv_prefix_get_prefix(sd_radv_prefix *p, struct in6_addr *ret_in6_addr,
- unsigned char *ret_prefixlen);
-int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink);
-int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p,
- int address_autoconfiguration);
-int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix *p, uint64_t lifetime_usec, uint64_t valid_until);
-int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p, uint64_t lifetime_usec, uint64_t valid_until);
-
-int sd_radv_route_prefix_new(sd_radv_route_prefix **ret);
-sd_radv_route_prefix *sd_radv_route_prefix_ref(sd_radv_route_prefix *ra);
-sd_radv_route_prefix *sd_radv_route_prefix_unref(sd_radv_route_prefix *ra);
-
-int sd_radv_route_prefix_set_prefix(sd_radv_route_prefix *p, const struct in6_addr *in6_addr, unsigned char prefixlen);
-int sd_radv_route_prefix_set_lifetime(sd_radv_route_prefix *p, uint64_t lifetime_usec, uint64_t valid_until);
-
-int sd_radv_pref64_prefix_new(sd_radv_pref64_prefix **ret);
-int sd_radv_pref64_prefix_set_prefix(sd_radv_pref64_prefix *p, const struct in6_addr *prefix,
- uint8_t prefixlen, uint64_t lifetime_usec);
-sd_radv_pref64_prefix *sd_radv_pref64_prefix_ref(sd_radv_pref64_prefix *ra);
-sd_radv_pref64_prefix *sd_radv_pref64_prefix_unref(sd_radv_pref64_prefix *ra);
-
-/* Mobile IPv6 extension: Home Agent Info. */
-int sd_radv_set_home_agent_information(sd_radv *ra, int home_agent);
-int sd_radv_set_home_agent_preference(sd_radv *ra, uint16_t preference);
-int sd_radv_set_home_agent_lifetime(sd_radv *ra, uint64_t usec);
+
+/* Options */
+int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr);
+void sd_radv_unset_mac(sd_radv *ra);
+int sd_radv_add_prefix(
+ sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen,
+ uint8_t flags,
+ uint64_t valid_lifetime_usec,
+ uint64_t preferred_lifetime_usec,
+ uint64_t valid_until,
+ uint64_t preferred_until);
+void sd_radv_remove_prefix(
+ sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen);
+int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu);
+void sd_radv_unset_mtu(sd_radv *ra);
+int sd_radv_set_home_agent(sd_radv *ra, uint16_t preference, uint64_t lifetime_usec, uint64_t valid_until);
+void sd_radv_unset_home_agent(sd_radv *ra);
+int sd_radv_add_route(
+ sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen,
+ uint8_t preference,
+ uint64_t lifetime_usec,
+ uint64_t valid_until);
+void sd_radv_remove_route(
+ sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen);
+int sd_radv_add_rdnss(
+ sd_radv *ra,
+ size_t n_dns,
+ const struct in6_addr *dns,
+ uint64_t lifetime_usec,
+ uint64_t valid_until);
+void sd_radv_clear_rdnss(sd_radv *ra);
+int sd_radv_add_dnssl(
+ sd_radv *ra,
+ char * const *domains,
+ uint64_t lifetime_usec,
+ uint64_t valid_until);
+void sd_radv_clear_dnssl(sd_radv *ra);
+int sd_radv_set_captive_portal(sd_radv *ra, const char *portal);
+void sd_radv_unset_captive_portal(sd_radv *ra);
+int sd_radv_add_prefix64(
+ sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen,
+ uint64_t lifetime_usec,
+ uint64_t valid_until);
+void sd_radv_remove_prefix64(
+ sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv, sd_radv_unref);
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv_prefix, sd_radv_prefix_unref);
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv_route_prefix, sd_radv_route_prefix_unref);
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv_pref64_prefix, sd_radv_pref64_prefix_unref);
_SD_END_DECLARATIONS;