sd-radv: drop sd_radv_prefix and friends, and use sd_ndisc_option to manage NDisc options

No effective functional change, just refactoring.
This commit is contained in:
Yu Watanabe
2024-10-15 05:59:20 +09:00
parent f121d8179a
commit 098f32e2a1
5 changed files with 374 additions and 982 deletions
+2 -110
View File
@@ -75,24 +75,11 @@
* 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;
@@ -104,114 +91,19 @@ struct sd_radv {
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, ...) \
+204 -565
View File
@@ -3,6 +3,7 @@
Copyright © 2017 Intel Corporation. All rights reserved.
***/
#include <linux/ipv6.h>
#include <netinet/icmp6.h>
#include <netinet/in.h>
#include <arpa/inet.h>
@@ -68,7 +69,6 @@ int sd_radv_attach_event(sd_radv *ra, sd_event *event, int64_t priority) {
}
int sd_radv_detach_event(sd_radv *ra) {
assert_return(ra, -EINVAL);
ra->event = sd_event_unref(ra->event);
@@ -102,13 +102,6 @@ static sd_radv *radv_free(sd_radv *ra) {
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);
@@ -117,6 +110,8 @@ static sd_radv *radv_free(sd_radv *ra) {
ra->fd = safe_close(ra->fd);
free(ra->ifname);
set_free(ra->options);
return mfree(ra);
}
@@ -135,6 +130,7 @@ static int radv_send_router_on_stop(sd_radv *ra) {
};
_cleanup_set_free_ Set *options = NULL;
struct ether_addr mac_addr;
usec_t time_now;
int r;
@@ -144,8 +140,9 @@ static int radv_send_router_on_stop(sd_radv *ra) {
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;
}
@@ -156,39 +153,12 @@ static int radv_send_router_on_stop(sd_radv *ra) {
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;
@@ -196,72 +166,16 @@ static int radv_send_router(sd_radv *ra, const struct in6_addr *dst) {
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) {
@@ -512,25 +426,7 @@ int sd_radv_set_link_local_address(sd_radv *ra, const struct in6_addr *addr) {
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);
@@ -588,497 +484,240 @@ int sd_radv_set_preference(sd_radv *ra, uint8_t preference) {
return 0;
}
int sd_radv_set_home_agent_information(sd_radv *ra, int home_agent) {
/* Managing options. */
int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
assert_return(ra, -EINVAL);
SET_FLAG(ra->flags, ND_RA_FLAG_HOME_AGENT, home_agent);
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_preference(sd_radv *ra, uint16_t preference) {
assert_return(ra, -EINVAL);
void sd_radv_unset_mac(sd_radv *ra) {
if (!ra)
return;
ra->home_agent.nd_opt_home_agent_info_preference = htobe16(preference);
return 0;
ndisc_option_remove_by_type(ra->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS);
}
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;
ra->home_agent.nd_opt_home_agent_info_lifetime = usec_to_be16_sec(lifetime_usec);
return 0;
}
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);
assert_return(prefix, -EINVAL);
/* Refuse prefixes that don't have a prefix set */
if (in6_addr_is_null(&p->opt.in6_addr))
return -ENOEXEC;
sd_ndisc_option *opt;
SET_FOREACH(opt, ra->options) {
if (opt->type != SD_NDISC_OPTION_PREFIX_INFORMATION)
continue;
const char *addr_p = IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen);
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 || !prefix)
return;
ndisc_option_remove(ra->options,
&(sd_ndisc_option) {
.type = SD_NDISC_OPTION_PREFIX_INFORMATION,
.prefix.prefixlen = prefixlen,
.prefix.address = *prefix,
});
}
int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) {
assert_return(ra, -EINVAL);
assert_return(mtu >= IPV6_MIN_MTU, -EINVAL);
return ndisc_option_set_mtu(&ra->options, mtu);
}
void sd_radv_unset_mtu(sd_radv *ra) {
if (!ra)
return;
if (!prefix)
ndisc_option_remove_by_type(ra->options, SD_NDISC_OPTION_MTU);
}
int sd_radv_set_home_agent(sd_radv *ra, uint16_t preference, uint64_t lifetime_usec, uint64_t valid_until) {
assert_return(ra, -EINVAL);
ra->flags |= ND_RA_FLAG_HOME_AGENT;
return ndisc_option_set_home_agent(&ra->options, preference, lifetime_usec, valid_until);
}
void sd_radv_unset_home_agent(sd_radv *ra) {
if (!ra)
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;
}
ra->flags &= ~ND_RA_FLAG_HOME_AGENT;
ndisc_option_remove_by_type(ra->options, SD_NDISC_OPTION_HOME_AGENT);
}
int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) {
sd_radv_route_prefix *found = NULL;
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));
}
return 0;
}
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));
}
return 0;
}
int sd_radv_set_rdnss(
int sd_radv_add_route(
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;
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->n_rdnss = n_dns;
return 0;
}
int sd_radv_set_dnssl(
sd_radv *ra,
uint64_t lifetime_usec,
char **search_list) {
_cleanup_free_ struct sd_radv_opt_dns *opt_dnssl = NULL;
size_t len = 0;
uint8_t *p;
assert_return(ra, -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;
}
int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p, int address_autoconfiguration) {
assert_return(p, -EINVAL);
SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_AUTO, address_autoconfiguration);
return 0;
}
int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix *p, uint64_t lifetime_usec, uint64_t valid_until) {
assert_return(p, -EINVAL);
p->lifetime_valid_usec = lifetime_usec;
p->valid_until = valid_until;
return 0;
}
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;
return 0;
}
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,
.lifetime_usec = RADV_DEFAULT_VALID_LIFETIME_USEC,
.valid_until = USEC_INFINITY,
};
*ret = p;
return 0;
}
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;
return 0;
}
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;
return 0;
}
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,
};
*ret = p;
return 0;
}
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,
const struct in6_addr *prefix,
uint8_t prefixlen,
uint64_t lifetime_usec) {
uint8_t preference,
uint64_t lifetime_usec,
uint64_t valid_until) {
uint16_t pref64_lifetime;
uint8_t prefixlen_code;
int r;
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);
return ndisc_option_set_route(
&ra->options,
preference,
prefixlen,
prefix,
lifetime_usec,
valid_until);
}
if (lifetime_usec > PREF64_MAX_LIFETIME_USEC)
return -EINVAL;
void sd_radv_remove_route(
sd_radv *ra,
const struct in6_addr *prefix,
uint8_t prefixlen) {
/* 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;
if (!ra || !prefix)
return;
unaligned_write_be16(&p->opt.lifetime_and_plc, pref64_lifetime);
memcpy(&p->opt.prefix, prefix, sizeof(p->opt.prefix));
ndisc_option_remove(ra->options,
&(sd_ndisc_option) {
.type = SD_NDISC_OPTION_ROUTE_INFORMATION,
.route.prefixlen = prefixlen,
.route.address = *prefix,
});
}
p->in6_addr = *prefix;
p->prefixlen = 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) {
return 0;
assert_return(ra, -EINVAL);
assert_return(dns, -EINVAL);
return ndisc_option_set_rdnss(
&ra->options,
n_dns,
dns,
lifetime_usec,
valid_until);
}
void sd_radv_clear_rdnss(sd_radv *ra) {
if (!ra)
return;
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_add_dnssl(
sd_radv *ra,
char * const *domains,
uint64_t lifetime_usec,
uint64_t valid_until) {
assert_return(ra, -EINVAL);
return ndisc_option_set_dnssl(
&ra->options,
domains,
lifetime_usec,
valid_until);
}
void sd_radv_clear_dnssl(sd_radv *ra) {
if (!ra)
return;
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_set_captive_portal(sd_radv *ra, const char *portal) {
assert_return(ra, -EINVAL);
assert_return(portal, -EINVAL);
return ndisc_option_set_captive_portal(&ra->options, portal);
}
void sd_radv_unset_captive_portal(sd_radv *ra) {
if (!ra)
return;
ndisc_option_remove_by_type(ra->options, SD_NDISC_OPTION_CAPTIVE_PORTAL);
}
int sd_radv_add_prefix64(
sd_radv *ra,
const struct in6_addr *prefix,
uint8_t prefixlen,
uint64_t lifetime_usec,
uint64_t valid_until) {
assert_return(ra, -EINVAL);
assert_return(prefix, -EINVAL);
return ndisc_option_set_prefix64(
&ra->options,
prefixlen,
prefix,
lifetime_usec,
valid_until);
}
void sd_radv_remove_prefix64(
sd_radv *ra,
const struct in6_addr *prefix,
uint8_t prefixlen) {
if (!ra || !prefix)
return;
ndisc_option_remove(ra->options,
&(sd_ndisc_option) {
.type = SD_NDISC_OPTION_PREF64,
.prefix64.prefixlen = prefixlen,
.prefix64.prefix = *prefix,
});
}
+58 -144
View File
@@ -12,6 +12,7 @@
#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"
@@ -67,92 +68,6 @@ static const struct in6_addr test_rdnss = { { { 0x20, 0x01, 0x0d, 0xb8,
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;
@@ -165,16 +80,7 @@ TEST(radv) {
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);
@@ -209,31 +115,37 @@ TEST(radv) {
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);
/* 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_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_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_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_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_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);
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_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_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);
@@ -266,9 +178,9 @@ static void verify_message(const uint8_t *buf, size_t len) {
/* 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,
@@ -276,9 +188,9 @@ static void verify_message(const uint8_t *buf, size_t len) {
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,
@@ -347,33 +259,35 @@ TEST(ra) {
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);
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);
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);
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);
+52 -115
View File
@@ -277,83 +277,6 @@ int link_reconfigure_radv_address(Address *address, Link *link) {
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;
@@ -430,9 +353,12 @@ static int radv_set_dns(Link *link, Link *uplink) {
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) {
@@ -466,9 +392,11 @@ set_domains:
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);
}
@@ -556,21 +484,40 @@ static int radv_configure(Link *link) {
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;
}
@@ -585,17 +532,15 @@ static int radv_configure(Link *link) {
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;
}
@@ -739,31 +684,23 @@ int radv_add_prefix(
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)
+58 -48
View File
@@ -32,11 +32,7 @@
_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);
@@ -54,8 +50,8 @@ int sd_radv_set_ifindex(sd_radv *ra, int interface_index);
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);
@@ -63,51 +59,65 @@ int sd_radv_set_router_lifetime(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;