Re: [RFC PATCH v2 2/2] platform: add support for WireGuard links



On Thu, 2018-03-29 at 21:00 +0100, Javier Arteaga wrote:
Add support for a new wireguard link type to the platform code. For
now
this only covers querying existing links via Generic Netlink and
parsing
them into platform objects.

[TODO: detect wireguard interface changes and invalidate nmp cache]


Hi,

it looks quite good!! :)
thanks



---
 libnm-core/nm-core-types-internal.h |  31 ++++
 src/nm-types.h                      |   2 +
 src/platform/nm-linux-platform.c    | 302
++++++++++++++++++++++++++++++++++++
 src/platform/nm-platform.c          | 133 ++++++++++++++++
 src/platform/nm-platform.h          |  19 +++
 src/platform/nmp-object.c           |  95 ++++++++++++
 src/platform/nmp-object.h           |   9 ++
 7 files changed, 591 insertions(+)

diff --git a/libnm-core/nm-core-types-internal.h b/libnm-core/nm-
core-types-internal.h
index 4d43aaf45..1c4d74ca9 100644
--- a/libnm-core/nm-core-types-internal.h
+++ b/libnm-core/nm-core-types-internal.h

@@ -26,11 +26,42 @@
 #error Cannot use this header.
 #endif
 
+#include "nm-utils/c-list.h"
+
 typedef struct {
      guint32 from;
      guint32 to;
 } NMVlanQosMapping;
 
+typedef struct {
+     guint16 family;

Family always either 2 (AF_INET) or 10 (AF_INET6).
Can you make it a gint8, and move it together with "cidr" field?


+     union {
+             struct in_addr ip4;
+             struct in6_addr ip6;
+     } ip;

NMIPAddr?


+     guint8 cidr;
+
+     CList allowedips_lst;
+} 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;
+     struct timespec last_handshake_time;
+     guint64 rx_bytes, tx_bytes;
+     guint16 persistent_keepalive_interval;
+
+     CList allowedips_lst_head;

CList is a great data structure (+1 for simplicity). But in this case,
can you not just allocate an array?

        guint allowed_ips_len;
        const NMWireguardAllowedIP *allowed_ips;

I would see a point in making NMWireguardAllowedIP individually
allocated structures, if they'd be reference-counted. But embedding a
CList in the structure, kinda prevents meaningfully sharing it, because
the entire list would be shared too (at which point, there is no need
to make individual elements shared/ref-counted/malloc-ed).
        

