diff options
author | Lennart Poettering <lennart@poettering.net> | 2019-03-13 10:15:27 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-03-13 10:15:27 +0100 |
commit | 96c45cc6972bdb30306c09a69ef28da6edc85041 (patch) | |
tree | 277f49d02033cb18cd2516f3e24553ec3e57be27 /src | |
parent | Merge pull request #11893 from yuwata/wait-online-take-operstate (diff) | |
parent | test-network: add test for WireGuard.PrivateKeyFile= (diff) | |
download | systemd-96c45cc6972bdb30306c09a69ef28da6edc85041.tar.xz systemd-96c45cc6972bdb30306c09a69ef28da6edc85041.zip |
Merge pull request #11861 from yuwata/network-verify-2
network: config parser updates and wireguard refactoring
Diffstat (limited to 'src')
25 files changed, 608 insertions, 377 deletions
diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index f7ca98fa46..29bdc65a21 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -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 diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c index 7d35afae6d..eefb543c4d 100644 --- a/src/network/netdev/wireguard.c +++ b/src/network/netdev/wireguard.c @@ -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,23 +282,19 @@ 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; -static void wireguard_endpoint_destroy_callback(WireguardEndpoint *e) { - assert(e); - assert(e->netdev); + assert(peer); + assert(peer->wireguard); - netdev_unref(e->netdev); - wireguard_endpoint_free(e); -} + netdev = NETDEV(peer->wireguard); -DEFINE_TRIVIAL_CLEANUP_FUNC(WireguardEndpoint*, wireguard_endpoint_free); + if (section_is_invalid(peer->section)) + wireguard_peer_free(peer); + + netdev_unref(netdev); +} static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) { NetDev *netdev = userdata; @@ -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,17 +527,42 @@ 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, @@ -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, }; diff --git a/src/network/netdev/wireguard.h b/src/network/netdev/wireguard.h index 143e93cdc5..6cf6eec14d 100644 --- a/src/network/netdev/wireguard.h +++ b/src/network/netdev/wireguard.h @@ -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); diff --git a/src/network/networkd-address-label.c b/src/network/networkd-address-label.c index 94a12f8bfe..ab738448c5 100644 --- a/src/network/networkd-address-label.c +++ b/src/network/networkd-address-label.c @@ -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; diff --git a/src/network/networkd-address-label.h b/src/network/networkd-address-label.h index 6922cb0faf..595072a17e 100644 --- a/src/network/networkd-address-label.h +++ b/src/network/networkd-address-label.h @@ -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); diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 7712fd6ae3..1a9ba4223c 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -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; +} diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 4714c07d4d..455985d225 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -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); diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c index 324f6a5a1a..fa13949538 100644 --- a/src/network/networkd-fdb.c +++ b/src/network/networkd-fdb.c @@ -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); diff --git a/src/network/networkd-fdb.h b/src/network/networkd-fdb.h index 55fc3a2170..6b7da2e741 100644 --- a/src/network/networkd-fdb.h +++ b/src/network/networkd-fdb.h @@ -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); diff --git a/src/network/networkd-ipv6-proxy-ndp.c b/src/network/networkd-ipv6-proxy-ndp.c index 98d4205547..e2d77e9ad2 100644 --- a/src/network/networkd-ipv6-proxy-ndp.c +++ b/src/network/networkd-ipv6-proxy-ndp.c @@ -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); diff --git a/src/network/networkd-ipv6-proxy-ndp.h b/src/network/networkd-ipv6-proxy-ndp.h index 546e8561f1..d6666beab5 100644 --- a/src/network/networkd-ipv6-proxy-ndp.h +++ b/src/network/networkd-ipv6-proxy-ndp.h @@ -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); diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c index 713bad2bba..d0275fdd3e 100644 --- a/src/network/networkd-neighbor.c +++ b/src/network/networkd-neighbor.c @@ -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); diff --git a/src/network/networkd-neighbor.h b/src/network/networkd-neighbor.h index 094bf7977e..f591f0b03f 100644 --- a/src/network/networkd-neighbor.h +++ b/src/network/networkd-neighbor.h @@ -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); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 73a7fcdf08..f7f61699e7 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -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) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index fe26720103..efbbd58b92 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -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, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 2561bba1bc..cc4ac2e0d1 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -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); diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index ecf984e226..8cb14b588c 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -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; diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h index bb88f8a407..3192bb8b6c 100644 --- a/src/network/networkd-radv.h +++ b/src/network/networkd-radv.h @@ -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); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 2074698780..379077cbfd 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -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; +} diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 7a9fc161bb..1e8320fdc0 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -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); diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c index ae94272781..f6253215ed 100644 --- a/src/network/networkd-routing-policy-rule.c +++ b/src/network/networkd-routing-policy-rule.c @@ -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; diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h index b35126e2cf..e1bd78f809 100644 --- a/src/network/networkd-routing-policy-rule.h +++ b/src/network/networkd-routing-policy-rule.h @@ -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); diff --git a/src/network/networkd-util.c b/src/network/networkd-util.c index 9b6bc12858..a392aadd4c 100644 --- a/src/network/networkd-util.c +++ b/src/network/networkd-util.c @@ -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); +} diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h index 3c0c279b97..a49e289351 100644 --- a/src/network/networkd-util.h +++ b/src/network/networkd-util.h @@ -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); diff --git a/src/network/test-networkd-conf.c b/src/network/test-networkd-conf.c index 86d4e7e733..6408719d15 100644 --- a/src/network/test-networkd-conf.c +++ b/src/network/test-networkd-conf.c @@ -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); |