1
0
mirror of https://github.com/systemd/systemd.git synced 2025-02-03 17:47:28 +03:00

Merge pull request #6616 from pfl/rdnss

networkd: RDNSS option for systemd-networkd prefix delegation
This commit is contained in:
Lennart Poettering 2017-09-07 19:01:57 +02:00 committed by GitHub
commit 9ecf63a457
9 changed files with 265 additions and 4 deletions

View File

@ -636,6 +636,13 @@
<option>IPv6ProxyNDP</option> has been set to false. Defaults to unset.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>IPv6PrefixDelegation=</varname></term>
<listitem><para>Whether to enable or disable Router Advertisement sending on a link.
Defaults to <literal>false</literal>. See the <literal>[IPv6PrefixDelegation]</literal>
and the <literal>[IPv6Prefix]</literal> sections for configuration options.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Bridge=</varname></term>
<listitem>
@ -1333,6 +1340,116 @@
</refsect1>
<refsect1>
<title>[IPv6PrefixDelegation] Section Options</title>
<para>The <literal>[IPv6PrefixDelegation]</literal> section contains
settings for sending IPv6 Router Advertisements and whether to act as
a router, if enabled via the <varname>IPv6PrefixDelegation=</varname>
option described above. IPv6 network prefixes are defined with one or
more <literal>[IPv6Prefix]</literal> sections.</para>
<variablelist class='network-directives'>
<varlistentry>
<term><varname>Managed=</varname></term>
<term><varname>OtherInformation=</varname></term>
<listitem><para>Controls whether a DHCPv6 server is used to acquire IPv6
addresses on the network link when <varname>Managed=</varname> boolean
is set to <literal>true</literal> or if only additional network
information can be obtained via DHCPv6 for the network link when
<varname>OtherInformation=</varname> boolean is set to
<literal>true</literal>. Both settings default to
<literal>false</literal>, which means that a DHCPv6 server is not being
used.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>RouterLifetimeSec=</varname></term>
<listitem><para>Configures the IPv6 router lifetime in seconds. If set,
this host also announces itself in Router Advertisements as an IPv6
router for the network link. Defaults to unset, which means the host is
not acting as a router.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>RouterPreference=</varname></term>
<listitem><para>Configures IPv6 router preference if
<varname>RouterLifetimeSec=</varname> is non-zero. Valid values are
<literal>high</literal>, <literal>medium</literal> and
<literal>low</literal>, with <literal>normal</literal> and
<literal>default</literal> added as synonyms for
<literal>medium</literal> just to make configuration easier. See
<ulink url="https://tools.ietf.org/html/rfc4191">RFC 4191</ulink>
for details. Defaults to <literal>medium</literal>.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>DNS=</varname></term>
<listitem><para>A list of recursive DNS server IPv6 addresses
distributed via Router Advertisement messages.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>DNSLifetimeSec=</varname></term>
<listitem><para>Lifetime in seconds for the DNS server addresses listed
in <varname>DNS=</varname>.</para></listitem>
</varlistentry>
</variablelist>
</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
<ulink url="https://tools.ietf.org/html/rfc4861">RFC 4861</ulink>
for further details.</para>
<variablelist class='network-directives'>
<varlistentry>
<term><varname>AddressAutoconfiguration=</varname></term>
<term><varname>OnLink=</varname></term>
<listitem><para>Boolean values to specify whether IPv6 addresses can be
autoconfigured with this prefix and whether the prefix can be used for
onlink determination. Both settings default to <literal>true</literal>
in order to ease configuration.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Prefix=</varname></term>
<listitem><para>The IPv6 prefix that is to be distributed to hosts.
Similarly to configuring static IPv6 addresses, the setting is
configured as an IPv6 prefix and its prefix length, separated by a
<literal>/</literal> character. Use multiple
<literal>[IPv6Prefix]</literal> sections to configure multiple IPv6
prefixes since prefix lifetimes, address autoconfiguration and onlink
status may differ from one prefix to another.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>PreferredLifetimeSec=</varname></term>
<term><varname>ValidLifetimeSec=</varname></term>
<listitem><para>Preferred and valid lifetimes for the prefix measured in
seconds. <varname>PreferredLifetimeSec=</varname> defaults to 604800
seconds (one week) and <varname>ValidLifetimeSec=</varname> defaults
to 2592000 seconds (30 days).</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[Bridge] Section Options</title>
<para>The <literal>[Bridge]</literal> section accepts the
following keys.</para>