+} 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/src/nm-types.h b/src/nm-types.h
index 487b001ec..95248ab19 100644
--- a/src/nm-types.h
+++ b/src/nm-types.h
@@ -162,6 +162,7 @@ typedef enum {
      NM_LINK_TYPE_VETH,
      NM_LINK_TYPE_VLAN,
      NM_LINK_TYPE_VXLAN,
+     NM_LINK_TYPE_WIREGUARD,
 
      /* Software types with slaves */
      NM_LINK_TYPE_BRIDGE = 0x10000 | 0x20000,
@@ -194,6 +195,7 @@ typedef enum {
      NMP_OBJECT_TYPE_LNK_TUN,
      NMP_OBJECT_TYPE_LNK_VLAN,
      NMP_OBJECT_TYPE_LNK_VXLAN,
+     NMP_OBJECT_TYPE_LNK_WIREGUARD,
 
      __NMP_OBJECT_TYPE_LAST,
      NMP_OBJECT_TYPE_MAX = __NMP_OBJECT_TYPE_LAST - 1,
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-
linux-platform.c
index b8d252070..13546cbdb 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -173,6 +173,40 @@ G_STATIC_ASSERT (RTA_MAX == (__RTA_MAX - 1));
 
 /*******************************************************************
**********/
 
+#define WG_CMD_GET_DEVICE 0
+#define WG_CMD_SET_DEVICE 1
+
+#define IFLA_WG_DEVICE_UNSPEC           0
+#define IFLA_WG_DEVICE_IFINDEX          1
+#define IFLA_WG_DEVICE_IFNAME           2
+#define IFLA_WG_DEVICE_PRIVATE_KEY      3
+#define IFLA_WG_DEVICE_PUBLIC_KEY       4
+#define IFLA_WG_DEVICE_FLAGS            5
+#define IFLA_WG_DEVICE_LISTEN_PORT      6
+#define IFLA_WG_DEVICE_FWMARK           7
+#define IFLA_WG_DEVICE_PEERS            8
+#define __IFLA_WG_DEVICE_MAX            9
+
+#define IFLA_WG_PEER_UNSPEC                        0
+#define IFLA_WG_PEER_PUBLIC_KEY                    1
+#define IFLA_WG_PEER_PRESHARED_KEY                 2
+#define IFLA_WG_PEER_FLAGS                         3
+#define IFLA_WG_PEER_ENDPOINT                      4
+#define IFLA_WG_PEER_PERSISTENT_KEEPALIVE_INTERVAL 5
+#define IFLA_WG_PEER_LAST_HANDSHAKE_TIME           6
+#define IFLA_WG_PEER_RX_BYTES                      7
+#define IFLA_WG_PEER_TX_BYTES                      8
+#define IFLA_WG_PEER_ALLOWEDIPS                    9

WireGuard's uapi headers seem to call these fields differently.
E.g. WGPEER_A_LAST_HANDSHAKE_TIME instead of
IFLA_WG_PEER_LAST_HANDSHAKE_TIME.

These compat defines are here, so we can compile against older kernel
headers. The name should be identical to the kernel API, except,
maybe a NM_ prefix, to distinuish them from the real name. The NM-
prefix is used if the define is put to a header, otherwise, it is not
used.



+#define __IFLA_WG_PEER_MAX                         10
+
+#define IFLA_WG_ALLOWEDIP_UNSPEC        0
+#define IFLA_WG_ALLOWEDIP_FAMILY        1
+#define IFLA_WG_ALLOWEDIP_IPADDR        2
+#define IFLA_WG_ALLOWEDIP_CIDR_MASK     3
+#define __IFLA_WG_ALLOWEDIP_MAX         4
+
+/*******************************************************************
**********/
+
 #define _NMLOG_PREFIX_NAME                "platform-linux"
 #define _NMLOG_DOMAIN                     LOGD_PLATFORM
 #define _NMLOG2_DOMAIN                    LOGD_PLATFORM
@@ -554,6 +588,7 @@ static const LinkDesc linktypes[] = {
      {
NM_LINK_TYPE_VETH,          "veth",        "veth",        NULL },
      {
NM_LINK_TYPE_VLAN,          "vlan",        "vlan",        "vlan" },
      {
NM_LINK_TYPE_VXLAN,         "vxlan",       "vxlan",       "vxlan" },
+     {
NM_LINK_TYPE_WIREGUARD,     "wireguard",   "wireguard",   "wireguard"
},
 
      {
NM_LINK_TYPE_BRIDGE,        "bridge",      "bridge",      "bridge" },
      {
NM_LINK_TYPE_BOND,          "bond",        "bond",        "bond" },
@@ -1548,6 +1583,40 @@ _parse_lnk_vlan (const char *kind, struct
nlattr *info_data)
 
 /*******************************************************************
**********/
 
+static void
+_wireguard_peer_free (gpointer data)
+{
+     NMWireguardPeer *peer = data;
+     NMWireguardAllowedIP *allowedip;
+
+     c_list_for_each_entry (allowedip, &peer-
allowedips_lst_head, allowedips_lst)
+             g_free (allowedip);

this would be wrong, because you'd need c_list_for_each_entry_safe().
Anyway, let's not make this a C-List.

+     g_free (peer);
+}
+
+static NMPObject *
+_parse_lnk_wireguard (const char *kind)
+{
+     nm_auto_nmpobj NMPObject *obj = NULL;
+     NMPObject *obj_result;
+     NMPObjectLnkWireguard *wgobj;
+
+     if (!nm_streq0 (kind, "wireguard"))
+             return NULL;
+
+     obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_WIREGUARD, NULL);
+     wgobj = &obj->_lnk_wireguard;
+
+     wgobj->peers = g_ptr_array_sized_new (1);
+     g_ptr_array_set_free_func (wgobj->peers,
_wireguard_peer_free);

this doesn't seem useful. _parse_lnk_wireguard() doesn't really parse
anything. Just drop.

+
+     obj_result = obj;
+     obj = NULL;
+     return obj_result;
+}
+
+/*******************************************************************
**********/
+
 /* The installed kernel headers might not have VXLAN stuff at all,
or
  * they might have the original properties, but not PORT, GROUP6, or
LOCAL6.
  * So until we depend on kernel >= 3.11, we just ignore the actual
enum
@@ -1875,6 +1944,9 @@ _new_from_nl_link (NMPlatform *platform, const
NMPCache *cache, struct nlmsghdr
      case NM_LINK_TYPE_VXLAN:
              lnk_data = _parse_lnk_vxlan (nl_info_kind,
nl_info_data);
              break;
+     case NM_LINK_TYPE_WIREGUARD:
+             lnk_data = _parse_lnk_wireguard (nl_info_kind);

here, no need to create an empty lnk_data instance.

+             break;
      default:
              lnk_data_complete_from_cache = FALSE;
              break;
@@ -1914,6 +1986,15 @@ _new_from_nl_link (NMPlatform *platform, const
NMPCache *cache, struct nlmsghdr
                              obj->link.tx_packets = link_cached-
link.tx_packets;
                              obj->link.tx_bytes = link_cached-
link.tx_bytes;
                      }
+             } else {

I think, it's not really if-else.

By default, you'd assume that the cache already contains a suitable
NMPObjectLnkWireguard instance. Just look into the cache (the lines
with the "if", and if it's there that's it.

Only if no NMPObjectLnkWireguard can be found in the cache, fetch it
the first time. As said, this leaves out some notification mechanism to
 refetch the data as necessary. For now, just first do the if-part
(always), and then always refetch via genl. Then, compare the two, and
if they differ, keep the newly received one.

+                     /* TODO: we have no mechanism to detect
changes to cached WireGuard
+                      * interfaces yet, so any changes will not
be picked up. */
+                     switch (obj->link.type) {
+                     case NM_LINK_TYPE_WIREGUARD:
+                             nm_platform_wireguard_get_link_prope
rties (platform, &obj->link, lnk_data);

this can be just a static function inside nm-linux-platform.c. Do you
expect this to be useful from somewhere else?

+                     default:
+                             break;
+                     }
              }
      }
 
