@@ -618,6 +618,12 @@ static const struct genl_ops genl_ops[] = {
}
};
+static struct genl_multicast_group genl_mcgrps[] = {
+ {
+ .name = WG_MULTICAST_GROUP_PEER_CHANGE
+ }
+};
+
static struct genl_family genl_family __ro_after_init = {
.ops = genl_ops,
.n_ops = ARRAY_SIZE(genl_ops),
@@ -626,7 +632,9 @@ static struct genl_family genl_family __ro_after_init = {
.maxattr = WGDEVICE_A_MAX,
.module = THIS_MODULE,
.policy = device_policy,
- .netnsok = true
+ .netnsok = true,
+ .mcgrps = genl_mcgrps,
+ .n_mcgrps = ARRAY_SIZE(genl_mcgrps)
};
int __init wg_genetlink_init(void)
@@ -638,3 +646,45 @@ void __exit wg_genetlink_uninit(void)
{
genl_unregister_family(&genl_family);
}
+
+int wg_genl_mcast_peer_endpoint_change(struct wg_peer *peer)
+{
+ struct sk_buff *skb;
+ void *hdr, *peer_nest, *peer_array_nest;
+ int fail = 0;
+
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ hdr = genlmsg_put(skb, 0, 0,
+ &genl_family, 0, WG_CMD_CHANGED_PEER);
+
+ nla_put_u32(skb, WGDEVICE_A_IFINDEX, peer->device->dev->ifindex);
+ nla_put_string(skb, WGDEVICE_A_IFNAME, peer->device->dev->name);
+
+ peer_nest = nla_nest_start(skb, WGDEVICE_A_PEERS);
+ peer_array_nest = nla_nest_start(skb, 0);
+ down_read(&peer->handshake.lock);
+ nla_put(skb, WGPEER_A_PUBLIC_KEY, NOISE_PUBLIC_KEY_LEN,
+ peer->handshake.remote_static);
+ up_read(&peer->handshake.lock);
+
+ read_lock_bh(&peer->endpoint_lock);
+ if (peer->endpoint.addr.sa_family == AF_INET)
+ fail = nla_put(skb, WGPEER_A_ENDPOINT,
+ sizeof(peer->endpoint.addr4),
+ &peer->endpoint.addr4);
+ else if (peer->endpoint.addr.sa_family == AF_INET6)
+ fail = nla_put(skb, WGPEER_A_ENDPOINT,
+ sizeof(peer->endpoint.addr6),
+ &peer->endpoint.addr6);
+ read_unlock_bh(&peer->endpoint_lock);
+ if (fail)
+ return fail;
+
+ nla_nest_end(skb, peer_array_nest);
+ nla_nest_end(skb, peer_nest);
+ genlmsg_end(skb, hdr);
+
+ fail = genlmsg_multicast_netns(&genl_family, dev_net(peer->device->dev),
+ skb, 0, 0, GFP_KERNEL);
+ return fail;
+}
@@ -6,6 +6,10 @@
#ifndef _WG_NETLINK_H
#define _WG_NETLINK_H
+#include "peer.h"
+
+int wg_genl_mcast_peer_endpoint_change(struct wg_peer *peer);
+
int wg_genetlink_init(void);
void wg_genetlink_uninit(void);
@@ -8,6 +8,7 @@
#include "socket.h"
#include "queueing.h"
#include "messages.h"
+#include "netlink.h"
#include <linux/ctype.h>
#include <linux/net.h>
@@ -293,6 +294,9 @@ void wg_socket_set_peer_endpoint(struct wg_peer *peer,
dst_cache_reset(&peer->endpoint_cache);
out:
write_unlock_bh(&peer->endpoint_lock);
+
+ /* We need to notify the netlink listeners for about this change */
+ wg_genl_mcast_peer_endpoint_change(peer);
}
void wg_socket_set_peer_endpoint_from_skb(struct wg_peer *peer,
@@ -136,9 +136,12 @@
#define WG_KEY_LEN 32
+#define WG_MULTICAST_GROUP_PEER_CHANGE "wg_peer_change"
+
enum wg_cmd {
WG_CMD_GET_DEVICE,
WG_CMD_SET_DEVICE,
+ WG_CMD_CHANGED_PEER,
__WG_CMD_MAX
};
#define WG_CMD_MAX (__WG_CMD_MAX - 1)
This commit adds a new multicast group to the netlink api for wireguard. The purpose of this multicast group is to notify userspace when the peers of an interface change. Right now this is only done when the endpoint is changed by whatever means. An example for an consumer of this API would be a service that keeps track of all peer endpoints and sends this information to the peers. This would allow NAT-to-NAT connections without the need of using STUN on each client. In v2 I fixed a possible uninitialized use. Reported-by: kernel test robot <lkp@intel.com> Signed-off-by: Linus Lotz <linus@lotz.li> --- drivers/net/wireguard/netlink.c | 52 ++++++++++++++++++++++++++++++++- drivers/net/wireguard/netlink.h | 4 +++ drivers/net/wireguard/socket.c | 4 +++ include/uapi/linux/wireguard.h | 3 ++ 4 files changed, 62 insertions(+), 1 deletion(-) base-commit: 65f0d2414b7079556fbbcc070b3d1c9f9587606d