summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2018-09-07 09:54:07 +0200
committerThomas Haller <thaller@redhat.com>2018-09-07 11:24:17 +0200
commit62d14e188489fab4ea8b20527925b47dc2c15f40 (patch)
treef3d3af0260126245593f951b1e65246ec4113984
parentcb23779e0acd1b7db3c9f5367ac03f3bc76a562c (diff)
downloadNetworkManager-62d14e188489fab4ea8b20527925b47dc2c15f40.tar.gz
platform/wireguard: rework parsing wireguard links in platform
- previously, parsing wireguard genl data resulted in memory corruption: - _wireguard_update_from_allowedips_nla() takes pointers to allowedip = &g_array_index (buf->allowedips, NMWireGuardAllowedIP, buf->allowedips->len - 1); but resizing the GArray will invalidate this pointer. This happens when there are multiple allowed-ips to parse. - there was some confusion who owned the allowedips pointers. _wireguard_peers_cpy() and _vt_cmd_obj_dispose_lnk_wireguard() assumed each peer owned their own chunk, but _wireguard_get_link_properties() would not duplicate the memory properly. - rework memory handling for allowed_ips. Now, the NMPObjectLnkWireGuard keeps a pointer _allowed_ips_buf. This buffer contains the instances for all peers. The parsing of the netlink message is the complicated part, because we don't know upfront how many peers/allowed-ips we receive. During construction, the tracking of peers/allowed-ips is complicated, via a CList/GArray. At the end of that, we prettify the data representation and put everything into two buffers. That is more efficient and simpler for user afterwards. This moves complexity to the way how the object is created, vs. how it is used later. - ensure that we nm_explicit_bzero() private-key and preshared-key. However, that only works to a certain point, because our netlink library does not ensure that no data is leaked. - don't use a "struct sockaddr" union for the peer's endpoint. Instead, use a combintation of endpoint_family, endpoint_port, and endpoint_addr. - a lot of refactoring.
-rw-r--r--libnm-core/nm-core-types-internal.h25
-rw-r--r--shared/nm-utils/nm-hash-utils.h5
-rw-r--r--src/platform/nm-linux-platform.c434
-rw-r--r--src/platform/nm-platform.c105
-rw-r--r--src/platform/nm-platform.h12
-rw-r--r--src/platform/nmp-object.c301
-rw-r--r--src/platform/nmp-object.h40
7 files changed, 555 insertions, 367 deletions
diff --git a/libnm-core/nm-core-types-internal.h b/libnm-core/nm-core-types-internal.h
index 7ab0f59455..4d43aaf45d 100644
--- a/libnm-core/nm-core-types-internal.h
+++ b/libnm-core/nm-core-types-internal.h
@@ -31,31 +31,6 @@ typedef struct {
guint32 to;
} NMVlanQosMapping;
-typedef struct {
- NMIPAddr ip;
- guint8 family;
- guint8 mask;
-} NMWireGuardAllowedIP;
-
-#define NM_WG_PUBLIC_KEY_LEN 32
-#define NM_WG_SYMMETRIC_KEY_LEN 32
-
-typedef struct {
- guint8 public_key[NM_WG_PUBLIC_KEY_LEN];
- guint8 preshared_key[NM_WG_SYMMETRIC_KEY_LEN];
- union {
- struct sockaddr addr;
- struct sockaddr_in addr4;
- struct sockaddr_in6 addr6;
- } endpoint;
- guint16 persistent_keepalive_interval;
- struct timespec last_handshake_time;
- guint64 rx_bytes, tx_bytes;
-
- gsize allowedips_len;
- NMWireGuardAllowedIP *allowedips;
-} NMWireGuardPeer;
-
#define _NM_IP_TUNNEL_FLAG_ALL_IP6TNL \
( NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT \
| NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS \
diff --git a/shared/nm-utils/nm-hash-utils.h b/shared/nm-utils/nm-hash-utils.h
index 7d9620b96c..b797fb75af 100644
--- a/shared/nm-utils/nm-hash-utils.h
+++ b/shared/nm-utils/nm-hash-utils.h
@@ -57,6 +57,11 @@ nm_hash_update (NMHashState *state, const void *ptr, gsize n)
nm_assert (ptr);
nm_assert (n > 0);
+ /* Note: the data passed in here might be sensitive data (secrets),
+ * that we should nm_explicty_zero() afterwards. However, since
+ * we are using siphash24 with a random key, that is not really
+ * necessary. Something to keep in mind, if we ever move away from
+ * this hash implementation. */
c_siphash_append (&state->_state, ptr, n);
}
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 0de0884497..e73d5d8c3d 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -44,6 +44,7 @@
#include "nm-core-internal.h"
#include "nm-setting-vlan.h"
+#include "nm-utils/nm-secret-utils.h"
#include "nm-netlink.h"
#include "nm-core-utils.h"
#include "nmp-object.h"
@@ -1912,63 +1913,60 @@ _parse_lnk_vxlan (const char *kind, struct nlattr *info_data)
/*****************************************************************************/
-/* Context to build a NMPObjectLnkWireGuard instance.
- * GArray wrappers are discarded after processing all netlink messages. */
-struct _wireguard_device_buf {
- NMPObject *obj;
- GArray *peers;
- GArray *allowedips;
-};
-
static gboolean
-_wireguard_update_from_allowedips_nla (struct _wireguard_device_buf *buf,
- struct nlattr *allowedip_attr)
+_wireguard_update_from_allowed_ips_nla (NMPWireGuardAllowedIP *allowed_ip,
+ struct nlattr *nlattr)
{
static const struct nla_policy policy[WGALLOWEDIP_A_MAX + 1] = {
[WGALLOWEDIP_A_FAMILY] = { .type = NLA_U16 },
[WGALLOWEDIP_A_IPADDR] = { .minlen = sizeof (struct in_addr) },
[WGALLOWEDIP_A_CIDR_MASK] = { .type = NLA_U8 },
};
- struct nlattr *tba[WGALLOWEDIP_A_MAX + 1];
- NMWireGuardPeer *peer = &g_array_index (buf->peers, NMWireGuardPeer, buf->peers->len - 1);
- NMWireGuardAllowedIP *allowedip;
- NMWireGuardAllowedIP new_allowedip = {0};
+ struct nlattr *tb[WGALLOWEDIP_A_MAX + 1];
+ int family;
int addr_len;
- int nlerr;
- nlerr = nla_parse_nested (tba, WGALLOWEDIP_A_MAX, allowedip_attr, policy);
- if (nlerr < 0)
+ if (nla_parse_nested (tb, WGALLOWEDIP_A_MAX, nlattr, policy) < 0)
return FALSE;
- g_array_append_val (buf->allowedips, new_allowedip);
- allowedip = &g_array_index (buf->allowedips, NMWireGuardAllowedIP, buf->allowedips->len - 1);
- peer->allowedips_len++;
-
- if (tba[WGALLOWEDIP_A_FAMILY])
- allowedip->family = nla_get_u16 (tba[WGALLOWEDIP_A_FAMILY]);
+ if (!tb[WGALLOWEDIP_A_FAMILY])
+ return FALSE;
- if (allowedip->family == AF_INET)
+ family = nla_get_u16 (tb[WGALLOWEDIP_A_FAMILY]);
+ if (family == AF_INET)
addr_len = sizeof (in_addr_t);
- else if (allowedip->family == AF_INET6)
+ else if (family == AF_INET6)
addr_len = sizeof (struct in6_addr);
else
return FALSE;
- _check_addr_or_return_val (tba, WGALLOWEDIP_A_IPADDR, addr_len, FALSE);
- if (tba[WGALLOWEDIP_A_IPADDR])
- nla_memcpy (&allowedip->ip, tba[WGALLOWEDIP_A_IPADDR], addr_len);
- if (tba[WGALLOWEDIP_A_CIDR_MASK])
- allowedip->mask = nla_get_u8 (tba[WGALLOWEDIP_A_CIDR_MASK]);
+ _check_addr_or_return_val (tb, WGALLOWEDIP_A_IPADDR, addr_len, FALSE);
+
+ memset (allowed_ip, 0, sizeof (NMPWireGuardAllowedIP));
+
+ allowed_ip->family = family;
+ nm_assert ((int) allowed_ip->family == family);
+
+ if (tb[WGALLOWEDIP_A_IPADDR])
+ nla_memcpy (&allowed_ip->addr, tb[WGALLOWEDIP_A_IPADDR], addr_len);
+ if (tb[WGALLOWEDIP_A_CIDR_MASK])
+ allowed_ip->mask = nla_get_u8 (tb[WGALLOWEDIP_A_CIDR_MASK]);
return TRUE;
}
+typedef struct {
+ CList lst;
+ NMPWireGuardPeer data;
+} WireGuardPeerConstruct;
+
static gboolean
-_wireguard_update_from_peers_nla (struct _wireguard_device_buf *buf,
+_wireguard_update_from_peers_nla (CList *peers,
+ GArray **p_allowed_ips,
struct nlattr *peer_attr)
{
static const struct nla_policy policy[WGPEER_A_MAX + 1] = {
- [WGPEER_A_PUBLIC_KEY] = { .minlen = NM_WG_PUBLIC_KEY_LEN },
+ [WGPEER_A_PUBLIC_KEY] = { .minlen = NMP_WIREGUARD_PUBLIC_KEY_LEN },
[WGPEER_A_PRESHARED_KEY] = { },
[WGPEER_A_FLAGS] = { .type = NLA_U32 },
[WGPEER_A_ENDPOINT] = { },
@@ -1978,10 +1976,8 @@ _wireguard_update_from_peers_nla (struct _wireguard_device_buf *buf,
[WGPEER_A_TX_BYTES] = { .type = NLA_U64 },
[WGPEER_A_ALLOWEDIPS] = { .type = NLA_NESTED },
};
+ WireGuardPeerConstruct *peer_c;
struct nlattr *tb[WGPEER_A_MAX + 1];
- NMWireGuardPeer *const last = buf->peers->len ? &g_array_index (buf->peers, NMWireGuardPeer, buf->peers->len - 1) : NULL;
- NMWireGuardPeer *peer;
- NMWireGuardPeer new_peer = { 0 };
if (nla_parse_nested (tb, WGPEER_A_MAX, peer_attr, policy) < 0)
return FALSE;
@@ -1990,51 +1986,98 @@ _wireguard_update_from_peers_nla (struct _wireguard_device_buf *buf,
return FALSE;
/* a peer with the same public key as last peer is just a continuation for extra AllowedIPs */
- if ( last
- && !memcmp (nla_data (tb[WGPEER_A_PUBLIC_KEY]), last->public_key, sizeof (last->public_key)))
- peer = last;
+ peer_c = c_list_last_entry (peers, WireGuardPeerConstruct, lst);
+ if ( peer_c
+ && !memcmp (nla_data (tb[WGPEER_A_PUBLIC_KEY]), peer_c->data.public_key, NMP_WIREGUARD_PUBLIC_KEY_LEN)) {
+ G_STATIC_ASSERT_EXPR (NMP_WIREGUARD_PUBLIC_KEY_LEN == sizeof (peer_c->data.public_key));
+ /* this message is a continuation of the previous peer.
+ * Only parse WGPEER_A_ALLOWEDIPS below. */
+ }
else {
/* otherwise, start a new peer */
- g_array_append_val (buf->peers, new_peer);
- peer = &g_array_index (buf->peers, NMWireGuardPeer, buf->peers->len - 1);
+ peer_c = g_slice_new0 (WireGuardPeerConstruct);
+ c_list_link_tail (peers, &peer_c->lst);
- nla_memcpy (&peer->public_key, tb[WGPEER_A_PUBLIC_KEY], sizeof (peer->public_key));
+ nla_memcpy (&peer_c->data.public_key, tb[WGPEER_A_PUBLIC_KEY], sizeof (peer_c->data.public_key));
- if (tb[WGPEER_A_PRESHARED_KEY])
- nla_memcpy (&peer->preshared_key, tb[WGPEER_A_PRESHARED_KEY], sizeof (peer->preshared_key));
+ if (tb[WGPEER_A_PRESHARED_KEY]) {
+ nla_memcpy (&peer_c->data.preshared_key, tb[WGPEER_A_PRESHARED_KEY], sizeof (peer_c->data.preshared_key));
+ /* FIXME(netlink-bzero-secret) */
+ nm_explicit_bzero (nla_data (tb[WGPEER_A_PRESHARED_KEY]),
+ nla_len (tb[WGPEER_A_PRESHARED_KEY]));
+ }
if (tb[WGPEER_A_ENDPOINT]) {
- struct sockaddr *addr = nla_data (tb[WGPEER_A_ENDPOINT]);
- if (addr->sa_family == AF_INET)
- nla_memcpy (&peer->endpoint.addr4, tb[WGPEER_A_ENDPOINT], sizeof (peer->endpoint.addr4));
- else if (addr->sa_family == AF_INET6)
- nla_memcpy (&peer->endpoint.addr6, tb[WGPEER_A_ENDPOINT], sizeof (peer->endpoint.addr6));
+ const struct sockaddr *addr = nla_data (tb[WGPEER_A_ENDPOINT]);
+ unsigned short family;
+
+ G_STATIC_ASSERT (sizeof (addr->sa_family) == sizeof (family));
+ memcpy (&family, &addr->sa_family, sizeof (addr->sa_family));
+
+ if ( family == AF_INET
+ && nla_len (tb[WGPEER_A_ENDPOINT]) == sizeof (struct sockaddr_in)) {
+ const struct sockaddr_in *addr4 = (const struct sockaddr_in *) addr;
+
+ peer_c->data.endpoint_family = AF_INET;
+ peer_c->data.endpoint_port = unaligned_read_be16 (&addr4->sin_port);
+ peer_c->data.endpoint_addr.addr4 = unaligned_read_ne32 (&addr4->sin_addr.s_addr);
+ memcpy (&peer_c->data.endpoint_addr.addr4, &addr4->sin_addr.s_addr, 4);
+ } else if ( family == AF_INET6
+ && nla_len (tb[WGPEER_A_ENDPOINT]) == sizeof (struct sockaddr_in6)) {
+ const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *) addr;
+
+ peer_c->data.endpoint_family = AF_INET6;
+ peer_c->data.endpoint_port = unaligned_read_be16 (&addr6->sin6_port);
+ memcpy (&peer_c->data.endpoint_addr.addr6, &addr6->sin6_addr, 16);
+ }
}
if (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL])
- peer->persistent_keepalive_interval = nla_get_u64 (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]);
+ peer_c->data.persistent_keepalive_interval = nla_get_u64 (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]);
if (tb[WGPEER_A_LAST_HANDSHAKE_TIME])
- nla_memcpy (&peer->last_handshake_time, tb[WGPEER_A_LAST_HANDSHAKE_TIME], sizeof (peer->last_handshake_time));
+ nla_memcpy (&peer_c->data.last_handshake_time, tb[WGPEER_A_LAST_HANDSHAKE_TIME], sizeof (peer_c->data.last_handshake_time));
if (tb[WGPEER_A_RX_BYTES])
- peer->rx_bytes = nla_get_u64 (tb[WGPEER_A_RX_BYTES]);
+ peer_c->data.rx_bytes = nla_get_u64 (tb[WGPEER_A_RX_BYTES]);
if (tb[WGPEER_A_TX_BYTES])
- peer->tx_bytes = nla_get_u64 (tb[WGPEER_A_TX_BYTES]);
-
- peer->allowedips = NULL;
- peer->allowedips_len = 0;
+ peer_c->data.tx_bytes = nla_get_u64 (tb[WGPEER_A_TX_BYTES]);
}
if (tb[WGPEER_A_ALLOWEDIPS]) {
struct nlattr *attr;
int rem;
+ GArray *allowed_ips = *p_allowed_ips;
nla_for_each_nested (attr, tb[WGPEER_A_ALLOWEDIPS], rem) {
- if (!_wireguard_update_from_allowedips_nla (buf, attr))
- return FALSE;
+ if (!allowed_ips) {
+ allowed_ips = g_array_new (FALSE, FALSE, sizeof (NMPWireGuardAllowedIP));
+ *p_allowed_ips = allowed_ips;
+ g_array_set_size (allowed_ips, 1);
+ } else
+ g_array_set_size (allowed_ips, allowed_ips->len + 1);
+
+ if (!_wireguard_update_from_allowed_ips_nla (&g_array_index (allowed_ips,
+ NMPWireGuardAllowedIP,
+ allowed_ips->len - 1),
+ attr)) {
+ /* we ignore the error of parsing one allowed-ip. */
+ g_array_set_size (allowed_ips, allowed_ips->len - 1);
+ continue;
+ }
+
+ if (!peer_c->data._construct_idx_end)
+ peer_c->data._construct_idx_start = allowed_ips->len - 1;
+ peer_c->data._construct_idx_end = allowed_ips->len;
}
}
return TRUE;
}
+typedef struct {
+ const int ifindex;
+ NMPObject *obj;
+ CList peers;
+ GArray *allowed_ips;
+} WireGuardParseData;
+
static int
_wireguard_get_device_cb (struct nl_msg *msg, void *arg)
{
@@ -2048,95 +2091,196 @@ _wireguard_get_device_cb (struct nl_msg *msg, void *arg)
[WGDEVICE_A_FWMARK] = { .type = NLA_U32 },
[WGDEVICE_A_PEERS] = { .type = NLA_NESTED },
};
- struct _wireguard_device_buf *buf = arg;
+ WireGuardParseData *parse_data = arg;
struct nlattr *tb[WGDEVICE_A_MAX + 1];
- NMPlatformLnkWireGuard *props = &buf->obj->lnk_wireguard;
- struct nlmsghdr *nlh = nlmsg_hdr (msg);
int nlerr;
- nlerr = genlmsg_parse (nlh, 0, tb, WGDEVICE_A_MAX, policy);
+ nlerr = genlmsg_parse (nlmsg_hdr (msg), 0, tb, WGDEVICE_A_MAX, policy);
if (nlerr < 0)
return NL_SKIP;
- if (tb[WGDEVICE_A_PRIVATE_KEY])
- nla_memcpy (props->private_key, tb[WGDEVICE_A_PRIVATE_KEY], sizeof (props->private_key));
- if (tb[WGDEVICE_A_PUBLIC_KEY])
- nla_memcpy (props->public_key, tb[WGDEVICE_A_PUBLIC_KEY], sizeof (props->public_key));
- if (tb[WGDEVICE_A_LISTEN_PORT])
- props->listen_port = nla_get_u16 (tb[WGDEVICE_A_LISTEN_PORT]);
- if (tb[WGDEVICE_A_FWMARK])
- props->fwmark = nla_get_u32 (tb[WGDEVICE_A_FWMARK]);
+ if (tb[WGDEVICE_A_IFINDEX]) {
+ int ifindex;
+
+ ifindex = (int) nla_get_u32 (tb[WGDEVICE_A_IFINDEX]);
+ if ( ifindex <= 0
+ || parse_data->ifindex != ifindex)
+ return NL_SKIP;
+ } else {
+ if (!parse_data->obj)
+ return NL_SKIP;
+ }
+
+ if (parse_data->obj) {
+ /* we already have an object instance. This means the netlink message
+ * is a continuation, only providing more WGDEVICE_A_PEERS data below. */
+ } else {
+ NMPObject *obj;
+ NMPlatformLnkWireGuard *props;
+
+ obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_WIREGUARD, NULL);
+ props = &obj->lnk_wireguard;
+
+ if (tb[WGDEVICE_A_PRIVATE_KEY]) {
+ nla_memcpy (props->private_key, tb[WGDEVICE_A_PRIVATE_KEY], sizeof (props->private_key));
+ /* FIXME(netlink-bzero-secret): extend netlink library to wipe memory. For now,
+ * just hack it here (yes, this does not cover all places where the
+ * private key was copied). */
+ nm_explicit_bzero (nla_data (tb[WGDEVICE_A_PRIVATE_KEY]),
+ nla_len (tb[WGDEVICE_A_PRIVATE_KEY]));
+ }
+ if (tb[WGDEVICE_A_PUBLIC_KEY])
+ nla_memcpy (props->public_key, tb[WGDEVICE_A_PUBLIC_KEY], sizeof (props->public_key));
+ if (tb[WGDEVICE_A_LISTEN_PORT])
+ props->listen_port = nla_get_u16 (tb[WGDEVICE_A_LISTEN_PORT]);
+ if (tb[WGDEVICE_A_FWMARK])
+ props->fwmark = nla_get_u32 (tb[WGDEVICE_A_FWMARK]);
+
+ parse_data->obj = obj;
+ }
if (tb[WGDEVICE_A_PEERS]) {
struct nlattr *attr;
int rem;
nla_for_each_nested (attr, tb[WGDEVICE_A_PEERS], rem) {
- if (!_wireguard_update_from_peers_nla (buf, attr))
- return NL_SKIP;
+ if (!_wireguard_update_from_peers_nla (&parse_data->peers, &parse_data->allowed_ips, attr)) {
+ /* we ignore the error of parsing one peer.
+ * _wireguard_update_from_peers_nla() leaves the @peers array in the
+ * desired state. */
+ }
}
}
return NL_OK;
}
-static gboolean
-_wireguard_get_link_properties (NMPlatform *platform, const NMPlatformLink *link, NMPObject *obj)
+static const NMPObject *
+_wireguard_read_info (NMPlatform *platform /* used only as logging context */,
+ struct nl_sock *genl,
+ int wireguard_family_id,
+ int ifindex)
{
- NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
nm_auto_nlmsg struct nl_msg *msg = NULL;
- struct _wireguard_device_buf buf = {
- .obj = obj,
- .peers = g_array_new (FALSE, FALSE, sizeof (NMWireGuardPeer)),
- .allowedips = g_array_new (FALSE, FALSE, sizeof (NMWireGuardAllowedIP)),
- };
- struct nl_cb cb = {
- .valid_cb = _wireguard_get_device_cb,
- .valid_arg = &buf,
+ NMPObject *obj = NULL;
+ WireGuardPeerConstruct *peer_c;
+ WireGuardPeerConstruct *peer_c_safe;
+ gs_unref_array GArray *allowed_ips = NULL;
+ WireGuardParseData parse_data = {
+ .ifindex = ifindex,
};
- guint i, j;
- int wireguard_family_id;
+ guint i;
- wireguard_family_id = genl_ctrl_resolve (priv->genl, "wireguard");
- if (wireguard_family_id < 0) {
- _LOGD ("wireguard: kernel support not available for wireguard link %s", link->name);
- goto err;
- }
+ nm_assert (genl);
+ nm_assert (wireguard_family_id >= 0);
+ nm_assert (ifindex > 0);
msg = nlmsg_alloc ();
- if (!genlmsg_put (msg, NL_AUTO_PORT, NL_AUTO_SEQ, wireguard_family_id,
- 0, NLM_F_DUMP, WG_CMD_GET_DEVICE, 1))
- goto err;
+ if (!genlmsg_put (msg,
+ NL_AUTO_PORT,
+ NL_AUTO_SEQ,
+ wireguard_family_id,
+ 0,
+ NLM_F_DUMP,
+ WG_CMD_GET_DEVICE,
+ 1))
+ return NULL;
+
+ NLA_PUT_U32 (msg, WGDEVICE_A_IFINDEX, (guint32) ifindex);
- NLA_PUT_U32 (msg, WGDEVICE_A_IFINDEX, link->ifindex);
+ if (nl_send_auto (genl, msg) < 0)
+ return NULL;
- if (nl_send_auto (priv->genl, msg) < 0)
- goto err;
+ c_list_init (&parse_data.peers);
- if (nl_recvmsgs (priv->genl, &cb) < 0)
- goto err;
+ /* we ignore errors, and return whatever we could successfully
+ * parse. */
+ nl_recvmsgs (genl,
+ &((const struct nl_cb) {
+ .valid_cb = _wireguard_get_device_cb,
+ .valid_arg = (gpointer) &parse_data,
+ }));
- /* have each peer point to its own chunk of the allowedips buffer */
- for (i = 0, j = 0; i < buf.peers->len; i++) {
- NMWireGuardPeer *p = &g_array_index (buf.peers, NMWireGuardPeer, i);
+ /* unpack: transfer ownership */
+ obj = parse_data.obj;
+ allowed_ips = parse_data.allowed_ips;
- p->allowedips = &g_array_index (buf.allowedips, NMWireGuardAllowedIP, j);
- j += p->allowedips_len;
+ if (!obj) {
+ while ((peer_c = c_list_first_entry (&parse_data.peers, WireGuardPeerConstruct, lst))) {
+ c_list_unlink_stale (&peer_c->lst);
+ nm_explicit_bzero (&peer_c->data.preshared_key, sizeof (peer_c->data.preshared_key));
+ g_slice_free (WireGuardPeerConstruct, peer_c);
+ }
+ return NULL;
}
- /* drop the wrapper (but also the buffer if no peer points to it) */
- g_array_free (buf.allowedips, buf.peers->len ? FALSE : TRUE);
- obj->_lnk_wireguard.peers_len = buf.peers->len;
- obj->_lnk_wireguard.peers = (NMWireGuardPeer *) g_array_free (buf.peers, FALSE);
+ /* we receive peers/allowed-ips possibly in separate netlink messages. Hence, while
+ * parsing the dump, we don't know upfront how many peers/allowed-ips we will receive.
+ *
+ * We solve that, by collecting all peers with a CList. It's done this way,
+ * because a GArray would require growing the array, but we want to bzero()
+ * the preshared-key of each peer while reallocating. The CList apprach avoids
+ * that.
+ *
+ * For allowed-ips, we instead track one GArray, which are all appended
+ * there. The realloc/resize of the GArray is fine there. However,
+ * while we build the GArray, we don't yet have the final pointers.
+ * Hence, while constructing, we track the indexes with peer->_construct_idx_*
+ * fields. These indexes must be convered to actual pointers blow.
+ *
+ * This is all done during parsing. In the final NMPObjectLnkWireGuard we
+ * don't want the CList anymore and repackage the NMPObject tightly. The
+ * reason is, that NMPObject instances are immutable and long-living. Spend
+ * a bit effort below during construction to obtain a most suitable representation
+ * in this regard. */
+ obj->_lnk_wireguard.peers_len = c_list_length (&parse_data.peers);
+ obj->_lnk_wireguard.peers = obj->_lnk_wireguard.peers_len > 0
+ ? g_new (NMPWireGuardPeer, obj->_lnk_wireguard.peers_len)
+ : NULL;
+
+ /* duplicate allowed_ips instead of using the pointer. The GArray possibly has more
+ * space allocated then we need, and we want to get rid of this excess buffer.
+ * Note that NMPObject instance is possibly put into the cache and long-living. */
+ obj->_lnk_wireguard._allowed_ips_buf_len = allowed_ips ? allowed_ips->len : 0u;
+ obj->_lnk_wireguard._allowed_ips_buf = obj->_lnk_wireguard._allowed_ips_buf_len > 0
+ ? (NMPWireGuardAllowedIP *) nm_memdup (allowed_ips->data,
+ sizeof (NMPWireGuardAllowedIP) * allowed_ips->len)
+ : NULL;
+
+ i = 0;
+ c_list_for_each_entry_safe (peer_c, peer_c_safe, &parse_data.peers, lst) {
+ NMPWireGuardPeer *peer = (NMPWireGuardPeer *) &obj->_lnk_wireguard.peers[i++];
+
+ *peer = peer_c->data;
+
+ c_list_unlink_stale (&peer_c->lst);
+ nm_explicit_bzero (&peer_c->data.preshared_key, sizeof (peer_c->data.preshared_key));
+ g_slice_free (WireGuardPeerConstruct, peer_c);
+
+ if (peer->_construct_idx_end != 0) {
+ guint len;
+
+ nm_assert (obj->_lnk_wireguard._allowed_ips_buf);
+ nm_assert (peer->_construct_idx_end > peer->_construct_idx_start);
+ nm_assert (peer->_construct_idx_start < obj->_lnk_wireguard._allowed_ips_buf_len);
+ nm_assert (peer->_construct_idx_end <= obj->_lnk_wireguard._allowed_ips_buf_len);
+
+ len = peer->_construct_idx_end - peer->_construct_idx_start;
+ peer->allowed_ips = &obj->_lnk_wireguard._allowed_ips_buf[peer->_construct_idx_start];
+ peer->allowed_ips_len = len;
+ } else {
+ nm_assert (!peer->_construct_idx_start);
+ nm_assert (!peer->_construct_idx_end);
+ peer->allowed_ips = NULL;
+ peer->allowed_ips_len = 0;
+ }
+ }
- return TRUE;
+ return obj;
-err:
nla_put_failure:
- g_array_free (buf.peers, TRUE);
- g_array_free (buf.allowedips, TRUE);
- return FALSE;
+ g_return_val_if_reached (NULL);
}
/*****************************************************************************/
@@ -2188,7 +2332,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
gboolean completed_from_cache_val = FALSE;
gboolean *completed_from_cache = cache ? &completed_from_cache_val : NULL;
const NMPObject *link_cached = NULL;
- NMPObject *lnk_data = NULL;
+ const NMPObject *lnk_data = NULL;
gboolean address_complete_from_cache = TRUE;
gboolean lnk_data_complete_from_cache = TRUE;
gboolean need_ext_data = FALSE;
@@ -2197,10 +2341,12 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
if (!nlmsg_valid_hdr (nlh, sizeof (*ifi)))
return NULL;
- ifi = nlmsg_data(nlh);
+ ifi = nlmsg_data (nlh);
if (ifi->ifi_family != AF_UNSPEC)
return NULL;
+ if (ifi->ifi_index <= 0)
+ return NULL;
obj = nmp_object_new_link (ifi->ifi_index);
@@ -2358,6 +2504,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
lnk_data_complete_from_cache = FALSE;
break;
case NM_LINK_TYPE_WIREGUARD:
+ lnk_data_complete_from_cache = TRUE;
break;
default:
lnk_data_complete_from_cache = FALSE;
@@ -2386,7 +2533,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
* Also, sometimes the info-data is missing for updates. In this case
* we want to keep the previously received lnk_data. */
nmp_object_unref (lnk_data);
- lnk_data = (NMPObject *) nmp_object_ref (link_cached->_link.netlink.lnk);
+ lnk_data = nmp_object_ref (link_cached->_link.netlink.lnk);
}
if ( need_ext_data
@@ -2411,29 +2558,10 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
}
}
- if (obj->link.type == NM_LINK_TYPE_WIREGUARD) {
- nm_auto_nmpobj NMPObject *lnk_data_now = NULL;
-
- /* The WireGuard kernel module does not yet send link update
- * notifications, so we don't actually update the cache. For
- * now, always refetch link data here. */
- lnk_data_now = nmp_object_new (NMP_OBJECT_TYPE_LNK_WIREGUARD, NULL);
- if (!_wireguard_get_link_properties (platform, &obj->link, lnk_data_now)) {
- _LOGD ("wireguard: %d %s: failed to get properties",
- obj->link.ifindex,
- obj->link.name ?: "");
- }
-
- if (lnk_data && nmp_object_cmp (lnk_data, lnk_data_now))
- nmp_object_unref (g_steal_pointer (&lnk_data));
-
- if (!lnk_data)
- lnk_data = (NMPObject *) nmp_object_ref (lnk_data_now);
- }
-
obj->_link.netlink.lnk = lnk_data;
- if (need_ext_data && obj->_link.ext_data == NULL) {
+ if ( need_ext_data
+ && obj->_link.ext_data == NULL) {
switch (obj->link.type) {
case NM_LINK_TYPE_WIFI:
obj->_link.ext_data = (GObject *) nm_wifi_utils_new (ifi->ifi_index,
@@ -2459,6 +2587,42 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
}
}
+ if (obj->link.type == NM_LINK_TYPE_WIREGUARD) {
+ const NMPObject *lnk_data_new = NULL;
+ struct nl_sock *genl = NM_LINUX_PLATFORM_GET_PRIVATE (platform)->genl;
+
+ /* The WireGuard kernel module does not yet send link update
+ * notifications, so we don't actually update the cache. For
+ * now, always refetch link data here. */
+
+ _lookup_cached_link (cache, obj->link.ifindex, completed_from_cache, &link_cached);
+ if ( link_cached
+ && link_cached->_link.netlink.is_in_netlink
+ && link_cached->link.type == NM_LINK_TYPE_WIREGUARD)
+ obj->_link.wireguard_family_id = link_cached->_link.wireguard_family_id;
+ else
+ obj->_link.wireguard_family_id = -1;
+
+ if (obj->_link.wireguard_family_id < 0)
+ obj->_link.wireguard_family_id = genl_ctrl_resolve (genl, "wireguard");
+
+ if (obj->_link.wireguard_family_id >= 0) {
+ lnk_data_new = _wireguard_read_info (platform,
+ genl,
+ obj->_link.wireguard_family_id,
+ obj->link.ifindex);
+ }
+
+ if ( lnk_data_new
+ && obj->_link.netlink.lnk
+ && nmp_object_equal (obj->_link.netlink.lnk, lnk_data_new))
+ nmp_object_unref (lnk_data_new);
+ else {
+ nmp_object_unref (obj->_link.netlink.lnk);
+ obj->_link.netlink.lnk = lnk_data_new;
+ }
+ }
+
obj->_link.netlink.is_in_netlink = TRUE;
return g_steal_pointer (&obj);
}
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index 0020acc924..7ddcf41ae5 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -5532,67 +5532,57 @@ nm_platform_lnk_vxlan_to_string (const NMPlatformLnkVxlan *lnk, char *buf, gsize
}
const char *
-nm_platform_wireguard_peer_to_string (const NMWireGuardPeer *peer, char *buf, gsize len)
+nm_platform_wireguard_peer_to_string (const NMPWireGuardPeer *peer, char *buf, gsize len)
{
- gs_free char *public_b64 = NULL;
- char s_address[INET6_ADDRSTRLEN] = {0};
- char s_endpoint[INET6_ADDRSTRLEN + NI_MAXSERV + sizeof("endpoint []:") + 1] = {0};
- guint8 nonzero_key = 0;
- gsize i;
+ gs_free char *public_key_b64 = NULL;
+ char s_endpoint[NM_UTILS_INET_ADDRSTRLEN + 100];
+ char s_addr[NM_UTILS_INET_ADDRSTRLEN];
+ guint i;
nm_utils_to_string_buffer_init (&buf, &len);
- if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) {
- char s_service[NI_MAXSERV];
- socklen_t addr_len = 0;
-
- if (peer->endpoint.addr.sa_family == AF_INET)
- addr_len = sizeof (struct sockaddr_in);
- else if (peer->endpoint.addr.sa_family == AF_INET6)
- addr_len = sizeof (struct sockaddr_in6);
- if (!getnameinfo (&peer->endpoint.addr, addr_len, s_address, sizeof(s_address), s_service, sizeof(s_service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) {
- if (peer->endpoint.addr.sa_family == AF_INET6 && strchr (s_address, ':'))
- g_snprintf(s_endpoint, sizeof (s_endpoint), "endpoint [%s]:%s ", s_address, s_service);
- else
- g_snprintf(s_endpoint, sizeof (s_endpoint), "endpoint %s:%s ", s_address, s_service);
- }
- }
-
-
- for (i = 0; i < sizeof (peer->preshared_key); i++)
- nonzero_key |= peer->preshared_key[i];
+ if (peer->endpoint_family == AF_INET) {
+ nm_sprintf_buf (s_endpoint,
+ " endpoint %s:%u",
+ nm_utils_inet4_ntop (peer->endpoint_addr.addr4, s_addr),
+ (guint) peer->endpoint_port);
+ } else if (peer->endpoint_family == AF_INET6) {
+ nm_sprintf_buf (s_endpoint,
+ " endpoint [%s]:%u",
+ nm_utils_inet6_ntop (&peer->endpoint_addr.addr6, s_addr),
+ (guint) peer->endpoint_port);
+ } else
+ s_endpoint[0] = '\0';
- public_b64 = g_base64_encode (peer->public_key, sizeof (peer->public_key));
+ public_key_b64 = g_base64_encode (peer->public_key, sizeof (peer->public_key));
nm_utils_strbuf_append (&buf, &len,
- "{ "
- "public_key %s "
- "%s" /* preshared key indicator */
+ "public-key %s"
+ "%s" /* preshared-key */
"%s" /* endpoint */
- "rx %"G_GUINT64_FORMAT" "
- "tx %"G_GUINT64_FORMAT" "
- "allowedips (%"G_GSIZE_FORMAT") {",
- public_b64,
- nonzero_key ? "preshared_key (hidden) " : "",
+ " rx %"G_GUINT64_FORMAT
+ " tx %"G_GUINT64_FORMAT
+ "%s", /* allowed-ips */
+ public_key_b64,
+ nm_utils_mem_all_zero (peer->preshared_key, sizeof (peer->preshared_key))
+ ? ""
+ : " preshared-key (hidden)",
s_endpoint,
peer->rx_bytes,
peer->tx_bytes,
- peer->allowedips_len);
-
-
- for (i = 0; i < peer->allowedips_len; i++) {
- NMWireGuardAllowedIP *allowedip = &peer->allowedips[i];
- const char *ret;
+ peer->allowed_ips_len > 0
+ ? " allowed-ips"
+ : "");
- ret = inet_ntop (allowedip->family, &allowedip->ip, s_address, sizeof(s_address));
+ for (i = 0; i < peer->allowed_ips_len; i++) {
+ const NMPWireGuardAllowedIP *allowed_ip = &peer->allowed_ips[i];
nm_utils_strbuf_append (&buf, &len,
" %s/%u",
- ret ? s_address : "<EAFNOSUPPORT>",
- allowedip->mask);
+ nm_utils_inet_ntop (allowed_ip->family, &allowed_ip->addr, s_addr),
+ allowed_ip->mask);
}
- nm_utils_strbuf_append_str (&buf, &len, " } }");
return buf;
}
@@ -5600,25 +5590,26 @@ const char *
nm_platform_lnk_wireguard_to_string (const NMPlatformLnkWireGuard *lnk, char *buf, gsize len)
{
gs_free char *public_b64 = NULL;
- guint8 nonzero_key = 0;
- gsize i;
if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len))
return buf;
- public_b64 = g_base64_encode (lnk->public_key, sizeof (lnk->public_key));
-
- for (i = 0; i < sizeof (lnk->private_key); i++)
- nonzero_key |= lnk->private_key[i];
+ if (!nm_utils_mem_all_zero (lnk->public_key, sizeof (lnk->public_key)))
+ public_b64 = g_base64_encode (lnk->public_key, sizeof (lnk->public_key));
g_snprintf (buf, len,
- "wireguard "
- "public_key %s "
- "%s" /* private key indicator */
- "listen_port %u "
- "fwmark 0x%x",
- public_b64,
- nonzero_key ? "private_key (hidden) " : "",
+ "wireguard"
+ "%s%s" /* public-key */
+ "%s" /* private-key */
+ " listen-port %u"
+ " fwmark 0x%x",
+ public_b64
+ ? " public-key "
+ : "",
+ public_b64 ?: "",
+ nm_utils_mem_all_zero (lnk->private_key, sizeof (lnk->private_key))
+ ? ""
+ : " private-key (hidden)",
lnk->listen_port,
lnk->fwmark);
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index a1171342f1..11495aff4f 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -752,11 +752,14 @@ typedef struct {
bool l3miss:1;
} NMPlatformLnkVxlan;
+#define NMP_WIREGUARD_PUBLIC_KEY_LEN 32
+#define NMP_WIREGUARD_SYMMETRIC_KEY_LEN 32
+
typedef struct {
- guint8 private_key[NM_WG_PUBLIC_KEY_LEN];
- guint8 public_key[NM_WG_PUBLIC_KEY_LEN];
- guint16 listen_port;
guint32 fwmark;
+ guint16 listen_port;
+ guint8 private_key[NMP_WIREGUARD_PUBLIC_KEY_LEN];
+ guint8 public_key[NMP_WIREGUARD_PUBLIC_KEY_LEN];
} NMPlatformLnkWireGuard;
typedef enum {
@@ -1463,7 +1466,8 @@ const char *nm_platform_vlan_qos_mapping_to_string (const char *name,
char *buf,
gsize len);
-const char *nm_platform_wireguard_peer_to_string (const NMWireGuardPeer *peer,
+struct _NMPWireGuardPeer;
+const char *nm_platform_wireguard_peer_to_string (const struct _NMPWireGuardPeer *peer,
char *buf,
gsize len);
diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c
index 4358f8d714..fdc27440cc 100644
--- a/src/platform/nmp-object.c
+++ b/src/platform/nmp-object.c
@@ -27,6 +27,7 @@
#include <libudev.h>
#include "nm-utils.h"
+#include "nm-utils/nm-secret-utils.h"
#include "nm-core-utils.h"
#include "nm-platform-utils.h"
@@ -347,117 +348,92 @@ _vlan_xgress_qos_mappings_cpy (guint *dst_n_map,
/*****************************************************************************/
static void
-_wireguard_peers_hash_update (gsize n_peers,
- const NMWireGuardPeer *peers,
- NMHashState *h)
-{
- gsize i, j;
-
- nm_hash_update_val (h, n_peers);
- for (i = 0; i < n_peers; i++) {
- const NMWireGuardPeer *p = &peers[i];
-
- nm_hash_update (h, p->public_key, sizeof (p->public_key));
- nm_hash_update (h, p->preshared_key, sizeof (p->preshared_key));
- nm_hash_update_vals (h,
- p->persistent_keepalive_interval,
- p->allowedips_len,
- p->rx_bytes,
- p->tx_bytes,
- p->last_handshake_time.tv_sec,
- p->last_handshake_time.tv_nsec,
- p->endpoint.addr.sa_family);
-
- if (p->endpoint.addr.sa_family == AF_INET)
- nm_hash_update_val (h, p->endpoint.addr4);
- else if (p->endpoint.addr.sa_family == AF_INET6)
- nm_hash_update_val (h, p->endpoint.addr6);
- else if (p->endpoint.addr.sa_family != AF_UNSPEC)
- g_assert_not_reached ();
-
- for (j = 0; j < p->allowedips_len; j++) {
- const NMWireGuardAllowedIP *ip = &p->allowedips[j];
-
- nm_hash_update_vals (h, ip->family, ip->mask);
-
- if (ip->family == AF_INET)
- nm_hash_update_val (h, ip->ip.addr4);
- else if (ip->family == AF_INET6)
- nm_hash_update_val (h, ip->ip.addr6);
- else if (ip->family != AF_UNSPEC)
- g_assert_not_reached ();
- }
- }
+_wireguard_allowed_ip_hash_update (const NMPWireGuardAllowedIP *ip,
+ NMHashState *h)
+{
+ nm_hash_update_vals (h, ip->family,
+ ip->mask);
+
+ if (ip->family == AF_INET)
+ nm_hash_update_val (h, ip->addr.addr4);
+ else if (ip->family == AF_INET6)
+ nm_hash_update_val (h, ip->addr.addr6);
}
static int
-_wireguard_peers_cmp (gsize n_peers,
- const NMWireGuardPeer *p1,
- const NMWireGuardPeer *p2)
-{
- gsize i, j;
-
- for (i = 0; i < n_peers; i++) {
- const NMWireGuardPeer *a = &p1[i];
- const NMWireGuardPeer *b = &p2[i];
-
- NM_CMP_FIELD (a, b, last_handshake_time.tv_sec);
- NM_CMP_FIELD (a, b, last_handshake_time.tv_nsec);
- NM_CMP_FIELD (a, b, rx_bytes);
- NM_CMP_FIELD (a, b, tx_bytes);
- NM_CMP_FIELD (a, b, allowedips_len);
- NM_CMP_FIELD (a, b, persistent_keepalive_interval);
- NM_CMP_FIELD (a, b, endpoint.addr.sa_family);
- NM_CMP_FIELD_MEMCMP (a, b, public_key);
- NM_CMP_FIELD_MEMCMP (a, b, preshared_key);
-
- if (a->endpoint.addr.sa_family == AF_INET)
- NM_CMP_FIELD_MEMCMP (a, b, endpoint.addr4);
- else if (a->endpoint.addr.sa_family == AF_INET6)
- NM_CMP_FIELD_MEMCMP (a, b, endpoint.addr6);
- else if (a->endpoint.addr.sa_family != AF_UNSPEC)
- g_assert_not_reached ();
-
- for (j = 0; j < a->allowedips_len; j++) {
- const NMWireGuardAllowedIP *aip = &a->allowedips[j];
- const NMWireGuardAllowedIP *bip = &b->allowedips[j];
-
- NM_CMP_FIELD (aip, bip, family);
- NM_CMP_FIELD (aip, bip, mask);
-
- if (aip->family == AF_INET)
- NM_CMP_FIELD_MEMCMP (&aip->ip, &bip->ip, addr4);
- else if (aip->family == AF_INET6)
- NM_CMP_FIELD_MEMCMP (&aip->ip, &bip->ip, addr6);
- else if (aip->family != AF_UNSPEC)
- g_assert_not_reached ();
- }
- }
+_wireguard_allowed_ip_cmp (const NMPWireGuardAllowedIP *a,
+ const NMPWireGuardAllowedIP *b)
+{
+ NM_CMP_SELF (a, b);
+
+ NM_CMP_FIELD (a, b, family);
+ NM_CMP_FIELD (a, b, mask);
+
+ if (a->family == AF_INET)
+ NM_CMP_FIELD (a, b, addr.addr4);
+ else if (a->family == AF_INET6)
+ NM_CMP_FIELD_IN6ADDR (a, b, addr.addr6);
return 0;
}
static void
-_wireguard_peers_cpy (gsize *dst_n_peers,
- NMWireGuardPeer **dst_peers,
- gsize src_n_peers,
- const NMWireGuardPeer *src_peers)
-{
- if (src_n_peers == 0) {
- g_clear_pointer (dst_peers, g_free);
- *dst_n_peers = 0;
- } else if ( src_n_peers != *dst_n_peers
- || _wireguard_peers_cmp (src_n_peers, *dst_peers, src_peers) != 0) {
- gsize i;
- g_clear_pointer (dst_peers, g_free);
- *dst_n_peers = src_n_peers;
- if (src_n_peers > 0)
- *dst_peers = nm_memdup (src_peers, sizeof (*src_peers) * src_n_peers);
- for (i = 0; i < src_n_peers; i++) {
- dst_peers[i]->allowedips = nm_memdup (src_peers[i].allowedips, sizeof (src_peers[i].allowedips) * src_peers[i].allowedips_len);
- dst_peers[i]->allowedips_len = src_peers[i].allowedips_len;
- }
+_wireguard_peer_hash_update (const NMPWireGuardPeer *peer,
+ NMHashState *h)
+{
+ guint i;
+
+ nm_hash_update (h, peer->public_key, sizeof (peer->public_key));
+ nm_hash_update (h, peer->preshared_key, sizeof (peer->preshared_key));
+ nm_hash_update_vals (h,
+ peer->persistent_keepalive_interval,
+ peer->allowed_ips_len,
+ peer->rx_bytes,
+ peer->tx_bytes,
+ peer->last_handshake_time.tv_sec,
+ peer->last_handshake_time.tv_nsec,
+ peer->endpoint_port,
+ peer->endpoint_family);
+
+ if (peer->endpoint_family == AF_INET)
+ nm_hash_update_val (h, peer->endpoint_addr.addr4);
+ else if (peer->endpoint_family == AF_INET6)
+ nm_hash_update_val (h, peer->endpoint_addr.addr6);
+
+ for (i = 0; i < peer->allowed_ips_len; i++)
+ _wireguard_allowed_ip_hash_update (&peer->allowed_ips[i], h);
+}
+
+static int
+_wireguard_peer_cmp (const NMPWireGuardPeer *a,
+ const NMPWireGuardPeer *b)
+{
+ guint i;
+
+ NM_CMP_SELF (a, b);
+
+ NM_CMP_FIELD (a, b, last_handshake_time.tv_sec);
+ NM_CMP_FIELD (a, b, last_handshake_time.tv_nsec);
+ NM_CMP_FIELD (a, b, rx_bytes);
+ NM_CMP_FIELD (a, b, tx_bytes);
+ NM_CMP_FIELD (a, b, allowed_ips_len);
+ NM_CMP_FIELD (a, b, persistent_keepalive_interval);
+ NM_CMP_FIELD (a, b, endpoint_port);
+ NM_CMP_FIELD (a, b, endpoint_family);
+ NM_CMP_FIELD_MEMCMP (a, b, public_key);
+ NM_CMP_FIELD_MEMCMP (a, b, preshared_key);
+
+ if (a->endpoint_family == AF_INET)
+ NM_CMP_FIELD (a, b, endpoint_addr.addr4);
+ else if (a->endpoint_family == AF_INET6)
+ NM_CMP_FIELD_IN6ADDR (a, b, endpoint_addr.addr6);
+
+ for (i = 0; i < a->allowed_ips_len; i++) {
+ NM_CMP_RETURN (_wireguard_allowed_ip_cmp (&a->allowed_ips[i],
+ &b->allowed_ips[i]));
}
+
+ return 0;
}
/*****************************************************************************/
@@ -587,12 +563,25 @@ _vt_cmd_obj_dispose_lnk_vlan (NMPObject *obj)
}
static void
-_vt_cmd_obj_dispose_lnk_wireguard (NMPObject *obj)
+_wireguard_clear (NMPObjectLnkWireGuard *lnk)
{
- if (obj->_lnk_wireguard.peers_len)
- g_free (obj->_lnk_wireguard.peers[0].allowedips);
+ guint i;
+
+ nm_explicit_bzero (lnk->_public.private_key,
+ sizeof (lnk->_public.private_key));
+ for (i = 0; i < lnk->peers_len; i++) {
+ NMPWireGuardPeer *peer = (NMPWireGuardPeer *) &lnk->peers[i];
+
+ nm_explicit_bzero (peer->preshared_key, sizeof (peer->preshared_key));
+ }
+ g_free ((gpointer) lnk->peers);
+ g_free ((gpointer) lnk->_allowed_ips_buf);
+}
- g_free (obj->_lnk_wireguard.peers);
+static void
+_vt_cmd_obj_dispose_lnk_wireguard (NMPObject *obj)
+{
+ _wireguard_clear (&obj->_lnk_wireguard);
}
static NMPObject *
@@ -859,7 +848,7 @@ _vt_cmd_obj_to_string_lnk_wireguard (const NMPObject *obj, NMPObjectToStringMode
const NMPClass *klass;
char buf2[sizeof (_nm_utils_to_string_buffer)];
char *b;
- gsize i, l;
+ guint i;
klass = NMP_OBJECT_GET_CLASS (obj);
@@ -871,23 +860,26 @@ _vt_cmd_obj_to_string_lnk_wireguard (const NMPObject *obj, NMPObjectToStringMode
b = buf;
nm_utils_strbuf_append (&b, &buf_size,
- "[%s,%p,%u,%calive,%cvisible; %s "
- "peers (%" G_GSIZE_FORMAT ") {",
+ "[%s,%p,%u,%calive,%cvisible; %s"
+ "%s",
klass->obj_type_name, obj, obj->parent._ref_count,
nmp_object_is_alive (obj) ? '+' : '-',
nmp_object_is_visible (obj) ? '+' : '-',
nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof (buf2)),
- obj->_lnk_wireguard.peers_len);
+ obj->_lnk_wireguard.peers_len > 0
+ ? " peers {"
+ : "");
for (i = 0; i < obj->_lnk_wireguard.peers_len; i++) {
- const NMWireGuardPeer *peer = &obj->_lnk_wireguard.peers[i];
+ const NMPWireGuardPeer *peer = &obj->_lnk_wireguard.peers[i];
+
+ nm_utils_strbuf_append_str (&b, &buf_size, " { ");
nm_platform_wireguard_peer_to_string (peer, b, buf_size);
- l = strlen (b);
- b += l;
- buf_size -= l;
+ nm_utils_strbuf_seek_end (&b, &buf_size);
+ nm_utils_strbuf_append_str (&b, &buf_size, " }");
}
-
- nm_utils_strbuf_append_str (&b, &buf_size, " }");
+ if (obj->_lnk_wireguard.peers_len)
+ nm_utils_strbuf_append_str (&b, &buf_size, " }");
return buf;
case NMP_OBJECT_TO_STRING_PUBLIC:
@@ -947,6 +939,7 @@ _vt_cmd_obj_hash_update_link (const NMPObject *obj, NMHashState *h)
nm_platform_link_hash_update (&obj->link, h);
nm_hash_update_vals (h,
obj->_link.netlink.is_in_netlink,
+ obj->_link.wireguard_family_id,
obj->_link.udev.device);
if (obj->_link.netlink.lnk)
nmp_object_hash_update (obj->_link.netlink.lnk, h);
@@ -969,10 +962,15 @@ _vt_cmd_obj_hash_update_lnk_vlan (const NMPObject *obj, NMHashState *h)
static void
_vt_cmd_obj_hash_update_lnk_wireguard (const NMPObject *obj, NMHashState *h)
{
+ guint i;
+
nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LNK_WIREGUARD);
nm_platform_lnk_wireguard_hash_update (&obj->lnk_wireguard, h);
- _wireguard_peers_hash_update (obj->_lnk_wireguard.peers_len, obj->_lnk_wireguard.peers, h);
+
+ nm_hash_update_val (h, obj->_lnk_wireguard.peers_len);
+ for (i = 0; i < obj->_lnk_wireguard.peers_len; i++)
+ _wireguard_peer_hash_update (&obj->_lnk_wireguard.peers[i], h);
}
int
@@ -980,12 +978,7 @@ nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2)
{
const NMPClass *klass1, *klass2;
- if (obj1 == obj2)
- return 0;
- if (!obj1)
- return -1;
- if (!obj2)
- return 1;
+ NM_CMP_SELF (obj1, obj2);
g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj1), -1);
g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj2), 1);
@@ -1006,16 +999,11 @@ nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2)
static int
_vt_cmd_obj_cmp_link (const NMPObject *obj1, const NMPObject *obj2)
{
- int i;
+ NM_CMP_RETURN (nm_platform_link_cmp (&obj1->link, &obj2->link));
+ NM_CMP_DIRECT (obj1->_link.netlink.is_in_netlink, obj2->_link.netlink.is_in_netlink);
+ NM_CMP_RETURN (nmp_object_cmp (obj1->_link.netlink.lnk, obj2->_link.netlink.lnk));
+ NM_CMP_DIRECT (obj1->_link.wireguard_family_id, obj2->_link.wireguard_family_id);
- i = nm_platform_link_cmp (&obj1->link, &obj2->link);
- if (i)
- return i;
- if (obj1->_link.netlink.is_in_netlink != obj2->_link.netlink.is_in_netlink)
- return obj1->_link.netlink.is_in_netlink ? -1 : 1;
- i = nmp_object_cmp (obj1->_link.netlink.lnk, obj2->_link.netlink.lnk);
- if (i)
- return i;
if (obj1->_link.udev.device != obj2->_link.udev.device) {
if (!obj1->_link.udev.device)
return -1;
@@ -1028,6 +1016,7 @@ _vt_cmd_obj_cmp_link (const NMPObject *obj1, const NMPObject *obj2)
* Have this check as very last. */
return (obj1->_link.udev.device < obj2->_link.udev.device) ? -1 : 1;
}
+
return 0;
}
@@ -1056,16 +1045,16 @@ _vt_cmd_obj_cmp_lnk_vlan (const NMPObject *obj1, const NMPObject *obj2)
static int
_vt_cmd_obj_cmp_lnk_wireguard (const NMPObject *obj1, const NMPObject *obj2)
{
- int c;
+ guint i;
- c = nm_platform_lnk_wireguard_cmp (&obj1->lnk_wireguard, &obj2->lnk_wireguard);
- if (c)
- return c;
+ NM_CMP_RETURN (nm_platform_lnk_wireguard_cmp (&obj1->lnk_wireguard, &obj2->lnk_wireguard));
+
+ NM_CMP_FIELD (obj1, obj2, _lnk_wireguard.peers_len);
- if (obj1->_lnk_wireguard.peers_len != obj2->_lnk_wireguard.peers_len)
- return obj1->_lnk_wireguard.peers_len < obj2->_lnk_wireguard.peers_len ? -1 : 1;
+ for (i = 0; i < obj1->_lnk_wireguard.peers_len; i++)
+ NM_CMP_RETURN (_wireguard_peer_cmp (&obj1->_lnk_wireguard.peers[i], &obj2->_lnk_wireguard.peers[i]));
- return _wireguard_peers_cmp(obj1->_lnk_wireguard.peers_len, obj1->_lnk_wireguard.peers, obj2->_lnk_wireguard.peers);
+ return 0;
}
/* @src is a const object, which is not entirely correct for link types, where
@@ -1137,9 +1126,36 @@ _vt_cmd_obj_copy_lnk_vlan (NMPObject *dst, const NMPObject *src)
static void
_vt_cmd_obj_copy_lnk_wireguard (NMPObject *dst, const NMPObject *src)
{
- dst->lnk_wireguard = src->lnk_wireguard;
- _wireguard_peers_cpy (&dst->_lnk_wireguard.peers_len, &dst->_lnk_wireguard.peers,
- src->_lnk_wireguard.peers_len, src->_lnk_wireguard.peers);
+ guint i;
+
+ nm_assert (dst != src);
+
+ _wireguard_clear (&dst->_lnk_wireguard);
+
+ dst->_lnk_wireguard = src->_lnk_wireguard;
+
+ dst->_lnk_wireguard.peers = nm_memdup (dst->_lnk_wireguard.peers,
+ sizeof (NMPWireGuardPeer) * dst->_lnk_wireguard.peers_len);
+ dst->_lnk_wireguard._allowed_ips_buf = nm_memdup (dst->_lnk_wireguard._allowed_ips_buf,
+ sizeof (NMPWireGuardAllowedIP) * dst->_lnk_wireguard._allowed_ips_buf_len);
+
+ /* all the peers' pointers point into the buffer. They need to be readjusted. */
+ for (i = 0; i < dst->_lnk_wireguard.peers_len; i++) {
+ NMPWireGuardPeer *peer = (NMPWireGuardPeer *) &dst->_lnk_wireguard.peers[i];
+
+ if (peer->allowed_ips_len == 0) {
+ nm_assert (!peer->allowed_ips);
+ continue;
+ }
+ nm_assert (dst->_lnk_wireguard._allowed_ips_buf_len > 0);
+ nm_assert (src->_lnk_wireguard._allowed_ips_buf);
+ nm_assert (peer->allowed_ips >= src->_lnk_wireguard._allowed_ips_buf);
+ nm_assert (&peer->allowed_ips[peer->allowed_ips_len] <= &src->_lnk_wireguard._allowed_ips_buf[src->_lnk_wireguard._allowed_ips_buf_len]);
+
+ peer->allowed_ips = &dst->_lnk_wireguard._allowed_ips_buf[peer->allowed_ips - src->_lnk_wireguard._allowed_ips_buf];
+ }
+
+ nm_assert (nmp_object_equal (src, dst));
}
#define _vt_cmd_plobj_id_copy(type, plat_type, cmd) \
@@ -3084,4 +3100,3 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_vlan_cmp,
},
};
-
diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h
index ba63a3a34e..97be832177 100644
--- a/src/platform/nmp-object.h
+++ b/src/platform/nmp-object.h
@@ -27,6 +27,36 @@
struct udev_device;
+/*****************************************************************************/
+
+typedef struct {
+ NMIPAddr addr;
+ guint8 family;
+ guint8 mask;
+} NMPWireGuardAllowedIP;
+
+typedef struct _NMPWireGuardPeer {
+ NMIPAddr endpoint_addr;
+ struct timespec last_handshake_time;
+ guint64 rx_bytes;
+ guint64 tx_bytes;
+ union {
+ const NMPWireGuardAllowedIP *allowed_ips;
+ guint _construct_idx_start;
+ };
+ union {
+ guint allowed_ips_len;
+ guint _construct_idx_end;
+ };
+ guint16 persistent_keepalive_interval;
+ guint16 endpoint_port;
+ guint8 public_key[NMP_WIREGUARD_PUBLIC_KEY_LEN];
+ guint8 preshared_key[NMP_WIREGUARD_SYMMETRIC_KEY_LEN];
+ guint8 endpoint_family;
+} NMPWireGuardPeer;
+
+/*****************************************************************************/
+
typedef enum { /*< skip >*/
NMP_OBJECT_TO_STRING_ID,
NMP_OBJECT_TO_STRING_PUBLIC,
@@ -168,6 +198,9 @@ typedef struct {
/* Auxiliary data object for Wi-Fi and WPAN */
GObject *ext_data;
+ /* FIXME: not every NMPObjectLink should pay the price for tracking
+ * the wireguard family id. This should be tracked via ext_data, which
+ * would be exactly the right place. */
int wireguard_family_id;
} NMPObjectLink;
@@ -220,9 +253,10 @@ typedef struct {
typedef struct {
NMPlatformLnkWireGuard _public;
-
- gsize peers_len;
- NMWireGuardPeer *peers;
+ const NMPWireGuardPeer *peers;
+ const NMPWireGuardAllowedIP *_allowed_ips_buf;
+ guint peers_len;
+ guint _allowed_ips_buf_len;
} NMPObjectLnkWireGuard;
typedef struct {