Merge pull request #11861 from yuwata/network-verify-2

network: config parser updates and wireguard refactoring
This commit is contained in:
Lennart Poettering 2019-03-13 10:15:27 +01:00 committed by GitHub
commit 96c45cc697
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 661 additions and 382 deletions

View file

@ -1113,12 +1113,24 @@
<para>The Base64 encoded private key for the interface. It can be
generated using the <command>wg genkey</command> command
(see <citerefentry project="wireguard"><refentrytitle>wg</refentrytitle><manvolnum>8</manvolnum></citerefentry>).
This option is mandatory to use WireGuard.
This option or <varname>PrivateKeyFile=</varname> is mandatory to use WireGuard.
Note that because this information is secret, you may want to set
the permissions of the .netdev file to be owned by <literal>root:systemd-network</literal>
with a <literal>0640</literal> file mode.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>PrivateKeyFile=</varname></term>
<listitem>
<para>Takes a absolute path to a file which contains the Base64 encoded private key for the interface.
If both <varname>PrivateKey=</varname> and <varname>PrivateKeyFile=</varname> are specified, and if
the file specified in <varname>PrivateKeyFile=</varname> contains valid wireguard key, then
the key provided by <varname>PrivateKey=</varname> is ignored.
Note that the file must be readable by the user <literal>systemd-network</literal>, so it
should be, e.g., owned by <literal>root:systemd-network</literal> with a
<literal>0640</literal> file mode.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ListenPort=</varname></term>
<listitem>

View file

@ -167,6 +167,7 @@ VRF.Table, config_parse_uint32, 0,
WireGuard.FwMark, config_parse_unsigned, 0, offsetof(Wireguard, fwmark)
WireGuard.ListenPort, config_parse_wireguard_listen_port, 0, offsetof(Wireguard, port)
WireGuard.PrivateKey, config_parse_wireguard_private_key, 0, 0
WireGuard.PrivateKeyFile, config_parse_wireguard_private_key_file, 0, 0
WireGuardPeer.AllowedIPs, config_parse_wireguard_allowed_ips, 0, 0
WireGuardPeer.Endpoint, config_parse_wireguard_endpoint, 0, 0
WireGuardPeer.PublicKey, config_parse_wireguard_public_key, 0, 0

View file

