Skip to content

Commit

Permalink
Merge pull request systemd#13559 from ssahani/ipv6ra-route
Browse files Browse the repository at this point in the history
network: make networkd able to advertise IPv6 routes on links
  • Loading branch information
yuwata committed Sep 17, 2019
2 parents b49e14d + 9633f97 commit edfbf05
Show file tree
Hide file tree
Showing 13 changed files with 451 additions and 12 deletions.
33 changes: 32 additions & 1 deletion man/systemd.network.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1886,7 +1886,7 @@
</variablelist>
</refsect1>

<refsect1>
<refsect1>
<title>[IPv6Prefix] Section Options</title>
<para>One or more <literal>[IPv6Prefix]</literal> sections contain the IPv6
prefixes that are announced via Router Advertisements. See
Expand Down Expand Up @@ -1931,6 +1931,37 @@
</variablelist>
</refsect1>

<refsect1>
<title>[IPv6RoutePrefix] Section Options</title>
<para>One or more <literal>[IPv6RoutePrefix]</literal> sections contain the IPv6
prefix routes that are announced via Router Advertisements. See
<ulink url="https://tools.ietf.org/html/rfc4191">RFC 4191</ulink>
for further details.</para>

<variablelist class='network-directives'>

<varlistentry>
<term><varname>Route=</varname></term>

<listitem><para>The IPv6 route that is to be distributed to hosts.
Similarly to configuring static IPv6 routes, the setting is
configured as an IPv6 prefix routes and its prefix route length,
separated by a<literal>/</literal> character. Use multiple
<literal>[IPv6PrefixRoutes]</literal> sections to configure multiple IPv6
prefix routes.</para></listitem>
</varlistentry>

<varlistentry>
<term><varname>LifetimeSec=</varname></term>

<listitem><para>Lifetime for the route prefix measured in
seconds. <varname>LifetimeSec=</varname> defaults to 604800 seconds (one week).
</para></listitem>
</varlistentry>

</variablelist>
</refsect1>

<refsect1>
<title>[Bridge] Section Options</title>
<para>The <literal>[Bridge]</literal> section accepts the
Expand Down
26 changes: 26 additions & 0 deletions src/libsystemd-network/radv-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ assert_cc(SD_RADV_DEFAULT_MIN_TIMEOUT_USEC <= SD_RADV_DEFAULT_MAX_TIMEOUT_USEC);
#define SD_RADV_MIN_DELAY_BETWEEN_RAS 3
#define SD_RADV_MAX_RA_DELAY_TIME_USEC (500*USEC_PER_MSEC)

#define SD_RADV_OPT_ROUTE_INFORMATION 24
#define SD_RADV_OPT_RDNSS 25
#define SD_RADV_OPT_DNSSL 31

Expand Down Expand Up @@ -58,6 +59,9 @@ struct sd_radv {
unsigned n_prefixes;
LIST_HEAD(sd_radv_prefix, prefixes);

unsigned n_route_prefixes;
LIST_HEAD(sd_radv_route_prefix, route_prefixes);

size_t n_rdnss;
struct sd_radv_opt_dns *rdnss;
struct sd_radv_opt_dns *dnssl;
Expand Down Expand Up @@ -98,6 +102,28 @@ struct sd_radv_prefix {
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);
};

#define log_radv_full(level, error, fmt, ...) log_internal(level, error, PROJECT_FILE, __LINE__, __func__, "RADV: " fmt, ##__VA_ARGS__)
#define log_radv_errno(error, fmt, ...) log_radv_full(LOG_DEBUG, error, fmt, ##__VA_ARGS__)
#define log_radv(fmt, ...) log_radv_errno(0, fmt, ##__VA_ARGS__)
130 changes: 128 additions & 2 deletions src/libsystemd-network/sd-radv.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ static sd_radv *radv_free(sd_radv *ra) {
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv, sd_radv, radv_free);

static int radv_send(sd_radv *ra, const struct in6_addr *dst, uint32_t router_lifetime) {
sd_radv_route_prefix *rt;
sd_radv_prefix *p;
struct sockaddr_in6 dst_addr = {
.sin6_family = AF_INET6,
Expand All @@ -136,9 +137,9 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, uint32_t router_li
.nd_opt_mtu_type = ND_OPT_MTU,
.nd_opt_mtu_len = 1,
};
/* Reserve iov space for RA header, linkaddr, MTU, N prefixes, RDNSS
/* Reserve iov space for RA header, linkaddr, MTU, N prefixes, N routes, RDNSS
and DNSSL */
struct iovec iov[5 + ra->n_prefixes];
struct iovec iov[5 + ra->n_prefixes + ra->n_route_prefixes];
struct msghdr msg = {
.msg_name = &dst_addr,
.msg_namelen = sizeof(dst_addr),
Expand Down Expand Up @@ -190,6 +191,9 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, uint32_t router_li
iov[msg.msg_iovlen++] = IOVEC_MAKE(&p->opt, sizeof(p->opt));
}

LIST_FOREACH(prefix, rt, ra->route_prefixes)
iov[msg.msg_iovlen++] = IOVEC_MAKE(&rt->opt, sizeof(rt->opt));

if (ra->rdnss)
iov[msg.msg_iovlen++] = IOVEC_MAKE(ra->rdnss, ra->rdnss->length * 8);

Expand Down Expand Up @@ -606,6 +610,77 @@ _public_ sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra,
return cur;
}

