From: Yu Watanabe Date: Thu, 28 Feb 2019 08:12:02 +0000 (+0900) Subject: wireguard: use NetworkConfigSection to manage [WireGuardPeer] sections X-Git-Tag: v242-rc1~150^2~6 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f1368a333e5e08575f0b45dfe41e936b106a8627;p=thirdparty%2Fsystemd.git wireguard: use NetworkConfigSection to manage [WireGuardPeer] sections This also contains huge refactoring of wireguard support. --- diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c index 7d35afae6d7..3560c25eecd 100644 --- a/src/network/netdev/wireguard.c +++ b/src/network/netdev/wireguard.c @@ -24,26 +24,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 +279,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 +304,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,40 +324,53 @@ 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) { + + if (!set_isempty(w->peers_with_failed_endpoint)) { _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; w->n_retries++; @@ -342,24 +405,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 +433,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 +467,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; @@ -434,7 +500,8 @@ static int parse_wireguard_key(const char *unit, 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, + "Could not parse wireguard key \"%s\", ignoring assignment: %m", rvalue); return 0; } if (len != WG_KEY_LEN) { @@ -465,16 +532,8 @@ 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 parse_wireguard_key(unit, filename, line, section, section_line, + lvalue, ltype, rvalue, data, &w->private_key); } @@ -488,8 +547,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 +558,17 @@ 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 = parse_wireguard_key(unit, filename, line, section, section_line, + lvalue, ltype, rvalue, data, peer->preshared_key); + if (r < 0) + return r; + + TAKE_PTR(peer); + return 0; } int config_parse_wireguard_public_key(const char *unit, @@ -523,8 +581,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 +592,17 @@ 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; + + r = parse_wireguard_key(unit, filename, line, section, section_line, + lvalue, ltype, rvalue, data, peer->public_key); + if (r < 0) + return r; - return parse_wireguard_key(unit, - filename, - line, - section, - section_line, - lvalue, - ltype, - rvalue, - data, - peer->public_key); + TAKE_PTR(peer); + return 0; } int config_parse_wireguard_allowed_ips(const char *unit, @@ -558,11 +615,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 +628,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 +643,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 +668,7 @@ int config_parse_wireguard_allowed_ips(const char *unit, LIST_PREPEND(ipmasks, peer->ipmasks, ipmask); } + TAKE_PTR(peer); return 0; } @@ -619,12 +682,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 +696,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 +722,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 +761,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 +774,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 +810,29 @@ 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); - } + 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); +} - while ((e = w->unresolved_endpoints)) { - LIST_REMOVE(endpoints, w->unresolved_endpoints, e); - wireguard_endpoint_free(e); - } +static int wireguard_verify(NetDev *netdev, const char *filename) { + WireguardPeer *peer, *peer_next; + Wireguard *w; - while ((e = w->failed_endpoints)) { - LIST_REMOVE(endpoints, w->failed_endpoints, e); - wireguard_endpoint_free(e); - } + assert(netdev); + w = WIREGUARD(netdev); + assert(w); + + LIST_FOREACH_SAFE(peers, peer, peer_next, w->peers) + if (section_is_invalid(peer->section)) + wireguard_peer_free(peer); + + return 0; } const NetDevVTable wireguard_vtable = { @@ -772,4 +842,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 143e93cdc5f..72e895bf434 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,43 +16,37 @@ 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]; + 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; };