@@ -2982,6 +3063,8 @@ typedef struct {
      GIOChannel *event_channel;
      guint event_id;
 
+     struct nl_sock *genlh;
+
      bool pruning[_DELAYED_ACTION_IDX_REFRESH_ALL_NUM];
 
      bool sysctl_get_warned;
@@ -3006,6 +3089,8 @@ typedef struct {
      } delayed_action;
 
      GHashTable *wifi_data;
+
+     int wireguard_family_id;
 } NMLinuxPlatformPrivate;
 
 struct _NMLinuxPlatform {
@@ -6151,6 +6236,211 @@ mesh_set_ssid (NMPlatform *platform, int
ifindex, const guint8 *ssid, gsize len)
 
 /*******************************************************************
**********/
 
+static int
+_wireguard_allowedip_from_nla (struct nlattr *allowedip_attr,
NMWireguardAllowedIP *allowedip)
+{
+     static const struct nla_policy
allowedip_policy[__IFLA_WG_ALLOWEDIP_MAX] = {
+             [IFLA_WG_ALLOWEDIP_FAMILY]    = { .type = NLA_U16 },
+             [IFLA_WG_ALLOWEDIP_IPADDR]    = { .minlen = sizeof
(struct in_addr) },
+             [IFLA_WG_ALLOWEDIP_CIDR_MASK] = { .type = NLA_U8 },
+     };
+     struct nlattr *tba[__IFLA_WG_ALLOWEDIP_MAX];
+     int addr_len;
+     int ret;
+
+     ret = nla_parse_nested (tba, __IFLA_WG_ALLOWEDIP_MAX,
allowedip_attr, allowedip_policy);
+     if (ret)
+             goto errout;
+
+     if (tba[IFLA_WG_ALLOWEDIP_FAMILY])
+             allowedip->family = nla_get_u16
(tba[IFLA_WG_ALLOWEDIP_FAMILY]);
+
+     if (allowedip->family == AF_INET)
+             addr_len = sizeof (in_addr_t);
+     else if (allowedip->family == AF_INET6)
+             addr_len = sizeof (struct in6_addr);
+     else {
+             ret = -EAFNOSUPPORT;
+             goto errout;
+     }
+
+     ret = -EMSGSIZE;
+     _check_addr_or_errout (tba, IFLA_WG_ALLOWEDIP_IPADDR,
addr_len);
+     if (tba[IFLA_WG_ALLOWEDIP_IPADDR])
+             memcpy (&allowedip->ip, nla_data
(tba[IFLA_WG_ALLOWEDIP_IPADDR]), addr_len);
+
+     if (tba[IFLA_WG_ALLOWEDIP_CIDR_MASK])
+             allowedip->cidr = nla_get_u8
(tba[IFLA_WG_ALLOWEDIP_CIDR_MASK]);
+
+     ret = 0;
+errout:
+     return ret;
+}
+
+static int
+_wireguard_peer_from_nla (struct nlattr *peer_attr, NMWireguardPeer
*peer)
+{
+     static const struct nla_policy
peer_policy[__IFLA_WG_PEER_MAX] = {
+             [IFLA_WG_PEER_PUBLIC_KEY]                    = {
.minlen = NM_WG_PUBLIC_KEY_LEN },
+             [IFLA_WG_PEER_PRESHARED_KEY]                 = { },
+             [IFLA_WG_PEER_FLAGS]                         = {
.type = NLA_U32 },
+             [IFLA_WG_PEER_ENDPOINT]                      = { },
+             [IFLA_WG_PEER_PERSISTENT_KEEPALIVE_INTERVAL] = {
.type = NLA_U16 },
+             [IFLA_WG_PEER_LAST_HANDSHAKE_TIME]           = { },
+             [IFLA_WG_PEER_RX_BYTES]                      = {
.type = NLA_U64 },
+             [IFLA_WG_PEER_TX_BYTES]                      = {
.type = NLA_U64 },
+             [IFLA_WG_PEER_ALLOWEDIPS]                    = {
.type = NLA_NESTED },
+     };
+     struct nlattr *tbp[__IFLA_WG_PEER_MAX];
+     int ret;
+
+     if (nla_parse_nested (tbp, __IFLA_WG_PEER_MAX, peer_attr,
peer_policy)) {
+             ret = -EBADMSG;
+             goto errout;
+     }
+
+     if (tbp[IFLA_WG_PEER_PUBLIC_KEY])
+             memcpy (&peer->public_key, nla_data
(tbp[IFLA_WG_PEER_PUBLIC_KEY]), sizeof (peer->public_key));
+     if (tbp[IFLA_WG_PEER_PRESHARED_KEY])
+             memcpy (&peer->preshared_key, nla_data
(tbp[IFLA_WG_PEER_PRESHARED_KEY]), sizeof (peer->preshared_key));
+     if (tbp[IFLA_WG_PEER_ENDPOINT])
+             memcpy (&peer->endpoint, nla_data
(tbp[IFLA_WG_PEER_ENDPOINT]), sizeof (peer->endpoint));

this seems wrong. The size of this attribute depends on addr_family.

+     if (tbp[IFLA_WG_PEER_PERSISTENT_KEEPALIVE_INTERVAL])
+             peer->persistent_keepalive_interval = nla_get_u64
(tbp[IFLA_WG_PEER_PERSISTENT_KEEPALIVE_INTERVAL]);
+     if (tbp[IFLA_WG_PEER_LAST_HANDSHAKE_TIME])
+             memcpy (&peer->last_handshake_time, nla_data
(tbp[IFLA_WG_PEER_LAST_HANDSHAKE_TIME]), sizeof (peer-
last_handshake_time));
+     if (tbp[IFLA_WG_PEER_RX_BYTES])
+             peer->rx_bytes = nla_get_u64
(tbp[IFLA_WG_PEER_RX_BYTES]);
+     if (tbp[IFLA_WG_PEER_TX_BYTES])
+             peer->tx_bytes = nla_get_u64
(tbp[IFLA_WG_PEER_TX_BYTES]);
+
+     if (tbp[IFLA_WG_PEER_ALLOWEDIPS]) {
+             struct nlattr *allowedip_attr;
+             int rem;
+
+             c_list_init (&peer->allowedips_lst_head);
+             nla_for_each_nested (allowedip_attr,
tbp[IFLA_WG_PEER_ALLOWEDIPS], rem) {
+                     NMWireguardAllowedIP *allowedip =
g_new0(NMWireguardAllowedIP, 1);
+                     ret = _wireguard_allowedip_from_nla
(allowedip_attr, allowedip);
+                     if (ret)
+                             goto errout;
+                     c_list_link_tail (&peer-
allowedips_lst_head, &allowedip->allowedips_lst);
+             }
+     }
+
+     ret = 0;
+errout:
+     return ret;
+}
+
+static int
+_wireguard_read_device_cb (struct nl_msg *msg, void *arg)
+{
+     static const struct nla_policy
device_policy[__IFLA_WG_DEVICE_MAX] = {
+             [IFLA_WG_DEVICE_IFINDEX]     = { .type = NLA_U32 },
+             [IFLA_WG_DEVICE_IFNAME]      = { .type =
NLA_NUL_STRING, .maxlen = IFNAMSIZ },
+             [IFLA_WG_DEVICE_PRIVATE_KEY] = { },
+             [IFLA_WG_DEVICE_PUBLIC_KEY]  = { },
+             [IFLA_WG_DEVICE_FLAGS]       = { .type = NLA_U32 },
+             [IFLA_WG_DEVICE_LISTEN_PORT] = { .type = NLA_U16 },
+             [IFLA_WG_DEVICE_FWMARK]      = { .type = NLA_U32 },
+             [IFLA_WG_DEVICE_PEERS]       = { .type = NLA_NESTED
},
+     };
+     struct nlattr *tbd[__IFLA_WG_DEVICE_MAX];
+     NMPObject *obj = arg;
+     NMPlatformLnkWireguard *props = &obj->lnk_wireguard;
+     NMPObjectLnkWireguard *wgobj = &obj->_lnk_wireguard;
+     struct nlmsghdr *nlh = nlmsg_hdr (msg);
+     int ret;
+
+     ret = genlmsg_parse (nlh, 0, tbd, __IFLA_WG_DEVICE_MAX - 1,
device_policy);
+     if (ret)
+             goto errout;
+
+     if (tbd[IFLA_WG_DEVICE_PRIVATE_KEY])
+             nla_memcpy (props->private_key,
tbd[IFLA_WG_DEVICE_PRIVATE_KEY], sizeof (props->private_key));
+     if (tbd[IFLA_WG_DEVICE_PUBLIC_KEY])
+             nla_memcpy (props->public_key,
tbd[IFLA_WG_DEVICE_PUBLIC_KEY], sizeof (props->public_key));
+     if (tbd[IFLA_WG_DEVICE_LISTEN_PORT])
+             props->listen_port = nla_get_u16
(tbd[IFLA_WG_DEVICE_LISTEN_PORT]);
+     if (tbd[IFLA_WG_DEVICE_FWMARK])
+             props->fwmark = nla_get_u32
(tbd[IFLA_WG_DEVICE_FWMARK]);
+
+     if (tbd[IFLA_WG_DEVICE_PEERS]) {
+             struct nlattr *peer_attr;
+             int rem;
+
+             nla_for_each_nested (peer_attr,
tbd[IFLA_WG_DEVICE_PEERS], rem) {
+                     NMWireguardPeer *peer =
g_new0(NMWireguardPeer, 1);
+                     ret = _wireguard_peer_from_nla (peer_attr,
peer);
+                     if (ret)
+                             goto errout;
+                     g_ptr_array_add (wgobj->peers, peer);
+             }
+     }
+
+     return NL_OK;
+errout:
+     return NL_SKIP;
+}
+
+static void
+_wireguard_coalesce_peers (GPtrArray *peers)
+{
+     gsize i = 0;
+
+     while (i + 1 < peers->len) {
+             NMWireguardPeer *pa = g_ptr_array_index(peers, i);
+             NMWireguardPeer *pb = g_ptr_array_index(peers, i +
1);
+
+             if (memcmp(pa->public_key, pb->public_key, sizeof
(pa->public_key))) {

space between "memcmp" and "(".

+                     i++;
+                     continue;
+             }
+
+             c_list_splice (&pa->allowedips_lst_head, &pb-
allowedips_lst_head);
+             g_ptr_array_remove_index_fast (peers, i + 1);
+     }
+}
+
+static gboolean
+wireguard_get_link_properties (NMPlatform *platform, const
NMPlatformLink *link, NMPObject *obj)
+{
+     NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE
(platform);
+     nm_auto_nlmsg struct nl_msg *msg = NULL;
+     struct nl_cb cb = {
+             .valid_cb = _wireguard_read_device_cb,
+             .valid_arg = obj,
+     };
+
+     g_return_val_if_fail (priv->wireguard_family_id, FALSE);

g_return_*() is an assertion. I guess there can be run-time situations,
where this doesn't hold.

+
+     msg = nlmsg_alloc ();
+     if (!msg)
+             return FALSE;
+
+     if (!genlmsg_put (msg, NL_AUTO_PORT, NL_AUTO_SEQ, priv-
wireguard_family_id,
+                       0, NLM_F_DUMP, WG_CMD_GET_DEVICE, 1))
+             return FALSE;
+
+     NLA_PUT_U32 (msg, IFLA_WG_DEVICE_IFINDEX, link->ifindex);
+
+     if (nl_send_auto (priv->genlh, msg) < 0)
+             return FALSE;
+
+     if (nl_recvmsgs (priv->genlh, &cb) < 0)
+             return FALSE;
+
+     _wireguard_coalesce_peers(obj->_lnk_wireguard.peers);
+
+     return TRUE;
+
+nla_put_failure:
+     return FALSE;
+}
+
+/*******************************************************************
**********/
+
 static gboolean
 link_get_wake_on_lan (NMPlatform *platform, int ifindex)
 {
@@ -7066,6 +7356,16 @@ constructed (GObject *_object)
                                      (EVENT_CONDITIONS |
ERROR_CONDITIONS | DISCONNECT_CONDITIONS),
                                       event_handler, platform);
 
+     priv->genlh = nl_socket_alloc ();
+     g_assert (priv->genlh);
+
+     nle = nl_connect (priv->genlh, NETLINK_GENERIC);
+     g_assert (!nle);
+     _LOGD ("Generic netlink socket established: port=%u, fd=%d",
nl_socket_get_local_port (priv->genlh), nl_socket_get_fd (priv-
genlh));
+
+     priv->wireguard_family_id = genl_ctrl_resolve (priv->genlh,
"wireguard");
+     _LOG2D ("kernel-support: wireguard: %s", priv-
wireguard_family_id ? "detected" : "not detected");

I wonder, what happens if the wireguard module is not loaded initially.
I guess, caching the family-id is right, but you need to retry if it's
not avilable. For example, in wireguard_get_link_properties()


+
      /* complete construction of the GObject instance before
populating the cache. */
      G_OBJECT_CLASS (nm_linux_platform_parent_class)->constructed 
(_object);
 
@@ -7227,6 +7527,8 @@ nm_linux_platform_class_init
(NMLinuxPlatformClass *klass)
      platform_class->mesh_set_channel = mesh_set_channel;
      platform_class->mesh_set_ssid = mesh_set_ssid;
 
+     platform_class->wireguard_get_link_properties =
wireguard_get_link_properties;
+
      platform_class->link_gre_add = link_gre_add;
      platform_class->link_ip6tnl_add = link_ip6tnl_add;
      platform_class->link_macsec_add = link_macsec_add;
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index dac0a5ff9..f430c2600 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -27,6 +27,8 @@
 #include <unistd.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netdb.h>
 #include <string.h>
 #include <linux/ip.h>
 #include <linux/if_tun.h>
@@ -1912,6 +1914,12 @@ nm_platform_link_get_lnk_vxlan (NMPlatform
*self, int ifindex, const NMPlatformL
      return _link_get_lnk (self, ifindex, NM_LINK_TYPE_VXLAN,
out_link);
 }
 
+const NMPlatformLnkWireguard *
+nm_platform_link_get_lnk_wireguard (NMPlatform *self, int ifindex,
const NMPlatformLink **out_link)
+{
+     return _link_get_lnk (self, ifindex, NM_LINK_TYPE_WIREGUARD,
out_link);
+}
+
 /*******************************************************************
**********/
 
 /**
@@ -2907,6 +2915,16 @@ nm_platform_mesh_set_ssid (NMPlatform *self,
int ifindex, const guint8 *ssid, gs
      return klass->mesh_set_ssid (self, ifindex, ssid, len);
 }
 
+gboolean
+nm_platform_wireguard_get_link_properties (NMPlatform *self, const
NMPlatformLink *link, NMPObject *obj)
+{
+     _CHECK_SELF (self, klass, FALSE);
+
+     g_return_val_if_fail (obj != NULL, FALSE);
+
+     return klass->wireguard_get_link_properties (self, link,
obj);
+}
+
 #define TO_STRING_DEV_BUF_SIZE (5+15+1)
 static const char *
 _to_string_dev (NMPlatform *self, int ifindex, char *buf, size_t
size)
@@ -5087,6 +5105,7 @@ nm_platform_lnk_tun_to_string (const
NMPlatformLnkTun *lnk, char *buf, gsize len
                  lnk->multi_queue ? " multi_queue" : "",
                  lnk->owner_valid ? nm_sprintf_buf (str_owner, "
owner %u", (guint) lnk->owner) : "",
                  lnk->group_valid ? nm_sprintf_buf (str_group, "
group %u", (guint) lnk->group) : "");
+
      return buf;
 }
 
@@ -5193,6 +5212,99 @@ nm_platform_lnk_vxlan_to_string (const
NMPlatformLnkVxlan *lnk, char *buf, gsize
      return buf;
 }
 
+const char *
+nm_platform_wireguard_peer_to_string (const NMWireguardPeer *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};
+     NMWireguardAllowedIP *allowedip;
+     guint8 nonzero_key = 0;
+     gsize 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];
+
+     public_b64 = g_base64_encode (peer->public_key, sizeof
(peer->public_key));
+
+     nm_utils_strbuf_append (&buf, &len,
+                             "{ "
+                             "public_key %s "
+                             "%s" /* preshared key indicator */
+                             "%s" /* endpoint */
+                             "rx %"G_GUINT64_FORMAT" "
+                             "tx %"G_GUINT64_FORMAT" "
+                             "allowedips {",
+                             public_b64,
+                             nonzero_key ? "preshared_key
(hidden) " : "",
+                             s_endpoint,
+                             peer->rx_bytes,
+                             peer->tx_bytes);
+
+
+     c_list_for_each_entry (allowedip, &peer-
allowedips_lst_head, allowedips_lst) {
+             const char *ret;
+
+             ret = inet_ntop (allowedip->family, &allowedip->ip,
s_address, sizeof(s_address));
+
+             nm_utils_strbuf_append (&buf, &len,
+                                     " %s/%u",
+                                     ret ? s_address :
"<EAFNOSUPPORT>",
+                                     allowedip->cidr);
+     }
+
+     nm_utils_strbuf_append_str (&buf, &len, " } }");
+     return buf;
+}
+
+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];
+
+     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) " : "",
+                 lnk->listen_port,
+                 lnk->fwmark);
+
+     return buf;
+}
+
 /**
  * nm_platform_ip4_address_to_string:
  * @route: pointer to NMPlatformIP4Address address structure
@@ -6019,6 +6131,27 @@ nm_platform_lnk_vxlan_cmp (const
NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan
      return 0;
 }
 
+void
+nm_platform_lnk_wireguard_hash_update (const NMPlatformLnkWireguard
*obj, NMHashState *h)
+{
+     nm_hash_update_vals (h,
+                          obj->listen_port,
+                          obj->fwmark);
+     nm_hash_update (h, obj->private_key, sizeof (obj-
private_key));
+     nm_hash_update (h, obj->public_key, sizeof (obj-
public_key));
+}
+
+int
+nm_platform_lnk_wireguard_cmp (const NMPlatformLnkWireguard *a,
const NMPlatformLnkWireguard *b)
+{
+     NM_CMP_SELF (a, b);
+     NM_CMP_FIELD_MEMCMP (a, b, private_key);
+     NM_CMP_FIELD_MEMCMP (a, b, public_key);
+     NM_CMP_FIELD (a, b, listen_port);
+     NM_CMP_FIELD (a, b, fwmark);
+     return 0;
+}
+
 void
 nm_platform_ip4_address_hash_update (const NMPlatformIP4Address
*obj, NMHashState *h)
 {
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index 824735083..0b189c3f6 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -725,6 +725,13 @@ typedef struct {
      bool l3miss:1;
 } NMPlatformLnkVxlan;
 
+typedef struct {
+     guint8 private_key[NM_WG_PUBLIC_KEY_LEN];
+     guint8 public_key[NM_WG_PUBLIC_KEY_LEN];
+     guint16 listen_port;
+     guint32 fwmark;
+} NMPlatformLnkWireguard;
+
 typedef enum {
      NM_PLATFORM_LINK_DUPLEX_UNKNOWN,
      NM_PLATFORM_LINK_DUPLEX_HALF,
@@ -874,6 +881,8 @@ typedef struct {
      gboolean    (*mesh_set_channel)      (NMPlatform *, int
ifindex, guint32 channel);
      gboolean    (*mesh_set_ssid)         (NMPlatform *, int
ifindex, const guint8 *ssid, gsize len);
 
+     gboolean    (*wireguard_get_link_properties)    (NMPlatform
*, const NMPlatformLink *, NMPObject *obj);
+
      gboolean (*object_delete) (NMPlatform *, const NMPObject
*obj);
 
      gboolean (*ip4_address_add) (NMPlatform *,
@@ -1198,6 +1207,7 @@ const NMPlatformLnkSit
*nm_platform_link_get_lnk_sit (NMPlatform *self, int ifin
 const NMPlatformLnkTun *nm_platform_link_get_lnk_tun (NMPlatform
*self, int ifindex, const NMPlatformLink **out_link);
 const NMPlatformLnkVlan *nm_platform_link_get_lnk_vlan (NMPlatform
*self, int ifindex, const NMPlatformLink **out_link);
 const NMPlatformLnkVxlan *nm_platform_link_get_lnk_vxlan (NMPlatform
*self, int ifindex, const NMPlatformLink **out_link);
+const NMPlatformLnkWireguard *nm_platform_link_get_lnk_wireguard
(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
 
 NMPlatformError nm_platform_link_vlan_add (NMPlatform *self,
                                            const char *name,
@@ -1252,6 +1262,8 @@
guint32     nm_platform_mesh_get_channel      (NMPlatform *self, int
ifindex);
 gboolean    nm_platform_mesh_set_channel      (NMPlatform *self, int
ifindex, guint32 channel);
 gboolean    nm_platform_mesh_set_ssid         (NMPlatform *self, int
ifindex, const guint8 *ssid, gsize len);
 
+gboolean    nm_platform_wireguard_get_link_properties    (NMPlatform
*self, const NMPlatformLink *link, NMPObject *obj);
+
 void                   nm_platform_ip4_address_set_addr
(NMPlatformIP4Address *addr, in_addr_t address, guint8 plen);
 const struct in6_addr *nm_platform_ip6_address_get_peer (const
NMPlatformIP6Address *addr);
 
@@ -1373,6 +1385,7 @@ const char *nm_platform_lnk_sit_to_string
(const NMPlatformLnkSit *lnk, char *bu
 const char *nm_platform_lnk_tun_to_string (const NMPlatformLnkTun
*lnk, char *buf, gsize len);
 const char *nm_platform_lnk_vlan_to_string (const NMPlatformLnkVlan
*lnk, char *buf, gsize len);
 const char *nm_platform_lnk_vxlan_to_string (const
NMPlatformLnkVxlan *lnk, char *buf, gsize len);
+const char *nm_platform_lnk_wireguard_to_string (const
NMPlatformLnkWireguard *lnk, char *buf, gsize len);
 const char *nm_platform_ip4_address_to_string (const
NMPlatformIP4Address *address, char *buf, gsize len);
 const char *nm_platform_ip6_address_to_string (const
NMPlatformIP6Address *address, char *buf, gsize len);
 const char *nm_platform_ip4_route_to_string (const
NMPlatformIP4Route *route, char *buf, gsize len);
@@ -1386,6 +1399,10 @@ 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,
+                                                  char *buf,
+                                                  gsize len);
+
 int nm_platform_link_cmp (const NMPlatformLink *a, const
NMPlatformLink *b);
 int nm_platform_lnk_gre_cmp (const NMPlatformLnkGre *a, const
NMPlatformLnkGre *b);
 int nm_platform_lnk_infiniband_cmp (const NMPlatformLnkInfiniband
*a, const NMPlatformLnkInfiniband *b);
@@ -1397,6 +1414,7 @@ int nm_platform_lnk_sit_cmp (const
NMPlatformLnkSit *a, const NMPlatformLnkSit *
 int nm_platform_lnk_tun_cmp (const NMPlatformLnkTun *a, const
NMPlatformLnkTun *b);
 int nm_platform_lnk_vlan_cmp (const NMPlatformLnkVlan *a, const
NMPlatformLnkVlan *b);
 int nm_platform_lnk_vxlan_cmp (const NMPlatformLnkVxlan *a, const
NMPlatformLnkVxlan *b);
+int nm_platform_lnk_wireguard_cmp (const NMPlatformLnkWireguard *a,
const NMPlatformLnkWireguard *b);
 int nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a,
const NMPlatformIP4Address *b);
 int nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a,
const NMPlatformIP6Address *b);
 
@@ -1433,6 +1451,7 @@ void nm_platform_lnk_sit_hash_update (const
NMPlatformLnkSit *obj, NMHashState *
 void nm_platform_lnk_tun_hash_update (const NMPlatformLnkTun *obj,
NMHashState *h);
 void nm_platform_lnk_vlan_hash_update (const NMPlatformLnkVlan *obj,
NMHashState *h);
 void nm_platform_lnk_vxlan_hash_update (const NMPlatformLnkVxlan
*obj, NMHashState *h);
+void nm_platform_lnk_wireguard_hash_update (const
NMPlatformLnkWireguard *obj, NMHashState *h);
 
 void nm_platform_qdisc_hash_update (const NMPlatformQdisc *obj,
NMHashState *h);
 void nm_platform_tfilter_hash_update (const NMPlatformTfilter *obj,
NMHashState *h);
diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c
index 666d334c2..91f015bcc 100644
--- a/src/platform/nmp-object.c
+++ b/src/platform/nmp-object.c
@@ -466,6 +466,12 @@ _vt_cmd_obj_dispose_lnk_vlan (NMPObject *obj)
      g_free ((gpointer) obj->_lnk_vlan.egress_qos_map);
 }
 
+static void
+_vt_cmd_obj_dispose_lnk_wireguard (NMPObject *obj)
+{
+     g_ptr_array_unref (obj->_lnk_wireguard.peers);
+}
+
 static NMPObject *
 _nmp_object_new_from_class (const NMPClass *klass)
 {
@@ -720,6 +726,51 @@ _vt_cmd_obj_to_string_lnk_vlan (const NMPObject
*obj, NMPObjectToStringMode to_s
      }
 }
 
+static const char *
+_vt_cmd_obj_to_string_lnk_wireguard (const NMPObject *obj,
NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size)
+{
+     const NMPClass *klass;
+     char buf2[sizeof (_nm_utils_to_string_buffer)];
+     char *b;
+     gsize i, l;
+
+     klass = NMP_OBJECT_GET_CLASS (obj);
+
+     switch (to_string_mode) {
+     case NMP_OBJECT_TO_STRING_ID:
+             g_snprintf (buf, buf_size, "%p", obj);
+             return buf;
+     case NMP_OBJECT_TO_STRING_ALL:
+             b = buf;
+
+             nm_utils_strbuf_append (&b, &buf_size,
+                                     "[%s,%p,%u,%calive,%cvisible
; %s peers (%u) {",
+                                     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);
+
+             for (i = 0; i < obj->_lnk_wireguard.peers->len; i++)
{
+                     const NMWireguardPeer *peer =
g_ptr_array_index(obj->_lnk_wireguard.peers, i);
+                     nm_platform_wireguard_peer_to_string (peer,
b, buf_size);
+                     l = strlen (b);
+                     b += l;
+                     buf_size -= l;
+             }
+
+             nm_utils_strbuf_append_str (&b, &buf_size, " }");
+
+             return buf;
+     case NMP_OBJECT_TO_STRING_PUBLIC:
+             NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string
(&obj->object, buf, buf_size);
+
+             return buf;
+     default:
+             g_return_val_if_reached ("ERROR");
+     }
+}
+
 #define _vt_cmd_plobj_to_string_id(type, plat_type, ...) \
 static const char * \
 _vt_cmd_plobj_to_string_id_##type (const NMPlatformObject *_obj,
char *buf, gsize buf_len) \
@@ -787,6 +838,15 @@ _vt_cmd_obj_hash_update_lnk_vlan (const
NMPObject *obj, NMHashState *h)
                                             h);
 }
 
+static void
+_vt_cmd_obj_hash_update_lnk_wireguard (const NMPObject *obj,
NMHashState *h)
+{
+     nm_assert (NMP_OBJECT_GET_TYPE (obj) ==
NMP_OBJECT_TYPE_LNK_WIREGUARD);
+
+     nm_platform_lnk_wireguard_hash_update (&obj->lnk_wireguard,
h);
+     nm_hash_update_val (h, obj->_lnk_wireguard.peers);

You must hash the entire content, not just the peers-pointer.

+}
+
 int
 nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2)
 {
@@ -865,6 +925,18 @@ _vt_cmd_obj_cmp_lnk_vlan (const NMPObject *obj1,
const NMPObject *obj2)
      return c;
 }
 
+static int
+_vt_cmd_obj_cmp_lnk_wireguard (const NMPObject *obj1, const
NMPObject *obj2)
+{
+     int c;
+
+     c = nm_platform_lnk_wireguard_cmp (&obj1->lnk_wireguard,
&obj2->lnk_wireguard);
+     if (c)
+             return c;
+
+     return obj1->_lnk_wireguard.peers < obj2-
_lnk_wireguard.peers;

As with hash-update, you must compare the content of peers.

Sidenote: even if you really wanted to compare the pointers, then "<"
would only return 0 and 1. But cmp must return -1, 0, and 1.


+}
+
 /* @src is a const object, which is not entirely correct for link
types, where
  * we increase the ref count for src->_link.udev.device.
  * Hence, nmp_object_copy() can violate the const promise of @src.
@@ -925,6 +997,13 @@ _vt_cmd_obj_copy_lnk_vlan (NMPObject *dst, const
NMPObject *src)
                                     src-
_lnk_vlan.egress_qos_map);
 }
 
+static void
+_vt_cmd_obj_copy_lnk_wireguard (NMPObject *dst, const NMPObject
*src)
+{
+     dst->lnk_wireguard = src->lnk_wireguard;
+     dst->_lnk_wireguard.peers = g_ptr_array_ref (src-
_lnk_wireguard.peers);
+}
+
 #define _vt_cmd_plobj_id_copy(type, plat_type, cmd) \
 static void \
 _vt_cmd_plobj_id_copy_##type (NMPlatformObject *_dst, const
NMPlatformObject *_src) \
@@ -2818,5 +2897,21 @@ const NMPClass
_nmp_classes[NMP_OBJECT_TYPE_MAX] = {
              .cmd_plobj_hash_update              = (void (*)
(const NMPlatformObject *obj, NMHashState *h))
nm_platform_lnk_vxlan_hash_update,
              .cmd_plobj_cmp                      = (int (*)
(const NMPlatformObject *obj1, const NMPlatformObject *obj2))
nm_platform_lnk_vxlan_cmp,
      },
+     [NMP_OBJECT_TYPE_LNK_WIREGUARD - 1] = {
+             .parent                             =
DEDUP_MULTI_OBJ_CLASS_INIT(),
+             .obj_type                           =
NMP_OBJECT_TYPE_LNK_WIREGUARD,
+             .sizeof_data                        = sizeof
(NMPObjectLnkWireguard),
+             .sizeof_public                      = sizeof
(NMPlatformLnkWireguard),
+             .obj_type_name                      = "wireguard",
+             .lnk_link_type                      =
NM_LINK_TYPE_WIREGUARD,
+             .cmd_obj_hash_update                =
_vt_cmd_obj_hash_update_lnk_wireguard,
+             .cmd_obj_cmp                        =
_vt_cmd_obj_cmp_lnk_wireguard,
+             .cmd_obj_copy                       =
_vt_cmd_obj_copy_lnk_wireguard,
+             .cmd_obj_dispose                    =
_vt_cmd_obj_dispose_lnk_wireguard,
+             .cmd_obj_to_string                  =
_vt_cmd_obj_to_string_lnk_wireguard,
+             .cmd_plobj_to_string                = (const char
*(*) (const NMPlatformObject *obj, char *buf, gsize len))
nm_platform_lnk_wireguard_to_string,
+             .cmd_plobj_hash_update              = (void (*)
(const NMPlatformObject *obj, NMHashState *h))
nm_platform_lnk_vlan_hash_update,
+             .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 0dd2687a3..e64ac6614 100644
--- a/src/platform/nmp-object.h
+++ b/src/platform/nmp-object.h
@@ -213,6 +213,12 @@ typedef struct {
      NMPlatformLnkVxlan _public;
 } NMPObjectLnkVxlan;
 
+typedef struct {
+     NMPlatformLnkWireguard _public;
+
+     GPtrArray *peers;

It makes some sense that @peers is GPtrArray, because that type is ref-
counted, and possibly multiple NMPlatformLnkWireguard instances could
share it.

However, see that NMPObjectLnkVlan doesn't try to share the
ingress_qos_map. It doesn't because, what is instead shared is the
lnk_vlan instance itself.

See how
  - _parse_lnk_vlan() always creates a new NMPObjectLnkVlan instance.
    Since it parses the netlink attribute at once, it just allocates
    a suitable obj->_lnk_vlan.ingress_qos_map.
  - then, it will reach line
    
https://cgit.freedesktop.org/NetworkManager/NetworkManager/tree/src/platform/nm-linux-platform.c?id=eb8257dea5802a004af9cccacb30af98440e2172#n1894
    if the newly created object is identical to the already cached one,
    the newly created object is thrown away and the cached one is kept.
What is shared and ref-countable is the NMPObjectLnkVlan instance itself. Not
the data pointers to ingress_qos_map.

the same is for NMPObjectLnkWireguard.peers. Just make it a plain array, possibly
also add a "peers_len" field.


+} NMPObjectLnkWireguard;
+
 typedef struct {
      NMPlatformIP4Address _public;
 } NMPObjectIP4Address;
@@ -278,6 +284,9 @@ struct _NMPObject {
              NMPlatformLnkVxlan      lnk_vxlan;
              NMPObjectLnkVxlan       _lnk_vxlan;
 
+             NMPlatformLnkWireguard  lnk_wireguard;
+             NMPObjectLnkWireguard   _lnk_wireguard;
+
              NMPlatformIPAddress     ip_address;
              NMPlatformIPXAddress    ipx_address;
              NMPlatformIP4Address    ip4_address;

Attachment: signature.asc
Description: This is a digitally signed message part



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]