_public_ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p, int dynamic) {
char time_string_valid[FORMAT_TIMESPAN_MAX];
usec_t time_now, valid, valid_until;
_cleanup_free_ char *pretty = NULL;
sd_radv_route_prefix *cur;
int r;

assert_return(ra, -EINVAL);

if (!p)
return -EINVAL;

(void) in_addr_to_string(AF_INET6,
(union in_addr_union*) &p->opt.in6_addr,
&pretty);

LIST_FOREACH(prefix, cur, ra->route_prefixes) {
_cleanup_free_ char *addr = NULL;

r = in_addr_prefix_intersect(AF_INET6,
(union in_addr_union*) &cur->opt.in6_addr,
cur->opt.prefixlen,
(union in_addr_union*) &p->opt.in6_addr,
p->opt.prefixlen);
if (r < 0)
return r;
if (r == 0)
continue;

if (dynamic && cur->opt.prefixlen == p->opt.prefixlen)
goto update;

(void) in_addr_to_string(AF_INET6,
(union in_addr_union*) &cur->opt.in6_addr,
&addr);
log_radv("IPv6 route prefix %s/%u already configured, ignoring %s/%u",
strempty(addr), cur->opt.prefixlen,
strempty(pretty), p->opt.prefixlen);

return -EEXIST;
}

p = sd_radv_route_prefix_ref(p);

LIST_APPEND(prefix, ra->route_prefixes, p);
ra->n_route_prefixes++;

cur = p;
if (!dynamic) {
log_radv("Added prefix %s/%u", strempty(pretty), p->opt.prefixlen);
return 0;
}

update:
r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
return r;

valid = be32toh(p->opt.lifetime) * USEC_PER_SEC;
valid_until = usec_add(valid, time_now);
if (valid_until == USEC_INFINITY)
return -EOVERFLOW;

log_radv("%s route prefix %s/%u valid %s",
cur? "Updated": "Added",
strempty(pretty), p->opt.prefixlen,
format_timespan(time_string_valid, FORMAT_TIMESPAN_MAX, valid, USEC_PER_SEC));

return 0;
}

_public_ int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime,
const struct in6_addr *dns, size_t n_dns) {
_cleanup_free_ struct sd_radv_opt_dns *opt_rdnss = NULL;
Expand Down Expand Up @@ -770,3 +845,54 @@ _public_ int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p,

return 0;
}

_public_ 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 = SD_RADV_OPT_ROUTE_INFORMATION,
.opt.length = DIV_ROUND_UP(sizeof(p->opt), 8),
.opt.prefixlen = 64,

.opt.lifetime = htobe32(604800),
};

*ret = p;
return 0;
}

DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_route_prefix, sd_radv_route_prefix, mfree);

_public_ int sd_radv_prefix_set_route_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("Unusual prefix length %u greater than 64", prefixlen);

p->opt.in6_addr = *in6_addr;
p->opt.prefixlen = prefixlen;

return 0;
}

_public_ int sd_radv_route_prefix_set_lifetime(sd_radv_route_prefix *p, uint32_t valid_lifetime) {
assert_return(p, -EINVAL);

p->opt.lifetime = htobe32(valid_lifetime);

return 0;
}
2 changes: 2 additions & 0 deletions src/network/networkd-network-gperf.gperf
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ IPv6Prefix.OnLink, config_parse_prefix_flags,
IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0
IPv6Prefix.ValidLifetimeSec, config_parse_prefix_lifetime, 0, 0
IPv6Prefix.PreferredLifetimeSec, config_parse_prefix_lifetime, 0, 0
IPv6RoutePrefix.Route, config_parse_route_prefix, 0, 0
IPv6RoutePrefix.LifetimeSec, config_parse_route_prefix_lifetime, 0, 0
CAN.BitRate, config_parse_si_size, 0, offsetof(Network, can_bitrate)
CAN.SamplePoint, config_parse_permille, 0, offsetof(Network, can_sample_point)
CAN.RestartSec, config_parse_sec, 0, offsetof(Network, can_restart_us)
Expand Down
1 change: 1 addition & 0 deletions src/network/networkd-network.c
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,7 @@ int network_load_one(Manager *manager, const char *filename) {
"BridgeVLAN\0"
"IPv6PrefixDelegation\0"
"IPv6Prefix\0"
"IPv6RoutePrefix\0"
"CAN\0",
config_item_perf_lookup, network_network_gperf_lookup,
CONFIG_PARSE_WARN, network);
Expand Down
3 changes: 3 additions & 0 deletions src/network/networkd-network.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ struct Network {
LIST_HEAD(Neighbor, neighbors);
LIST_HEAD(AddressLabel, address_labels);
LIST_HEAD(Prefix, static_prefixes);
LIST_HEAD(Prefix, static_route_prefixes);
LIST_HEAD(RoutingPolicyRule, rules);

unsigned n_static_addresses;
Expand All @@ -230,6 +231,7 @@ struct Network {
unsigned n_neighbors;
unsigned n_address_labels;
unsigned n_static_prefixes;
unsigned n_static_route_prefixes;
unsigned n_rules;

Hashmap *addresses_by_section;
Expand All @@ -238,6 +240,7 @@ struct Network {
Hashmap *neighbors_by_section;
Hashmap *address_labels_by_section;
Hashmap *prefixes_by_section;
Hashmap *route_prefixes_by_section;
Hashmap *rules_by_section;

/* All kinds of DNS configuration */
Expand Down
Loading

0 comments on commit edfbf05

Please sign in to comment.