@ -9,13 +9,16 @@
#include "sd-resolve.h"
#include "alloc-util.h"
#include "event-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "hexdecoct.h"
#include "netlink-util.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "resolve-private.h"
#include "string-util.h"
#include "strv.h"
@ -24,26 +27,79 @@
static void resolve_endpoints(NetDev *netdev);
static WireguardPeer *wireguard_peer_new(Wireguard *w, unsigned section) {
WireguardPeer *peer;
static void wireguard_peer_free(WireguardPeer *peer) {
WireguardIPmask *mask;
if (!peer)
return;
if (peer->wireguard) {
LIST_REMOVE(peers, peer->wireguard->peers, peer);
set_remove(peer->wireguard->peers_with_unresolved_endpoint, peer);
set_remove(peer->wireguard->peers_with_failed_endpoint, peer);
if (peer->section)
hashmap_remove(peer->wireguard->peers_by_section, peer->section);
}
network_config_section_free(peer->section);
while ((mask = peer->ipmasks)) {
LIST_REMOVE(ipmasks, peer->ipmasks, mask);
free(mask);
}
free(peer->endpoint_host);
free(peer->endpoint_port);
free(peer);
}
DEFINE_NETWORK_SECTION_FUNCTIONS(WireguardPeer, wireguard_peer_free);
static int wireguard_peer_new_static(Wireguard *w, const char *filename, unsigned section_line, WireguardPeer **ret) {
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(wireguard_peer_freep) WireguardPeer *peer = NULL;
int r;
assert(w);
assert(ret);
assert(filename);
assert(section_line > 0);
if (w->last_peer_section == section && w->peers)
return w->peers;
r = network_config_section_new(filename, section_line, &n);
if (r < 0)
return r;
peer = hashmap_get(w->peers_by_section, n);
if (peer) {
*ret = TAKE_PTR(peer);
return 0;
}
peer = new(WireguardPeer, 1);
if (!peer)
return NULL;
return -ENOMEM;
*peer = (WireguardPeer) {
.flags = WGPEER_F_REPLACE_ALLOWEDIPS,
.wireguard = w,
.section = TAKE_PTR(n),
};
LIST_PREPEND(peers, w->peers, peer);
w->last_peer_section = section;
return peer;
r = hashmap_ensure_allocated(&w->peers_by_section, &network_config_hash_ops);
if (r < 0)
return r;
r = hashmap_put(w->peers_by_section, peer->section, peer);
if (r < 0)
return r;
*ret = TAKE_PTR(peer);
return 0;
}
static int wireguard_set_ipmask_one(NetDev *netdev, sd_netlink_message *message, const WireguardIPmask *mask, uint16_t index) {
@ -226,24 +282,20 @@ static int wireguard_set_interface(NetDev *netdev) {
return 0;
}
static WireguardEndpoint* wireguard_endpoint_free(WireguardEndpoint *e) {
if (!e)
return NULL;
e->host = mfree(e->host);
e->port = mfree(e->port);
return mfree(e);
static void wireguard_peer_destroy_callback(WireguardPeer *peer) {
NetDev *netdev;
assert(peer);
assert(peer->wireguard);
netdev = NETDEV(peer->wireguard);
if (section_is_invalid(peer->section))
wireguard_peer_free(peer);
netdev_unref(netdev);
}
static void wireguard_endpoint_destroy_callback(WireguardEndpoint *e) {
assert(e);
assert(e->netdev);
netdev_unref(e->netdev);
wireguard_endpoint_free(e);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(WireguardEndpoint*, wireguard_endpoint_free);
static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) {
NetDev *netdev = userdata;
Wireguard *w;
@ -255,8 +307,9 @@ static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) {
if (!netdev_is_managed(netdev))
return 0;
assert(!w->unresolved_endpoints);
w->unresolved_endpoints = TAKE_PTR(w->failed_endpoints);
assert(set_isempty(w->peers_with_unresolved_endpoint));
SWAP_TWO(w->peers_with_unresolved_endpoint, w->peers_with_failed_endpoint);
resolve_endpoints(netdev);
@ -274,63 +327,64 @@ static int exponential_backoff_milliseconds(unsigned n_retries) {
static int wireguard_resolve_handler(sd_resolve_query *q,
int ret,
const struct addrinfo *ai,
WireguardEndpoint *e) {
_cleanup_(netdev_unrefp) NetDev *netdev_will_unrefed = NULL;
WireguardPeer *peer) {
NetDev *netdev;
Wireguard *w;
int r;
assert(e);
assert(e->netdev);
assert(peer);
assert(peer->wireguard);
netdev = e->netdev;
w = WIREGUARD(netdev);
assert(w);
w = peer->wireguard;
netdev = NETDEV(w);
if (!netdev_is_managed(netdev))
return 0;
if (ret != 0) {
log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", e->host, e->port, gai_strerror(ret));
LIST_PREPEND(endpoints, w->failed_endpoints, e);
(void) sd_resolve_query_set_destroy_callback(q, NULL); /* Avoid freeing endpoint by destroy callback. */
netdev_will_unrefed = netdev; /* But netdev needs to be unrefed. */
log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", peer->endpoint_host, peer->endpoint_port, gai_strerror(ret));
r = set_ensure_allocated(&w->peers_with_failed_endpoint, NULL);
if (r < 0) {
log_oom();
peer->section->invalid = true;
goto resolve_next;
}
r = set_put(w->peers_with_failed_endpoint, peer);
if (r < 0) {
log_netdev_error(netdev, "Failed to save a peer, dropping the peer: %m");
peer->section->invalid = true;
goto resolve_next;
}
} else if ((ai->ai_family == AF_INET && ai->ai_addrlen == sizeof(struct sockaddr_in)) ||
(ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6)))
memcpy(&e->peer->endpoint, ai->ai_addr, ai->ai_addrlen);
memcpy(&peer->endpoint, ai->ai_addr, ai->ai_addrlen);
else
log_netdev_error(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint: %s:%s", e->host, e->port);
log_netdev_error(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint %s:%s, ignoring the address.",
peer->endpoint_host, peer->endpoint_port);
if (w->unresolved_endpoints) {
resolve_next:
if (!set_isempty(w->peers_with_unresolved_endpoint)) {
resolve_endpoints(netdev);
return 0;
}
(void) wireguard_set_interface(netdev);
if (w->failed_endpoints) {
_cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
if (!set_isempty(w->peers_with_failed_endpoint)) {
usec_t usec;
w->n_retries++;
r = sd_event_add_time(netdev->manager->event,
&s,
CLOCK_MONOTONIC,
now(CLOCK_MONOTONIC) + exponential_backoff_milliseconds(w->n_retries),
0,
on_resolve_retry,
netdev);
usec = usec_add(now(CLOCK_MONOTONIC), exponential_backoff_milliseconds(w->n_retries));
r = event_reset_time(netdev->manager->event, &w->resolve_retry_event_source,
CLOCK_MONOTONIC, usec, 0, on_resolve_retry, netdev,
0, "wireguard-resolve-retry", true);
if (r < 0) {
log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler: %m");
return 0;
}
r = sd_event_source_set_destroy_callback(s, (sd_event_destroy_t) netdev_destroy_callback);
if (r < 0) {
log_netdev_warning_errno(netdev, r, "Failed to set destroy callback to event source: %m");
return 0;
}
(void) sd_event_source_set_floating(s, true);
netdev_ref(netdev);
}
return 0;
@ -342,24 +396,24 @@ static void resolve_endpoints(NetDev *netdev) {
.ai_socktype = SOCK_DGRAM,
.ai_protocol = IPPROTO_UDP
};
WireguardEndpoint *endpoint;
WireguardPeer *peer;
Wireguard *w;
Iterator i;
int r = 0;
assert(netdev);
w = WIREGUARD(netdev);
assert(w);
LIST_FOREACH(endpoints, endpoint, w->unresolved_endpoints) {
SET_FOREACH(peer, w->peers_with_unresolved_endpoint, i) {
r = resolve_getaddrinfo(netdev->manager->resolve,
NULL,
endpoint->host,
endpoint->port,
peer->endpoint_host,
peer->endpoint_port,
&hints,
wireguard_resolve_handler,
wireguard_endpoint_destroy_callback,
endpoint);
wireguard_peer_destroy_callback,
peer);
if (r == -ENOBUFS)
break;
if (r < 0) {
@ -370,7 +424,7 @@ static void resolve_endpoints(NetDev *netdev) {
/* Avoid freeing netdev. It will be unrefed by the destroy callback. */
netdev_ref(netdev);
LIST_REMOVE(endpoints, w->unresolved_endpoints, endpoint);
(void) set_remove(w->peers_with_unresolved_endpoint, peer);
}
}
@ -404,9 +458,12 @@ int config_parse_wireguard_listen_port(const char *unit,
assert(data);
if (!streq(rvalue, "auto")) {
r = parse_ip_port(rvalue, &port);
if (r < 0)
log_syntax(unit, LOG_ERR, filename, line, r, "Invalid port specification, ignoring assignment: %s", rvalue);
r = parse_ip_port(rvalue, s);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Invalid port specification, ignoring assignment: %s", rvalue);
return 0;
}
}
*s = port;
@ -414,36 +471,41 @@ int config_parse_wireguard_listen_port(const char *unit,
return 0;
}
static int parse_wireguard_key(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) {
static int wireguard_decode_key_and_warn(
const char *rvalue,
uint8_t *ret,
const char *unit,
const char *filename,
unsigned line,
const char *lvalue) {
_cleanup_free_ void *key = NULL;
size_t len;
int r;
assert(filename);
assert(rvalue);
assert(userdata);
assert(ret);
assert(filename);
assert(lvalue);
if (isempty(rvalue)) {
memzero(ret, WG_KEY_LEN);
return 0;
}
r = unbase64mem(rvalue, strlen(rvalue), &key, &len);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse wireguard key \"%s\", ignoring assignment: %m", rvalue);
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to decode wireguard key provided by %s=, ignoring assignment: %m", lvalue);
return 0;
}
if (len != WG_KEY_LEN) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Wireguard key is too short, ignoring assignment: %s", rvalue);
log_syntax(unit, LOG_ERR, filename, line, 0,
"Wireguard key provided by %s= has invalid length (%zu bytes), ignoring assignment.",
lvalue, len);
return 0;
}
memcpy(userdata, key, WG_KEY_LEN);
memcpy(ret, key, WG_KEY_LEN);
return true;
}
@ -465,19 +527,44 @@ int config_parse_wireguard_private_key(const char *unit,
assert(w);
return parse_wireguard_key(unit,
filename,
line,
section,
section_line,
lvalue,
ltype,
rvalue,
data,
&w->private_key);
return wireguard_decode_key_and_warn(rvalue, w->private_key, unit, filename, line, lvalue);
}
int config_parse_wireguard_private_key_file(
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) {
_cleanup_free_ char *path = NULL;
Wireguard *w;
assert(data);
w = WIREGUARD(data);
assert(w);
if (isempty(rvalue)) {
w->private_key_file = mfree(w->private_key_file);
return 0;
}
path = strdup(rvalue);
if (!path)
return log_oom();
if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
return 0;
return free_and_replace(w->private_key_file, path);
}
int config_parse_wireguard_preshared_key(const char *unit,
const char *filename,
unsigned line,
@ -488,8 +575,10 @@ int config_parse_wireguard_preshared_key(const char *unit,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
Wireguard *w;
WireguardPeer *peer;
int r;
assert(data);
@ -497,20 +586,16 @@ int config_parse_wireguard_preshared_key(const char *unit,
assert(w);
peer = wireguard_peer_new(w, section_line);
if (!peer)
return log_oom();
r = wireguard_peer_new_static(w, filename, section_line, &peer);
if (r < 0)
return r;
return parse_wireguard_key(unit,
filename,
line,
section,
section_line,
lvalue,
ltype,
rvalue,
data,
peer->preshared_key);
r = wireguard_decode_key_and_warn(rvalue, peer->preshared_key, unit, filename, line, lvalue);
if (r < 0)
return r;
TAKE_PTR(peer);
return 0;
}
int config_parse_wireguard_public_key(const char *unit,
@ -523,8 +608,10 @@ int config_parse_wireguard_public_key(const char *unit,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
Wireguard *w;
WireguardPeer *peer;
int r;
assert(data);
@ -532,20 +619,16 @@ int config_parse_wireguard_public_key(const char *unit,
assert(w);
peer = wireguard_peer_new(w, section_line);
if (!peer)
return log_oom();
r = wireguard_peer_new_static(w, filename, section_line, &peer);
if (r < 0)
return r;
return parse_wireguard_key(unit,
filename,
line,
section,
section_line,
lvalue,
ltype,
rvalue,
data,
peer->public_key);
r = wireguard_decode_key_and_warn(rvalue, peer->public_key, unit, filename, line, lvalue);
if (r < 0)
return r;
TAKE_PTR(peer);
return 0;
}
int config_parse_wireguard_allowed_ips(const char *unit,
@ -558,11 +641,12 @@ int config_parse_wireguard_allowed_ips(const char *unit,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
union in_addr_union addr;
unsigned char prefixlen;
int r, family;
Wireguard *w;
WireguardPeer *peer;
WireguardIPmask *ipmask;
assert(rvalue);
@ -570,9 +654,11 @@ int config_parse_wireguard_allowed_ips(const char *unit,
w = WIREGUARD(data);
peer = wireguard_peer_new(w, section_line);
if (!peer)
return log_oom();
assert(w);
r = wireguard_peer_new_static(w, filename, section_line, &peer);
if (r < 0)
return r;
for (;;) {
_cleanup_free_ char *word = NULL;
@ -583,14 +669,16 @@ int config_parse_wireguard_allowed_ips(const char *unit,
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to split allowed ips \"%s\" option: %m", rvalue);
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to split allowed ips \"%s\" option: %m", rvalue);
break;
}
r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Network address is invalid, ignoring assignment: %s", word);
return 0;
log_syntax(unit, LOG_ERR, filename, line, r,
"Network address is invalid, ignoring assignment: %s", word);
continue;
}
ipmask = new(WireguardIPmask, 1);
@ -606,6 +694,7 @@ int config_parse_wireguard_allowed_ips(const char *unit,
LIST_PREPEND(ipmasks, peer->ipmasks, ipmask);
}
TAKE_PTR(peer);
return 0;
}
@ -619,12 +708,12 @@ int config_parse_wireguard_endpoint(const char *unit,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
const char *begin, *end;
Wireguard *w;
WireguardPeer *peer;
size_t len;
const char *begin, *end = NULL;
_cleanup_free_ char *host = NULL, *port = NULL;
_cleanup_(wireguard_endpoint_freep) WireguardEndpoint *endpoint = NULL;
int r;
assert(data);
assert(rvalue);
@ -633,21 +722,25 @@ int config_parse_wireguard_endpoint(const char *unit,
assert(w);
peer = wireguard_peer_new(w, section_line);
if (!peer)
return log_oom();
r = wireguard_peer_new_static(w, filename, section_line, &peer);
if (r < 0)
return r;
if (rvalue[0] == '[') {
begin = &rvalue[1];
end = strchr(rvalue, ']');
if (!end) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find matching brace of endpoint, ignoring assignment: %s", rvalue);
log_syntax(unit, LOG_ERR, filename, line, 0,
"Unable to find matching brace of endpoint, ignoring assignment: %s",
rvalue);
return 0;
}
len = end - begin;
++end;
if (*end != ':' || !*(end + 1)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find port of endpoint: %s", rvalue);
log_syntax(unit, LOG_ERR, filename, line, 0,
"Unable to find port of endpoint, ignoring assignment: %s",
rvalue);
return 0;
}
++end;
@ -655,33 +748,32 @@ int config_parse_wireguard_endpoint(const char *unit,
begin = rvalue;
end = strrchr(rvalue, ':');
if (!end || !*(end + 1)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find port of endpoint: %s", rvalue);
log_syntax(unit, LOG_ERR, filename, line, 0,
"Unable to find port of endpoint, ignoring assignment: %s",
rvalue);
return 0;
}
len = end - begin;
++end;
}
host = strndup(begin, len);
if (!host)
peer->endpoint_host = strndup(begin, len);
if (!peer->endpoint_host)
return log_oom();
port = strdup(end);
if (!port)
peer->endpoint_port = strdup(end);
if (!peer->endpoint_port)
return log_oom();
endpoint = new(WireguardEndpoint, 1);
if (!endpoint)
r = set_ensure_allocated(&w->peers_with_unresolved_endpoint, NULL);
if (r < 0)
return log_oom();
*endpoint = (WireguardEndpoint) {
.peer = TAKE_PTR(peer),
.host = TAKE_PTR(host),
.port = TAKE_PTR(port),
.netdev = data,
};
LIST_PREPEND(endpoints, w->unresolved_endpoints, TAKE_PTR(endpoint));
r = set_put(w->peers_with_unresolved_endpoint, peer);
if (r < 0)
return r;
TAKE_PTR(peer);
return 0;
}
@ -695,10 +787,11 @@ int config_parse_wireguard_keepalive(const char *unit,
const char *rvalue,
void *data,
void *userdata) {
int r;
_cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
uint16_t keepalive = 0;
Wireguard *w;
WireguardPeer *peer;
int r;
assert(rvalue);
assert(data);
@ -707,19 +800,25 @@ int config_parse_wireguard_keepalive(const char *unit,
assert(w);
peer = wireguard_peer_new(w, section_line);
if (!peer)
return log_oom();
r = wireguard_peer_new_static(w, filename, section_line, &peer);
if (r < 0)
return r;
if (streq(rvalue, "off"))
keepalive = 0;
else {
r = safe_atou16(rvalue, &keepalive);
if (r < 0)
log_syntax(unit, LOG_ERR, filename, line, r, "The persistent keepalive interval must be 0-65535. Ignore assignment: %s", rvalue);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"The persistent keepalive interval must be 0-65535. Ignore assignment: %s",
rvalue);
return 0;
}
}
peer->persistent_keepalive_interval = keepalive;
TAKE_PTR(peer);
return 0;
}
@ -737,32 +836,97 @@ static void wireguard_init(NetDev *netdev) {
static void wireguard_done(NetDev *netdev) {
Wireguard *w;
WireguardPeer *peer;
WireguardIPmask *mask;
WireguardEndpoint *e;
assert(netdev);
w = WIREGUARD(netdev);
assert(w);
while ((peer = w->peers)) {
LIST_REMOVE(peers, w->peers, peer);
while ((mask = peer->ipmasks)) {
LIST_REMOVE(ipmasks, peer->ipmasks, mask);
free(mask);
}
free(peer);
}
sd_event_source_unref(w->resolve_retry_event_source);
while ((e = w->unresolved_endpoints)) {
LIST_REMOVE(endpoints, w->unresolved_endpoints, e);
wireguard_endpoint_free(e);
}
free(w->private_key_file);
while ((e = w->failed_endpoints)) {
LIST_REMOVE(endpoints, w->failed_endpoints, e);
wireguard_endpoint_free(e);
}
hashmap_free_with_destructor(w->peers_by_section, wireguard_peer_free);
set_free(w->peers_with_unresolved_endpoint);
set_free(w->peers_with_failed_endpoint);
}
static int wireguard_read_private_key_file(Wireguard *w, bool fatal) {
_cleanup_free_ char *contents = NULL;
_cleanup_free_ void *key = NULL;
size_t size, key_len;
NetDev *netdev;
int level, r;
assert(w);
netdev = NETDEV(w);
if (!w->private_key_file)
return 0;
level = fatal ? LOG_ERR : LOG_INFO;
r = read_full_file(w->private_key_file, &contents, &size);
if (r < 0)
return log_netdev_full(netdev, level, r,
"Failed to read private key from '%s'%s: %m",
w->private_key_file, fatal ? "" : ", ignoring");
r = unbase64mem(contents, size, &key, &key_len);
if (r < 0)
return log_netdev_full(netdev, level, r,
"Failed to decode private key%s: %m",
fatal ? "" : ", ignoring");
if (key_len != WG_KEY_LEN)
return log_netdev_full(netdev, level, SYNTHETIC_ERRNO(EINVAL),
"Wireguard private key has invalid length (%zu bytes)%s: %m",
key_len, fatal ? "" : ", ignoring");
memcpy(w->private_key, key, WG_KEY_LEN);
return 0;
}
static int wireguard_peer_verify(WireguardPeer *peer) {
NetDev *netdev = NETDEV(peer->wireguard);
if (section_is_invalid(peer->section))
return -EINVAL;
if (eqzero(peer->public_key))
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
"%s: WireGuardPeer section without PublicKey= configured. "
"Ignoring [WireGuardPeer] section from line %u.",
peer->section->filename, peer->section->line);
return 0;
}
static int wireguard_verify(NetDev *netdev, const char *filename) {
WireguardPeer *peer, *peer_next;
Wireguard *w;
bool empty;
int r;
assert(netdev);
w = WIREGUARD(netdev);
assert(w);
empty = eqzero(w->private_key);
if (empty && !w->private_key_file)
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
"%s: Missing PrivateKey= or PrivateKeyFile=, ignoring.",
filename);
r = wireguard_read_private_key_file(w, empty);
if (r < 0 && empty)
return r;
LIST_FOREACH_SAFE(peers, peer, peer_next, w->peers)
if (wireguard_peer_verify(peer) < 0)
wireguard_peer_free(peer);
return 0;
}
const NetDevVTable wireguard_vtable = {
@ -772,4 +936,5 @@ const NetDevVTable wireguard_vtable = {
.init = wireguard_init,
.done = wireguard_done,
.create_type = NETDEV_CREATE_INDEPENDENT,
.config_verify = wireguard_verify,
};

View file

@ -7,10 +7,6 @@ typedef struct Wireguard Wireguard;
#include "socket-util.h"
#include "wireguard-netlink.h"
#ifndef IFNAMSIZ
#define IFNAMSIZ 16
#endif
typedef struct WireguardIPmask {
uint16_t family;
union in_addr_union ip;
@ -20,44 +16,40 @@ typedef struct WireguardIPmask {
} WireguardIPmask;
typedef struct WireguardPeer {
Wireguard *wireguard;
NetworkConfigSection *section;
uint8_t public_key[WG_KEY_LEN];
uint8_t preshared_key[WG_KEY_LEN];
uint32_t flags;
uint16_t persistent_keepalive_interval;
union sockaddr_union endpoint;
uint16_t persistent_keepalive_interval;
char *endpoint_host;
char *endpoint_port;
LIST_HEAD(WireguardIPmask, ipmasks);
LIST_FIELDS(struct WireguardPeer, peers);
} WireguardPeer;
typedef struct WireguardEndpoint {
char *host;
char *port;
NetDev *netdev;
WireguardPeer *peer;
LIST_FIELDS(struct WireguardEndpoint, endpoints);
} WireguardEndpoint;
struct Wireguard {
NetDev meta;
unsigned last_peer_section;
uint32_t flags;
uint8_t private_key[WG_KEY_LEN];
char *private_key_file;
uint16_t port;
uint32_t fwmark;
uint16_t port;
Hashmap *peers_by_section;
Set *peers_with_unresolved_endpoint;
Set *peers_with_failed_endpoint;
LIST_HEAD(WireguardPeer, peers);
LIST_HEAD(WireguardEndpoint, unresolved_endpoints);
LIST_HEAD(WireguardEndpoint, failed_endpoints);
unsigned n_retries;
sd_event_source *resolve_retry_event_source;
};
DEFINE_NETDEV_CAST(WIREGUARD, Wireguard);
@ -69,5 +61,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_listen_port);
CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_public_key);
CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_private_key);
CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_private_key_file);
CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_preshared_key);
CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_keepalive);

View file

@ -159,7 +159,7 @@ int config_parse_address_label_prefix(const char *unit,
void *data,
void *userdata) {
_cleanup_(address_label_freep) AddressLabel *n = NULL;
_cleanup_(address_label_free_or_set_invalidp) AddressLabel *n = NULL;
Network *network = userdata;
int r;
@ -196,7 +196,7 @@ int config_parse_address_label(
void *data,
void *userdata) {
_cleanup_(address_label_freep) AddressLabel *n = NULL;
_cleanup_(address_label_free_or_set_invalidp) AddressLabel *n = NULL;
Network *network = userdata;
uint32_t k;
int r;

View file

@ -11,6 +11,7 @@ typedef struct AddressLabel AddressLabel;
#include "networkd-link.h"
#include "networkd-network.h"
#include "networkd-util.h"
typedef struct Network Network;
typedef struct Link Link;
@ -30,7 +31,7 @@ struct AddressLabel {
void address_label_free(AddressLabel *label);
DEFINE_TRIVIAL_CLEANUP_FUNC(AddressLabel*, address_label_free);
DEFINE_NETWORK_SECTION_FUNCTIONS(AddressLabel, address_label_free);
int address_label_configure(AddressLabel *address, Link *link, link_netlink_message_handler_t callback, bool update);

View file

@ -39,7 +39,7 @@ int address_new(Address **ret) {
return 0;
}
int address_new_static(Network *network, const char *filename, unsigned section_line, Address **ret) {
static int address_new_static(Network *network, const char *filename, unsigned section_line, Address **ret) {
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(address_freep) Address *address = NULL;
int r;
@ -681,7 +681,7 @@ int config_parse_broadcast(
void *userdata) {
Network *network = userdata;
_cleanup_(address_freep) Address *n = NULL;
_cleanup_(address_free_or_set_invalidp) Address *n = NULL;
int r;
assert(filename);
@ -725,7 +725,7 @@ int config_parse_address(const char *unit,
void *userdata) {
Network *network = userdata;
_cleanup_(address_freep) Address *n = NULL;
_cleanup_(address_free_or_set_invalidp) Address *n = NULL;
union in_addr_union buffer;
unsigned char prefixlen;
int r, f;
@ -807,7 +807,7 @@ int config_parse_label(
void *data,
void *userdata) {
_cleanup_(address_freep) Address *n = NULL;
_cleanup_(address_free_or_set_invalidp) Address *n = NULL;
Network *network = userdata;
int r;
@ -846,7 +846,7 @@ int config_parse_lifetime(const char *unit,
void *data,
void *userdata) {
Network *network = userdata;
_cleanup_(address_freep) Address *n = NULL;
_cleanup_(address_free_or_set_invalidp) Address *n = NULL;
unsigned k;
int r;
@ -888,7 +888,7 @@ int config_parse_address_flags(const char *unit,
void *data,
void *userdata) {
Network *network = userdata;
_cleanup_(address_freep) Address *n = NULL;
_cleanup_(address_free_or_set_invalidp) Address *n = NULL;
int r;
assert(filename);
@ -936,7 +936,7 @@ int config_parse_address_scope(const char *unit,
void *data,
void *userdata) {
Network *network = userdata;
_cleanup_(address_freep) Address *n = NULL;
_cleanup_(address_free_or_set_invalidp) Address *n = NULL;
int r;
assert(filename);
@ -976,3 +976,19 @@ bool address_is_ready(const Address *a) {
else
return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED));
}
int address_section_verify(Address *address) {
if (section_is_invalid(address->section))
return -EINVAL;
if (address->family == AF_UNSPEC) {
assert(address->section);
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: Address section without Address= field configured. "
"Ignoring [Address] section from line %u.",
address->section->filename, address->section->line);
}
return 0;
}

View file

@ -11,6 +11,7 @@ typedef struct Address Address;
#include "networkd-link.h"
#include "networkd-network.h"
#include "networkd-util.h"
#define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU
@ -46,7 +47,6 @@ struct Address {
LIST_FIELDS(Address, addresses);
};
int address_new_static(Network *network, const char *filename, unsigned section, Address **ret);
int address_new(Address **ret);
void address_free(Address *address);
int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
@ -58,8 +58,9 @@ int address_configure(Address *address, Link *link, link_netlink_message_handler
int address_remove(Address *address, Link *link, link_netlink_message_handler_t callback);
bool address_equal(Address *a1, Address *a2);
bool address_is_ready(const Address *a);
int address_section_verify(Address *a);
DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free);
DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free);
CONFIG_PARSER_PROTOTYPE(config_parse_address);
CONFIG_PARSER_PROTOTYPE(config_parse_broadcast);

View file

@ -18,7 +18,7 @@
#define STATIC_FDB_ENTRIES_PER_NETWORK_MAX 1024U
/* create a new FDB entry or get an existing one. */
int fdb_entry_new_static(
static int fdb_entry_new_static(
Network *network,
const char *filename,
unsigned section_line,
@ -189,7 +189,7 @@ int config_parse_fdb_hwaddr(
void *userdata) {
Network *network = userdata;
_cleanup_(fdb_entry_freep) FdbEntry *fdb_entry = NULL;
_cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
int r;
assert(filename);
@ -235,7 +235,7 @@ int config_parse_fdb_vlan_id(
void *userdata) {
Network *network = userdata;
_cleanup_(fdb_entry_freep) FdbEntry *fdb_entry = NULL;
_cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
int r;
assert(filename);

View file

@ -8,6 +8,7 @@
#include "conf-parser.h"
#include "list.h"
#include "macro.h"
#include "networkd-util.h"
typedef struct Network Network;
typedef struct FdbEntry FdbEntry;
@ -24,11 +25,10 @@ struct FdbEntry {
LIST_FIELDS(FdbEntry, static_fdb_entries);
};
int fdb_entry_new_static(Network *network, const char *filename, unsigned section_line, FdbEntry **ret);
void fdb_entry_free(FdbEntry *fdb_entry);
int fdb_entry_configure(Link *link, FdbEntry *fdb_entry);
DEFINE_TRIVIAL_CLEANUP_FUNC(FdbEntry*, fdb_entry_free);
DEFINE_NETWORK_SECTION_FUNCTIONS(FdbEntry, fdb_entry_free);
CONFIG_PARSER_PROTOTYPE(config_parse_fdb_hwaddr);
CONFIG_PARSER_PROTOTYPE(config_parse_fdb_vlan_id);

View file

@ -50,7 +50,7 @@ static int ipv6_proxy_ndp_set(Link *link) {
return 0;
}
int ipv6_proxy_ndp_address_new_static(Network *network, IPv6ProxyNDPAddress **ret) {
static int ipv6_proxy_ndp_address_new_static(Network *network, IPv6ProxyNDPAddress **ret) {
_cleanup_(ipv6_proxy_ndp_address_freep) IPv6ProxyNDPAddress *ipv6_proxy_ndp_address = NULL;
assert(network);

View file

@ -10,13 +10,12 @@ typedef struct IPv6ProxyNDPAddress IPv6ProxyNDPAddress;
typedef struct Link Link;
struct IPv6ProxyNDPAddress {
Network *network;
struct in6_addr in_addr;
Network *network;
struct in6_addr in_addr;
LIST_FIELDS(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses);
LIST_FIELDS(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses);
};
int ipv6_proxy_ndp_address_new_static(Network *network, IPv6ProxyNDPAddress ** ipv6_proxy_ndp_address);
void ipv6_proxy_ndp_address_free(IPv6ProxyNDPAddress *ipv6_proxy_ndp_address);
int ipv6_proxy_ndp_address_configure(Link *link, IPv6ProxyNDPAddress *ipv6_proxy_ndp_address);
int ipv6_proxy_ndp_addresses_configure(Link *link);

View file

@ -164,7 +164,7 @@ int config_parse_neighbor_address(const char *unit,
void *userdata) {
Network *network = userdata;
_cleanup_(neighbor_freep) Neighbor *n = NULL;
_cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
int r;
assert(filename);
@ -200,7 +200,7 @@ int config_parse_neighbor_hwaddr(const char *unit,
void *userdata) {
Network *network = userdata;
_cleanup_(neighbor_freep) Neighbor *n = NULL;
_cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
int r;
assert(filename);

View file

@ -13,6 +13,7 @@ typedef struct Neighbor Neighbor;
#include "networkd-link.h"
#include "networkd-network.h"
#include "networkd-util.h"
struct Neighbor {
Network *network;
@ -29,7 +30,7 @@ struct Neighbor {
void neighbor_free(Neighbor *neighbor);
DEFINE_TRIVIAL_CLEANUP_FUNC(Neighbor*, neighbor_free);
DEFINE_NETWORK_SECTION_FUNCTIONS(Neighbor, neighbor_free);
int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_handler_t callback);

View file

@ -40,11 +40,11 @@ Link.RequiredForOnline, config_parse_required_for_online,
Network.Description, config_parse_string, 0, offsetof(Network, description)
Network.Bridge, config_parse_ifname, 0, offsetof(Network, bridge_name)
Network.Bond, config_parse_ifname, 0, offsetof(Network, bond_name)
Network.VLAN, config_parse_stacked_netdev, 0, offsetof(Network, stacked_netdev_names)
Network.MACVLAN, config_parse_stacked_netdev, 0, offsetof(Network, stacked_netdev_names)
Network.MACVTAP, config_parse_stacked_netdev, 0, offsetof(Network, stacked_netdev_names)
Network.IPVLAN, config_parse_stacked_netdev, 0, offsetof(Network, stacked_netdev_names)
Network.VXLAN, config_parse_stacked_netdev, 0, offsetof(Network, stacked_netdev_names)
Network.VLAN, config_parse_stacked_netdev, NETDEV_KIND_VLAN, offsetof(Network, stacked_netdev_names)
Network.MACVLAN, config_parse_stacked_netdev, NETDEV_KIND_MACVLAN, offsetof(Network, stacked_netdev_names)
Network.MACVTAP, config_parse_stacked_netdev, NETDEV_KIND_MACVTAP, offsetof(Network, stacked_netdev_names)
Network.IPVLAN, config_parse_stacked_netdev, NETDEV_KIND_IPVLAN, offsetof(Network, stacked_netdev_names)
Network.VXLAN, config_parse_stacked_netdev, NETDEV_KIND_VXLAN, offsetof(Network, stacked_netdev_names)
Network.Tunnel, config_parse_stacked_netdev, _NETDEV_KIND_TUNNEL, offsetof(Network, stacked_netdev_names)
Network.VRF, config_parse_ifname, 0, offsetof(Network, vrf_name)
Network.DHCP, config_parse_dhcp, 0, offsetof(Network, dhcp)

View file

@ -26,42 +26,6 @@
/* Let's assume that anything above this number is a user misconfiguration. */
#define MAX_NTP_SERVERS 128
static void network_config_hash_func(const NetworkConfigSection *c, struct siphash *state) {
siphash24_compress(c->filename, strlen(c->filename), state);
siphash24_compress(&c->line, sizeof(c->line), state);
}
static int network_config_compare_func(const NetworkConfigSection *x, const NetworkConfigSection *y) {
int r;
r = strcmp(x->filename, y->filename);
if (r != 0)
return r;
return CMP(x->line, y->line);
}
DEFINE_HASH_OPS(network_config_hash_ops, NetworkConfigSection, network_config_hash_func, network_config_compare_func);
int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s) {
NetworkConfigSection *cs;
cs = malloc0(offsetof(NetworkConfigSection, filename) + strlen(filename) + 1);
if (!cs)
return -ENOMEM;
strcpy(cs->filename, filename);
cs->line = line;
*s = TAKE_PTR(cs);
return 0;
}
void network_config_section_free(NetworkConfigSection *cs) {
free(cs);
}
/* Set defaults following RFC7844 */
void network_apply_anonymize_if_set(Network *network) {
if (!network->dhcp_anonymize)
@ -106,14 +70,15 @@ static int network_resolve_netdev_one(Network *network, const char *name, NetDev
NetDev *netdev;
int r;
/* For test-networkd-conf, the check must be earlier than the assertions. */
if (!name)
return 0;
assert(network);
assert(network->manager);
assert(network->filename);
assert(ret_netdev);
if (!name)
return 0;
if (kind == _NETDEV_KIND_TUNNEL)
kind_string = "tunnel";
else {
@ -195,9 +160,14 @@ static uint32_t network_get_stacked_netdevs_mtu(Network *network) {
return mtu;
}
static int network_verify(Network *network) {
int network_verify(Network *network) {
Address *address, *address_next;
Route *route, *route_next;
FdbEntry *fdb, *fdb_next;
Neighbor *neighbor, *neighbor_next;
AddressLabel *label, *label_next;
Prefix *prefix, *prefix_next;
RoutingPolicyRule *rule, *rule_next;
uint32_t mtu;
assert(network);
@ -285,34 +255,32 @@ static int network_verify(Network *network) {
}
LIST_FOREACH_SAFE(addresses, address, address_next, network->static_addresses)
if (address->family == AF_UNSPEC) {
log_warning("%s: Address section without Address= field configured. "
"Ignoring [Address] section from line %u.",
network->filename, address->section->line);
if (address_section_verify(address) < 0)
address_free(address);
}
LIST_FOREACH_SAFE(routes, route, route_next, network->static_routes) {
if (route->family == AF_UNSPEC) {
log_warning("%s: Route section without Gateway=, Destination=, Source=, "
"or PreferredSource= field configured. "
"Ignoring [Route] section from line %u.",
network->filename, route->section->line);
LIST_FOREACH_SAFE(routes, route, route_next, network->static_routes)
if (route_section_verify(route, network) < 0)
route_free(route);
continue;
}
if (network->n_static_addresses == 0 &&
in_addr_is_null(route->family, &route->gw) == 0 &&
route->gateway_onlink < 0) {
log_warning("%s: Gateway= without static address configured. "
"Enabling GatewayOnLink= option.",
network->filename);
route->gateway_onlink = true;
}
}
LIST_FOREACH_SAFE(static_fdb_entries, fdb, fdb_next, network->static_fdb_entries)
if (section_is_invalid(fdb->section))
fdb_entry_free(fdb);
LIST_FOREACH_SAFE(neighbors, neighbor, neighbor_next, network->neighbors)
if (section_is_invalid(neighbor->section))
neighbor_free(neighbor);
LIST_FOREACH_SAFE(labels, label, label_next, network->address_labels)
if (section_is_invalid(label->section))
address_label_free(label);
LIST_FOREACH_SAFE(prefixes, prefix, prefix_next, network->static_prefixes)
if (section_is_invalid(prefix->section))
prefix_free(prefix);
LIST_FOREACH_SAFE(rules, rule, rule_next, network->rules)
if (section_is_invalid(rule->section))
routing_policy_rule_free(rule);
return 0;
}
@ -460,6 +428,10 @@ int network_load_one(Manager *manager, const char *filename) {
network_apply_anonymize_if_set(network);
r = network_add_ipv4ll_route(network);
if (r < 0)
log_warning_errno(r, "%s: Failed to add IPv4LL route, ignoring: %m", network->filename);
LIST_PREPEND(networks, manager->networks, network);
network->manager = manager;
@ -674,33 +646,11 @@ int network_get(Manager *manager, sd_device *device,
}
int network_apply(Network *network, Link *link) {
int r;
assert(network);
assert(link);
link->network = network;
if (network->ipv4ll_route) {
Route *route;
r = route_new_static(network, NULL, 0, &route);
if (r < 0)
return r;
r = inet_pton(AF_INET, "169.254.0.0", &route->dst.in);
if (r == 0)
return -EINVAL;
if (r < 0)
return -errno;
route->family = AF_INET;
route->dst_prefixlen = 16;
route->scope = RT_SCOPE_LINK;
route->priority = IPV4LL_ROUTE_METRIC;
route->protocol = RTPROT_STATIC;
}
if (network->n_dns > 0 ||
!strv_isempty(network->ntp) ||
!ordered_set_isempty(network->search_domains) ||
@ -733,35 +683,18 @@ int config_parse_stacked_netdev(const char *unit,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_free_ char *kind_string = NULL, *name = NULL;
_cleanup_free_ char *name = NULL;
NetDevKind kind = ltype;
Hashmap **h = data;
NetDevKind kind;
char *p;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
if (ltype == _NETDEV_KIND_TUNNEL)
kind = _NETDEV_KIND_TUNNEL;
else {
kind_string = strdup(lvalue);
if (!kind_string)
return log_oom();
/* the keys are CamelCase versions of the kind */
for (p = kind_string; *p; p++)
*p = tolower(*p);
kind = netdev_kind_from_string(kind_string);
if (kind < 0 || IN_SET(kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND, NETDEV_KIND_VRF)) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Invalid NetDev kind: %s", lvalue);
return 0;
}
}
assert(IN_SET(kind,
NETDEV_KIND_VLAN, NETDEV_KIND_MACVLAN, NETDEV_KIND_MACVTAP,
NETDEV_KIND_IPVLAN, NETDEV_KIND_VXLAN, _NETDEV_KIND_TUNNEL));
if (!ifname_valid(rvalue)) {
log_syntax(unit, LOG_ERR, filename, line, 0,

View file

@ -84,16 +84,6 @@ typedef enum RADVPrefixDelegation {
_RADV_PREFIX_DELEGATION_INVALID = -1,
} RADVPrefixDelegation;
typedef struct NetworkConfigSection {
unsigned line;
char filename[];
} NetworkConfigSection;
int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s);
void network_config_section_free(NetworkConfigSection *network);
DEFINE_TRIVIAL_CLEANUP_FUNC(NetworkConfigSection*, network_config_section_free);
extern const struct hash_ops network_config_hash_ops;
typedef struct Manager Manager;
struct Network {
@ -296,6 +286,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_free);
int network_load(Manager *manager);
int network_load_one(Manager *manager, const char *filename);
int network_verify(Network *network);
int network_get_by_name(Manager *manager, const char *name, Network **ret);
int network_get(Manager *manager, sd_device *device, const char *ifname, const struct ether_addr *mac, Network **ret);

View file

@ -124,8 +124,8 @@ int prefix_new(Prefix **ret) {
return 0;
}
int prefix_new_static(Network *network, const char *filename,
unsigned section_line, Prefix **ret) {
static int prefix_new_static(Network *network, const char *filename,
unsigned section_line, Prefix **ret) {
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(prefix_freep) Prefix *prefix = NULL;
int r;
@ -186,7 +186,7 @@ int config_parse_prefix(const char *unit,
void *userdata) {
Network *network = userdata;
_cleanup_(prefix_freep) Prefix *p = NULL;
_cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
uint8_t prefixlen = 64;
union in_addr_union in6addr;
int r;
@ -228,7 +228,7 @@ int config_parse_prefix_flags(const char *unit,
void *data,
void *userdata) {
Network *network = userdata;
_cleanup_(prefix_freep) Prefix *p = NULL;
_cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
int r, val;
assert(filename);
@ -272,7 +272,7 @@ int config_parse_prefix_lifetime(const char *unit,
void *data,
void *userdata) {
Network *network = userdata;
_cleanup_(prefix_freep) Prefix *p = NULL;
_cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
usec_t usec;
int r;

View file

@ -8,6 +8,7 @@
#include "conf-parser.h"
#include "networkd-address.h"
#include "networkd-link.h"
#include "networkd-util.h"
typedef struct Prefix Prefix;
@ -22,9 +23,8 @@ struct Prefix {
int prefix_new(Prefix **ret);
void prefix_free(Prefix *prefix);
int prefix_new_static(Network *network, const char *filename, unsigned section, Prefix **ret);
DEFINE_TRIVIAL_CLEANUP_FUNC(Prefix*, prefix_free);
DEFINE_NETWORK_SECTION_FUNCTIONS(Prefix, prefix_free);
CONFIG_PARSER_PROTOTYPE(config_parse_router_prefix_delegation);
CONFIG_PARSER_PROTOTYPE(config_parse_router_preference);

View file

@ -67,7 +67,7 @@ int route_new(Route **ret) {
return 0;
}
int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
static int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(route_freep) Route *route = NULL;
int r;
@ -677,6 +677,34 @@ int route_configure(
return 0;
}
int network_add_ipv4ll_route(Network *network) {
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
int r;
assert(network);
if (!network->ipv4ll_route)
return 0;
/* IPv4LLRoute= is in [Network] section. */
r = route_new_static(network, NULL, 0, &n);
if (r < 0)
return r;
r = in_addr_from_string(AF_INET, "169.254.0.0", &n->dst);
if (r < 0)
return r;
n->family = AF_INET;
n->dst_prefixlen = 16;
n->scope = RT_SCOPE_LINK;
n->priority = IPV4LL_ROUTE_METRIC;
n->protocol = RTPROT_STATIC;
TAKE_PTR(n);
return 0;
}
int config_parse_gateway(
const char *unit,
const char *filename,
@ -690,7 +718,7 @@ int config_parse_gateway(
void *userdata) {
Network *network = userdata;
_cleanup_(route_freep) Route *n = NULL;
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
int r;
assert(filename);
@ -735,7 +763,7 @@ int config_parse_preferred_src(
void *userdata) {
Network *network = userdata;
_cleanup_(route_freep) Route *n = NULL;
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
int r;
assert(filename);
@ -775,7 +803,7 @@ int config_parse_destination(
void *userdata) {
Network *network = userdata;
_cleanup_(route_freep) Route *n = NULL;
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
union in_addr_union *buffer;
unsigned char *prefixlen;
int r;
@ -826,7 +854,7 @@ int config_parse_route_priority(
void *userdata) {
Network *network = userdata;
_cleanup_(route_freep) Route *n = NULL;
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
int r;
assert(filename);
@ -863,7 +891,7 @@ int config_parse_route_scope(
void *userdata) {
Network *network = userdata;
_cleanup_(route_freep) Route *n = NULL;
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
int r;
assert(filename);
@ -903,7 +931,7 @@ int config_parse_route_table(
void *data,
void *userdata) {
_cleanup_(route_freep) Route *n = NULL;
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
Network *network = userdata;
int r;
@ -941,7 +969,7 @@ int config_parse_gateway_onlink(
void *userdata) {
Network *network = userdata;
_cleanup_(route_freep) Route *n = NULL;
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
int r;
assert(filename);
@ -980,7 +1008,7 @@ int config_parse_ipv6_route_preference(
void *userdata) {
Network *network = userdata;
_cleanup_(route_freep) Route *n = NULL;
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
int r;
r = route_new_static(network, filename, section_line, &n);
@ -1015,7 +1043,7 @@ int config_parse_route_protocol(
void *userdata) {
Network *network = userdata;
_cleanup_(route_freep) Route *n = NULL;
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
int r;
r = route_new_static(network, filename, section_line, &n);
@ -1054,7 +1082,7 @@ int config_parse_route_type(
void *userdata) {
Network *network = userdata;
_cleanup_(route_freep) Route *n = NULL;
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
int r;
r = route_new_static(network, filename, section_line, &n);
@ -1093,7 +1121,7 @@ int config_parse_tcp_window(
void *data,
void *userdata) {
_cleanup_(route_freep) Route *n = NULL;
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
Network *network = userdata;
uint64_t k;
int r;
@ -1143,7 +1171,7 @@ int config_parse_quickack(
void *data,
void *userdata) {
_cleanup_(route_freep) Route *n = NULL;
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
Network *network = userdata;
int k, r;
@ -1182,7 +1210,7 @@ int config_parse_route_mtu(
void *userdata) {
Network *network = userdata;
_cleanup_(route_freep) Route *n = NULL;
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
int r;
assert(filename);
@ -1202,3 +1230,29 @@ int config_parse_route_mtu(
TAKE_PTR(n);
return 0;
}
int route_section_verify(Route *route, Network *network) {
if (section_is_invalid(route->section))
return -EINVAL;
if (route->family == AF_UNSPEC) {
assert(route->section);
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: Route section without Gateway=, Destination=, Source=, "
"or PreferredSource= field configured. "
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
}
if (network->n_static_addresses == 0 &&
in_addr_is_null(route->family, &route->gw) == 0 &&
route->gateway_onlink < 0) {
log_warning("%s: Gateway= without static address configured. "
"Enabling GatewayOnLink= option.",
network->filename);
route->gateway_onlink = true;
}
return 0;
}

View file

@ -7,6 +7,7 @@ typedef struct Route Route;
typedef struct NetworkConfigSection NetworkConfigSection;
#include "networkd-network.h"
#include "networkd-util.h"
struct Route {
Network *network;
@ -43,7 +44,6 @@ struct Route {
LIST_FIELDS(Route, routes);
};
int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret);
int route_new(Route **ret);
void route_free(Route *route);
int route_configure(Route *route, Link *link, link_netlink_message_handler_t callback);
@ -56,8 +56,11 @@ void route_update(Route *route, const union in_addr_union *src, unsigned char sr
bool route_equal(Route *r1, Route *r2);
int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata);
int route_section_verify(Route *route, Network *network);
DEFINE_TRIVIAL_CLEANUP_FUNC(Route*, route_free);
DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free);
int network_add_ipv4ll_route(Network *network);
CONFIG_PARSER_PROTOTYPE(config_parse_gateway);
CONFIG_PARSER_PROTOTYPE(config_parse_preferred_src);

View file

@ -636,7 +636,7 @@ int config_parse_routing_policy_rule_tos(
void *data,
void *userdata) {
_cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL;
_cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
Network *network = userdata;
int r;
@ -673,7 +673,7 @@ int config_parse_routing_policy_rule_priority(
void *data,
void *userdata) {
_cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL;
_cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
Network *network = userdata;
int r;
@ -710,7 +710,7 @@ int config_parse_routing_policy_rule_table(
void *data,
void *userdata) {
_cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL;
_cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
Network *network = userdata;
int r;
@ -747,7 +747,7 @@ int config_parse_routing_policy_rule_fwmark_mask(
void *data,
void *userdata) {
_cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL;
_cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
Network *network = userdata;
int r;
@ -784,7 +784,7 @@ int config_parse_routing_policy_rule_prefix(
void *data,
void *userdata) {
_cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL;
_cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
Network *network = userdata;
union in_addr_union *buffer;
uint8_t *prefixlen;
@ -831,7 +831,7 @@ int config_parse_routing_policy_rule_device(
void *data,
void *userdata) {
_cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL;
_cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
Network *network = userdata;
int r;
@ -876,7 +876,7 @@ int config_parse_routing_policy_rule_port_range(
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL;
_cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
Network *network = userdata;
uint16_t low, high;
int r;
@ -922,7 +922,7 @@ int config_parse_routing_policy_rule_ip_protocol(
void *data,
void *userdata) {
_cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL;
_cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
Network *network = userdata;
int r;
@ -961,7 +961,7 @@ int config_parse_routing_policy_rule_invert(
void *data,
void *userdata) {
_cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL;
_cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
Network *network = userdata;
int r;

View file

@ -13,6 +13,7 @@ typedef struct RoutingPolicyRule RoutingPolicyRule;
#include "networkd-link.h"
#include "networkd-network.h"
#include "networkd-util.h"
typedef struct Network Network;
typedef struct Link Link;
@ -54,7 +55,7 @@ struct RoutingPolicyRule {
int routing_policy_rule_new(RoutingPolicyRule **ret);
void routing_policy_rule_free(RoutingPolicyRule *rule);
DEFINE_TRIVIAL_CLEANUP_FUNC(RoutingPolicyRule*, routing_policy_rule_free);
DEFINE_NETWORK_SECTION_FUNCTIONS(RoutingPolicyRule, routing_policy_rule_free);
int routing_policy_rule_configure(RoutingPolicyRule *address, Link *link, link_netlink_message_handler_t callback, bool update);
int routing_policy_rule_remove(RoutingPolicyRule *routing_policy_rule, Link *link, link_netlink_message_handler_t callback);

View file

@ -102,3 +102,39 @@ int kernel_route_expiration_supported(void) {
}
return cached;
}
static void network_config_hash_func(const NetworkConfigSection *c, struct siphash *state) {
siphash24_compress(c->filename, strlen(c->filename), state);
siphash24_compress(&c->line, sizeof(c->line), state);
}
static int network_config_compare_func(const NetworkConfigSection *x, const NetworkConfigSection *y) {
int r;
r = strcmp(x->filename, y->filename);
if (r != 0)
return r;
return CMP(x->line, y->line);
}
DEFINE_HASH_OPS(network_config_hash_ops, NetworkConfigSection, network_config_hash_func, network_config_compare_func);
int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s) {
NetworkConfigSection *cs;
cs = malloc0(offsetof(NetworkConfigSection, filename) + strlen(filename) + 1);
if (!cs)
return -ENOMEM;
strcpy(cs->filename, filename);
cs->line = line;
*s = TAKE_PTR(cs);
return 0;
}
void network_config_section_free(NetworkConfigSection *cs) {
free(cs);
}

View file

@ -2,6 +2,7 @@
#pragma once
#include "conf-parser.h"
#include "hash-funcs.h"
#include "macro.h"
typedef enum AddressFamilyBoolean {
@ -14,6 +15,12 @@ typedef enum AddressFamilyBoolean {
_ADDRESS_FAMILY_BOOLEAN_INVALID = -1,
} AddressFamilyBoolean;
typedef struct NetworkConfigSection {
unsigned line;
bool invalid;
char filename[];
} NetworkConfigSection;
CONFIG_PARSER_PROTOTYPE(config_parse_address_family_boolean);
CONFIG_PARSER_PROTOTYPE(config_parse_address_family_boolean_with_kernel);
@ -21,3 +28,29 @@ const char *address_family_boolean_to_string(AddressFamilyBoolean b) _const_;
AddressFamilyBoolean address_family_boolean_from_string(const char *s) _const_;
int kernel_route_expiration_supported(void);
int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s);
void network_config_section_free(NetworkConfigSection *network);
DEFINE_TRIVIAL_CLEANUP_FUNC(NetworkConfigSection*, network_config_section_free);
extern const struct hash_ops network_config_hash_ops;
static inline bool section_is_invalid(NetworkConfigSection *section) {
/* If this retuns false, then it does _not_ mean the section is valid. */
if (!section)
return false;
return section->invalid;
}
#define DEFINE_NETWORK_SECTION_FUNCTIONS(type, free_func) \
static inline void free_func##_or_set_invalid(type *p) { \
assert(p); \
\
if (p->section) \
p->section->invalid = true; \
else \
free_func(p); \
} \
DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func); \
DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func##_or_set_invalid);

View file

@ -172,7 +172,10 @@ static void test_config_parse_address_one(const char *rvalue, int family, unsign
_cleanup_(network_freep) Network *network = NULL;
assert_se(network = new0(Network, 1));
assert_se(network->filename = strdup("hogehoge.network"));
assert_se(config_parse_address("network", "filename", 1, "section", 1, "Address", 0, rvalue, network, network) == 0);
assert_se(network->n_static_addresses == 1);
assert_se(network_verify(network) >= 0);
assert_se(network->n_static_addresses == n_addresses);
if (n_addresses > 0) {
assert_se(network->static_addresses);

View file

@ -9,6 +9,7 @@ Mode=
[WireGuard]
ListenPort=
PrivateKey=
PrivateKeyFile=
FwMark=
[MACVTAP]
Mode=

View file

@ -1,6 +1,11 @@
[Match]
Name=dummy98
[Network]
# these lines are ignored
Address=hogehoge
Address=foofoo
[Address]
Address=10.2.3.4/16
PreferredLifetime=0
@ -8,3 +13,15 @@ Scope=link
[Address]
Address=2001:0db8:0:f101::1/64
[Address]
# this section must be ignored
Peer=hoge
Address=10.10.0.1/16
Label=30
[Address]
# this section must be ignored
Label=30
Peer=hoge
Address=10.10.0.2/16

View file

@ -7,7 +7,9 @@ Description=For issue #11404
[WireGuard]
# 51820 is common port for Wireguard, 4500 is IPSec/UDP
ListenPort=4500
PrivateKey=CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=
# The key below should be overridden by PrivateKeyFile=
PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
PrivateKeyFile=/run/systemd/network/25-wireguard-private-key.txt
# peer 1
[WireGuardPeer]

View file

@ -0,0 +1,6 @@
CJQUtcS9emY2fLY
qDlpSZiE/QJyHkP
Wr+WHtZ
LZ90FU=

View file

@ -4,6 +4,7 @@ Kind=wireguard
[WireGuard]
PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
PrivateKeyFile=/run/systemd/network/not-exist
ListenPort=51820
FwMark=1234

View file

@ -242,6 +242,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-vxlan.netdev',
'25-wireguard-23-peers.netdev',
'25-wireguard-23-peers.network',
'25-wireguard-private-key.txt',
'25-wireguard.netdev',
'6rd.network',
'gre.network',
@ -454,16 +455,21 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
self.assertTrue(output, 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20')
output = subprocess.check_output(['wg', 'show', 'wg99', 'endpoints']).rstrip().decode('utf-8')
self.assertTrue(output, 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820')
output = subprocess.check_output(['wg', 'show', 'wg99', 'private-key']).rstrip().decode('utf-8')
self.assertTrue(output, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
self.assertTrue(self.link_exits('wg99'))
@expectedFailureIfModuleIsNotAvailable('wireguard')
def test_wireguard_23_peers(self):
self.copy_unit_to_networkd_unit_path('25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network')
self.copy_unit_to_networkd_unit_path('25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
'25-wireguard-private-key.txt')
self.start_networkd()
if shutil.which('wg'):
subprocess.call('wg')
output = subprocess.check_output(['wg', 'show', 'wg98', 'private-key']).rstrip().decode('utf-8')
self.assertTrue(output, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')
self.assertTrue(self.link_exits('wg98'))
@ -813,6 +819,9 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities):
print(output)
self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope link deprecated dummy98')
self.assertRegex(output, 'inet6 2001:db8:0:f101::1/64 scope global')
# also tests invalid [Address] section
self.assertNotRegex(output, '10.10.0.1/16')
self.assertNotRegex(output, '10.10.0.2/16')
def test_ip_route(self):
self.copy_unit_to_networkd_unit_path('25-route-section.network', '12-dummy.netdev')