summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2026-04-14 11:48:07 -0700
committerJakub Kicinski <kuba@kernel.org>2026-04-14 11:48:07 -0700
commit18cd10d2affe764eb26ec3bd4ffa14c259339ad0 (patch)
treea5c80cfad00fc0afddecbdf77dc7ff8498b4f2c9
parentbc28831d7a09f7058cdca4658d81e5faf635bed7 (diff)
parent60a25ef8dacb3566b1a8c4de00572a498e2a3bf9 (diff)
Merge branch 'wireguard-fixes-for-7-1-rc1'
Jason A. Donenfeld says: ==================== WireGuard fixes for 7.1-rc1 1) Asbjørn's YNL sample, finally merged. Sorry for the wait on this one. 2) A simplification to use kfree_rcu instead of call_rcu, since kfree_rcu now works with kmem caches. 3) A trivial formatting derp. 4) Fix for a deadlock by moving to using exit_rtnl instead of pre_exit. ==================== Link: https://patch.msgid.link/20260414153944.2742252-1-Jason@zx2c4.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-rw-r--r--drivers/net/wireguard/allowedips.c9
-rw-r--r--drivers/net/wireguard/device.c8
-rw-r--r--drivers/net/wireguard/selftest/allowedips.c2
-rw-r--r--tools/net/ynl/tests/.gitignore1
-rw-r--r--tools/net/ynl/tests/wireguard.c106
5 files changed, 113 insertions, 13 deletions
diff --git a/drivers/net/wireguard/allowedips.c b/drivers/net/wireguard/allowedips.c
index 09f7fcd7da78..5ece9acad64d 100644
--- a/drivers/net/wireguard/allowedips.c
+++ b/drivers/net/wireguard/allowedips.c
@@ -48,11 +48,6 @@ static void push_rcu(struct allowedips_node **stack,
}
}
-static void node_free_rcu(struct rcu_head *rcu)
-{
- kmem_cache_free(node_cache, container_of(rcu, struct allowedips_node, rcu));
-}
-
static void root_free_rcu(struct rcu_head *rcu)
{
struct allowedips_node *node, *stack[MAX_ALLOWEDIPS_DEPTH] = {
@@ -271,13 +266,13 @@ static void remove_node(struct allowedips_node *node, struct mutex *lock)
if (free_parent)
child = rcu_dereference_protected(parent->bit[!(node->parent_bit_packed & 1)],
lockdep_is_held(lock));
- call_rcu(&node->rcu, node_free_rcu);
+ kfree_rcu(node, rcu);
if (!free_parent)
return;
if (child)
child->parent_bit_packed = parent->parent_bit_packed;
*(struct allowedips_node **)(parent->parent_bit_packed & ~3UL) = child;
- call_rcu(&parent->rcu, node_free_rcu);
+ kfree_rcu(parent, rcu);
}
static int remove(struct allowedips_node __rcu **trie, u8 bits, const u8 *key,
diff --git a/drivers/net/wireguard/device.c b/drivers/net/wireguard/device.c
index 46a71ec36af8..67b07ee2d660 100644
--- a/drivers/net/wireguard/device.c
+++ b/drivers/net/wireguard/device.c
@@ -411,12 +411,11 @@ static struct rtnl_link_ops link_ops __read_mostly = {
.newlink = wg_newlink,
};
-static void wg_netns_pre_exit(struct net *net)
+static void __net_exit wg_netns_exit_rtnl(struct net *net, struct list_head *dev_kill_list)
{
struct wg_device *wg;
struct wg_peer *peer;
- rtnl_lock();
list_for_each_entry(wg, &device_list, device_list) {
if (rcu_access_pointer(wg->creating_net) == net) {
pr_debug("%s: Creating namespace exiting\n", wg->dev->name);
@@ -429,11 +428,10 @@ static void wg_netns_pre_exit(struct net *net)
mutex_unlock(&wg->device_update_lock);
}
}
- rtnl_unlock();
}
-static struct pernet_operations pernet_ops = {
- .pre_exit = wg_netns_pre_exit
+static struct pernet_operations pernet_ops __read_mostly = {
+ .exit_rtnl = wg_netns_exit_rtnl
};
int __init wg_device_init(void)
diff --git a/drivers/net/wireguard/selftest/allowedips.c b/drivers/net/wireguard/selftest/allowedips.c
index 2da3008c3a01..3e857e6fb627 100644
--- a/drivers/net/wireguard/selftest/allowedips.c
+++ b/drivers/net/wireguard/selftest/allowedips.c
@@ -623,7 +623,7 @@ bool __init wg_allowedips_selftest(void)
test_boolean(!remove(6, b, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 128));
test(6, a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef);
/* invalid CIDR should have no effect and return -EINVAL */
- test_boolean(remove(6, a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 129) == -EINVAL);
+ test_boolean(remove(6, a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 129) == -EINVAL);
test(6, a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef);
remove(6, a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 128);
test_negative(6, a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef);
diff --git a/tools/net/ynl/tests/.gitignore b/tools/net/ynl/tests/.gitignore
index 045385df42a4..a7832ebfdbbc 100644
--- a/tools/net/ynl/tests/.gitignore
+++ b/tools/net/ynl/tests/.gitignore
@@ -7,3 +7,4 @@ rt-link
rt-route
tc
tc-filter-add
+wireguard
diff --git a/tools/net/ynl/tests/wireguard.c b/tools/net/ynl/tests/wireguard.c
new file mode 100644
index 000000000000..df601e742c28
--- /dev/null
+++ b/tools/net/ynl/tests/wireguard.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <arpa/inet.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ynl.h>
+
+#include "wireguard-user.h"
+
+static void print_allowed_ip(const struct wireguard_wgallowedip *aip)
+{
+ char addr_out[INET6_ADDRSTRLEN];
+
+ if (!inet_ntop(aip->family, aip->ipaddr, addr_out, sizeof(addr_out))) {
+ addr_out[0] = '?';
+ addr_out[1] = '\0';
+ }
+ printf("\t\t\t%s/%u\n", addr_out, aip->cidr_mask);
+}
+
+/* Only printing public key in this demo. For better key formatting,
+ * use the constant-time implementation as found in wireguard-tools.
+ */
+static void print_peer_header(const struct wireguard_wgpeer *peer)
+{
+ unsigned int len = peer->_len.public_key;
+ uint8_t *key = peer->public_key;
+ unsigned int i;
+
+ if (len != 32)
+ return;
+ printf("\tPeer ");
+ for (i = 0; i < len; i++)
+ printf("%02x", key[i]);
+ printf(":\n");
+}
+
+static void print_peer(const struct wireguard_wgpeer *peer)
+{
+ unsigned int i;
+
+ print_peer_header(peer);
+ printf("\t\tData: rx: %llu / tx: %llu bytes\n",
+ peer->rx_bytes, peer->tx_bytes);
+ printf("\t\tAllowed IPs:\n");
+ for (i = 0; i < peer->_count.allowedips; i++)
+ print_allowed_ip(&peer->allowedips[i]);
+}
+
+static void build_request(struct wireguard_get_device_req *req, char *arg)
+{
+ char *endptr;
+ int ifindex;
+
+ ifindex = strtol(arg, &endptr, 0);
+ if (endptr != arg + strlen(arg) || errno != 0)
+ ifindex = 0;
+ if (ifindex > 0)
+ wireguard_get_device_req_set_ifindex(req, ifindex);
+ else
+ wireguard_get_device_req_set_ifname(req, arg);
+}
+
+int main(int argc, char **argv)
+{
+ struct wireguard_get_device_list *devs;
+ struct wireguard_get_device_req *req;
+ struct ynl_error yerr;
+ struct ynl_sock *ys;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s <ifindex|ifname>\n", argv[0]);
+ return 1;
+ }
+
+ ys = ynl_sock_create(&ynl_wireguard_family, &yerr);
+ if (!ys) {
+ fprintf(stderr, "YNL: %s\n", yerr.msg);
+ return 2;
+ }
+
+ req = wireguard_get_device_req_alloc();
+ build_request(req, argv[1]);
+
+ devs = wireguard_get_device_dump(ys, req);
+ if (!devs) {
+ fprintf(stderr, "YNL (%d): %s\n", ys->err.code, ys->err.msg);
+ wireguard_get_device_req_free(req);
+ ynl_sock_destroy(ys);
+ return 3;
+ }
+
+ ynl_dump_foreach(devs, d) {
+ unsigned int i;
+
+ printf("Interface %d: %s\n", d->ifindex, d->ifname);
+ for (i = 0; i < d->_count.peers; i++)
+ print_peer(&d->peers[i]);
+ }
+
+ wireguard_get_device_list_free(devs);
+ wireguard_get_device_req_free(req);
+ ynl_sock_destroy(ys);
+
+ return 0;
+}