View File

@ -35,12 +35,21 @@ 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_RDNSS 25
enum RAdvState {
SD_RADV_STATE_IDLE = 0,
SD_RADV_STATE_ADVERTISING = 1,
};
typedef enum RAdvState 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;
@ -63,6 +72,9 @@ struct sd_radv {
unsigned n_prefixes;
LIST_HEAD(sd_radv_prefix, prefixes);
size_t n_rdnss;
struct sd_radv_opt_dns *rdnss;
};
struct sd_radv_prefix {

View File

@ -126,6 +126,8 @@ _public_ sd_radv *sd_radv_unref(sd_radv *ra) {
sd_radv_prefix_unref(p);
}
free(ra->rdnss);
radv_reset(ra);
sd_radv_detach_event(ra);
@ -155,8 +157,8 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst,
.nd_opt_mtu_type = ND_OPT_MTU,
.nd_opt_mtu_len = 1,
};
/* Reserve iov space for RA header, linkaddr, MTU + N prefixes */
struct iovec iov[3 + ra->n_prefixes];
/* Reserve iov space for RA header, linkaddr, MTU, N prefixes, RDNSS */
struct iovec iov[4 + ra->n_prefixes];
struct msghdr msg = {
.msg_name = &dst_addr,
.msg_namelen = sizeof(dst_addr),
@ -196,6 +198,12 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst,
msg.msg_iovlen++;
}
if (ra->rdnss) {
iov[msg.msg_iovlen].iov_base = ra->rdnss;
iov[msg.msg_iovlen].iov_len = ra->rdnss->length * 8;
msg.msg_iovlen++;
}
if (sendmsg(ra->fd, &msg, 0) < 0)
return -errno;
@ -546,6 +554,42 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
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;
size_t len;
assert_return(ra, -EINVAL);
assert_return(n_dns < 128, -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 = SD_RADV_OPT_RDNSS;
opt_rdnss->length = len / 8;
opt_rdnss->lifetime = htobe32(lifetime);
memcpy(opt_rdnss + 1, dns, n_dns * sizeof(struct in6_addr));
free(ra->rdnss);
ra->rdnss = opt_rdnss;
opt_rdnss = NULL;
ra->n_rdnss = n_dns;
return 0;
}
_public_ int sd_radv_prefix_new(sd_radv_prefix **ret) {
_cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;

View File

@ -53,7 +53,7 @@ static uint8_t advertisement[] = {
0x00, 0x09, 0x3a, 0x80, 0x00, 0x00, 0x00, 0x00,
0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x01, 0x0d, 0xad,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* Recursive DNS Server Option - not yet supported */
/* Recursive DNS Server Option */
0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
@ -106,6 +106,11 @@ static struct {
false },
};
static const struct in6_addr test_rdnss = { { { 0x20, 0x01, 0x0d, 0xb8,
0xde, 0xad, 0xbe, 0xef,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01 } } };
static int test_rs_hangcheck(sd_event_source *s, uint64_t usec,
void *userdata) {
assert_se(false);
@ -207,6 +212,14 @@ static void test_radv(void) {
assert_se(sd_radv_set_other_information(ra, true) >= 0);
assert_se(sd_radv_set_other_information(ra, false) >= 0);
assert_se(sd_radv_set_rdnss(NULL, 0, NULL, 0) < 0);
assert_se(sd_radv_set_rdnss(ra, 0, NULL, 0) >= 0);
assert_se(sd_radv_set_rdnss(ra, 0, NULL, 128) < 0);
assert_se(sd_radv_set_rdnss(ra, 600, &test_rdnss, 0) >= 0);
assert_se(sd_radv_set_rdnss(ra, 600, &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);
ra = sd_radv_unref(ra);
assert_se(!ra);
}
@ -238,7 +251,7 @@ int icmp6_receive(int fd, void *iov_base, size_t iov_len,
static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
sd_radv *ra = userdata;
unsigned char buf[120];
unsigned char buf[144];
size_t i;
read(test_fd[0], &buf, sizeof(buf));
@ -254,6 +267,9 @@ static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdat
/* test only up to buf size, rest is not yet implemented */
for (i = 0; i < sizeof(buf); i++) {
if (!(i % 8))
printf("%3zd: ", i);
printf("0x%02x", buf[i]);
assert_se(buf[i] == advertisement[i]);
@ -302,6 +318,7 @@ static void test_ra(void) {
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, &test_rdnss, 1) >= 0);
for (i = 0; i < ELEMENTSOF(prefix); i++) {
sd_radv_prefix *p;

View File

@ -145,6 +145,8 @@ IPv6PrefixDelegation.RouterLifetimeSec, config_parse_sec,
IPv6PrefixDelegation.Managed, config_parse_bool, 0, offsetof(Network, router_managed)
IPv6PrefixDelegation.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information)
IPv6PrefixDelegation.RouterPreference, config_parse_router_preference, 0, 0
IPv6PrefixDelegation.DNS, config_parse_radv_dns, 0, 0
IPv6PrefixDelegation.DNSLifetimeSec, config_parse_sec, 0, offsetof(Network, router_dns_lifetime_usec)
IPv6Prefix.Prefix, config_parse_prefix, 0, 0
IPv6Prefix.OnLink, config_parse_prefix_flags, 0, 0
IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0

View File

@ -26,6 +26,7 @@
#include "dns-domain.h"
#include "fd-util.h"
#include "hostname-util.h"
#include "in-addr-util.h"
#include "network-internal.h"
#include "networkd-manager.h"
#include "networkd-network.h"
@ -1063,6 +1064,58 @@ int config_parse_dhcp_server_dns(
return 0;
}
int config_parse_radv_dns(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
Network *n = data;
const char *p = rvalue;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
for (;;) {
_cleanup_free_ char *w = NULL;
union in_addr_union a;
r = extract_first_word(&p, &w, NULL, 0);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue);
return 0;
}
if (r == 0)
break;
if (in_addr_from_string(AF_INET6, w, &a) >= 0) {
struct in6_addr *m;
m = realloc(n->router_dns, (n->n_router_dns + 1) * sizeof(struct in6_addr));
if (!m)
return log_oom();
m[n->n_router_dns++] = a.in6;
n->router_dns = m;
} else
log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DNS server address, ignoring: %s", w);
}
return 0;
}
int config_parse_dhcp_server_ntp(
const char *unit,
const char *filename,

View File

@ -165,6 +165,9 @@ struct Network {
uint8_t router_preference;
bool router_managed;
bool router_other_information;
usec_t router_dns_lifetime_usec;
struct in6_addr *router_dns;
unsigned n_router_dns;
/* Bridge Support */
bool use_bpdu;
@ -267,6 +270,7 @@ int config_parse_ipv6_privacy_extensions(const char *unit, const char *filename,
int config_parse_hostname(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_timezone(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_dhcp_server_dns(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_radv_dns(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_dhcp_server_ntp(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_dnssec_negative_trust_anchors(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_dhcp_use_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);

View File

@ -75,5 +75,15 @@ int radv_configure(Link *link) {
return r;
}
if (link->network->router_dns) {
r = sd_radv_set_rdnss(link->radv,
DIV_ROUND_UP(link->network->router_dns_lifetime_usec,
USEC_PER_SEC),
link->network->router_dns,
link->network->n_router_dns);
if (r < 0)
return r;
}
return 0;
}

View File

@ -57,6 +57,8 @@ int sd_radv_set_managed_information(sd_radv *ra, int managed);
int sd_radv_set_other_information(sd_radv *ra, int other);
int sd_radv_set_preference(sd_radv *ra, unsigned preference);
int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p);
int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime,
const struct in6_addr *dns, size_t n_dns);
/* Advertised prefixes */
int sd_radv_prefix_new(sd_radv_prefix **ret);