diff options
Diffstat (limited to 'src/platform/nm-linux-platform.c')
-rw-r--r-- | src/platform/nm-linux-platform.c | 451 |
1 files changed, 305 insertions, 146 deletions
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 418186b360..47b5d7e6a5 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 allowedip_policy[WGALLOWEDIP_A_MAX + 1] = { + 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, allowedip_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 peer_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,63 +1976,107 @@ _wireguard_update_from_peers_nla (struct _wireguard_device_buf *buf, [WGPEER_A_TX_BYTES] = { .type = NLA_U64 }, [WGPEER_A_ALLOWEDIPS] = { .type = NLA_NESTED }, }; - struct nlattr *tbp[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 }; + WireGuardPeerConstruct *peer_c; + struct nlattr *tb[WGPEER_A_MAX + 1]; - if (nla_parse_nested (tbp, WGPEER_A_MAX, peer_attr, peer_policy) < 0) + if (nla_parse_nested (tb, WGPEER_A_MAX, peer_attr, peer_policy) < 0) return FALSE; - if (!tbp[WGPEER_A_PUBLIC_KEY]) + if (!tb[WGPEER_A_PUBLIC_KEY]) return FALSE; /* a peer with the same public key as last peer is just a continuation for extra AllowedIPs */ - if ( last - && !memcmp (nla_data (tbp[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 is a continuation. */ + } 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); - - nla_memcpy (&peer->public_key, tbp[WGPEER_A_PUBLIC_KEY], sizeof (peer->public_key)); - - if (tbp[WGPEER_A_PRESHARED_KEY]) - nla_memcpy (&peer->preshared_key, tbp[WGPEER_A_PRESHARED_KEY], sizeof (peer->preshared_key)); - if (tbp[WGPEER_A_ENDPOINT]) { - struct sockaddr *addr = nla_data (tbp[WGPEER_A_ENDPOINT]); - if (addr->sa_family == AF_INET) - nla_memcpy (&peer->endpoint.addr4, tbp[WGPEER_A_ENDPOINT], sizeof (peer->endpoint.addr4)); - else if (addr->sa_family == AF_INET6) - nla_memcpy (&peer->endpoint.addr6, tbp[WGPEER_A_ENDPOINT], sizeof (peer->endpoint.addr6)); - } - if (tbp[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]) - peer->persistent_keepalive_interval = nla_get_u64 (tbp[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]); - if (tbp[WGPEER_A_LAST_HANDSHAKE_TIME]) - nla_memcpy (&peer->last_handshake_time, tbp[WGPEER_A_LAST_HANDSHAKE_TIME], sizeof (peer->last_handshake_time)); - if (tbp[WGPEER_A_RX_BYTES]) - peer->rx_bytes = nla_get_u64 (tbp[WGPEER_A_RX_BYTES]); - if (tbp[WGPEER_A_TX_BYTES]) - peer->tx_bytes = nla_get_u64 (tbp[WGPEER_A_TX_BYTES]); + peer_c = g_slice_new0 (WireGuardPeerConstruct); + c_list_link_tail (peers, &peer_c->lst); - peer->allowedips = NULL; - peer->allowedips_len = 0; + 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_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]) { + 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_c->data.persistent_keepalive_interval = nla_get_u64 (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]); + if (tb[WGPEER_A_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_c->data.rx_bytes = nla_get_u64 (tb[WGPEER_A_RX_BYTES]); + if (tb[WGPEER_A_TX_BYTES]) + peer_c->data.tx_bytes = nla_get_u64 (tb[WGPEER_A_TX_BYTES]); } - if (tbp[WGPEER_A_ALLOWEDIPS]) { + 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 (!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; + } - nla_for_each_nested (attr, tbp[WGPEER_A_ALLOWEDIPS], rem) { - if (!_wireguard_update_from_allowedips_nla (buf, attr)) - return FALSE; + 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 +2090,187 @@ _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; - struct nlattr *tbd[WGDEVICE_A_MAX + 1]; - NMPlatformLnkWireGuard *props = &buf->obj->lnk_wireguard; - struct nlmsghdr *nlh = nlmsg_hdr (msg); + WireGuardParseData *parse_data = arg; + struct nlattr *tb[WGDEVICE_A_MAX + 1]; + NMPObject *obj = NULL; int nlerr; - nlerr = genlmsg_parse (nlh, 0, tbd, WGDEVICE_A_MAX, device_policy); + nlerr = genlmsg_parse (nlmsg_hdr (msg), 0, tb, WGDEVICE_A_MAX, device_policy); if (nlerr < 0) return NL_SKIP; - if (tbd[WGDEVICE_A_PRIVATE_KEY]) - nla_memcpy (props->private_key, tbd[WGDEVICE_A_PRIVATE_KEY], sizeof (props->private_key)); - if (tbd[WGDEVICE_A_PUBLIC_KEY]) - nla_memcpy (props->public_key, tbd[WGDEVICE_A_PUBLIC_KEY], sizeof (props->public_key)); - if (tbd[WGDEVICE_A_LISTEN_PORT]) - props->listen_port = nla_get_u16 (tbd[WGDEVICE_A_LISTEN_PORT]); - if (tbd[WGDEVICE_A_FWMARK]) - props->fwmark = nla_get_u32 (tbd[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) + obj = parse_data->obj; + else { + 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]); - if (tbd[WGDEVICE_A_PEERS]) { + parse_data->obj = obj; + } + + if (tb[WGDEVICE_A_PEERS]) { struct nlattr *attr; int rem; - nla_for_each_nested (attr, tbd[WGDEVICE_A_PEERS], rem) { - if (!_wireguard_update_from_peers_nla (buf, attr)) - return NL_SKIP; + nla_for_each_nested (attr, tb[WGDEVICE_A_PEERS], rem) { + 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)), + NMPObject *obj = NULL; + WireGuardPeerConstruct *peer_c; + WireGuardPeerConstruct *peer_c_safe; + gs_unref_array GArray *allowed_ips = NULL; + WireGuardParseData parse_data = { + .ifindex = ifindex, }; - struct nl_cb cb = { + const struct nl_cb cb = { .valid_cb = _wireguard_get_device_cb, - .valid_arg = &buf, + .valid_arg = (gpointer) &parse_data, }; - 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, &cb); - /* 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 messages. Hence, while + * parsing the dump, we don't know 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 reallocating the array, but we want to + * bzero() the preshared-key of each peer. + * + * For allowed-ips, we instead track one GArray, which are all appended + * there. The realloc/resize of the GArray is desired 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 no be convered to actual pointers. + * + * This is all done during parsing. In the final NMPObjectLnkWireGuard we + * don't want the CList anymore. That is, because NMPObject instances are + * immutable and long-living. We repack the CList in a peers array. */ + 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; + + 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 *) g_array_free (g_steal_pointer (&allowed_ips), FALSE) + : 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 > peer->_construct_idx_start) { + guint len; + + nm_assert (obj->_lnk_wireguard._allowed_ips_buf); + 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); } /*****************************************************************************/ @@ -2197,10 +2331,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 +2494,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; @@ -2410,26 +2547,6 @@ _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) { @@ -2458,6 +2575,48 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr } } + if (obj->link.type == NM_LINK_TYPE_WIREGUARD) { + const NMPObject *lnk_data_old = NULL; + nm_auto_nmpobj 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) { + if (NMP_OBJECT_GET_TYPE (link_cached->_link.netlink.lnk) == NMP_OBJECT_TYPE_LNK_WIREGUARD) + lnk_data_old = link_cached->_link.netlink.lnk; + 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); + } + + nm_assert (!obj->_link.netlink.lnk); + if ( lnk_data_new + && ( !lnk_data_old + || !nmp_object_equal (lnk_data_old, lnk_data_new))) + obj->_link.netlink.lnk = g_steal_pointer (&lnk_data_new); + else { + /* we really want to keep the currently/old data. In particular, + * if we fail to fetch new information above, we stick to the + * stale data (instead of clearing the information). */ + obj->_link.netlink.lnk = nmp_object_ref (lnk_data_old); + } + } + obj->_link.netlink.is_in_netlink = TRUE; return g_steal_pointer (&obj); } |