xref: /openbmc/linux/drivers/net/wireguard/netlink.c (revision e7096c131e5161fa3b8e52a650d7719d2857adfd)
1*e7096c13SJason A. Donenfeld // SPDX-License-Identifier: GPL-2.0
2*e7096c13SJason A. Donenfeld /*
3*e7096c13SJason A. Donenfeld  * Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4*e7096c13SJason A. Donenfeld  */
5*e7096c13SJason A. Donenfeld 
6*e7096c13SJason A. Donenfeld #include "netlink.h"
7*e7096c13SJason A. Donenfeld #include "device.h"
8*e7096c13SJason A. Donenfeld #include "peer.h"
9*e7096c13SJason A. Donenfeld #include "socket.h"
10*e7096c13SJason A. Donenfeld #include "queueing.h"
11*e7096c13SJason A. Donenfeld #include "messages.h"
12*e7096c13SJason A. Donenfeld 
13*e7096c13SJason A. Donenfeld #include <uapi/linux/wireguard.h>
14*e7096c13SJason A. Donenfeld 
15*e7096c13SJason A. Donenfeld #include <linux/if.h>
16*e7096c13SJason A. Donenfeld #include <net/genetlink.h>
17*e7096c13SJason A. Donenfeld #include <net/sock.h>
18*e7096c13SJason A. Donenfeld #include <crypto/algapi.h>
19*e7096c13SJason A. Donenfeld 
20*e7096c13SJason A. Donenfeld static struct genl_family genl_family;
21*e7096c13SJason A. Donenfeld 
22*e7096c13SJason A. Donenfeld static const struct nla_policy device_policy[WGDEVICE_A_MAX + 1] = {
23*e7096c13SJason A. Donenfeld 	[WGDEVICE_A_IFINDEX]		= { .type = NLA_U32 },
24*e7096c13SJason A. Donenfeld 	[WGDEVICE_A_IFNAME]		= { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
25*e7096c13SJason A. Donenfeld 	[WGDEVICE_A_PRIVATE_KEY]	= { .type = NLA_EXACT_LEN, .len = NOISE_PUBLIC_KEY_LEN },
26*e7096c13SJason A. Donenfeld 	[WGDEVICE_A_PUBLIC_KEY]		= { .type = NLA_EXACT_LEN, .len = NOISE_PUBLIC_KEY_LEN },
27*e7096c13SJason A. Donenfeld 	[WGDEVICE_A_FLAGS]		= { .type = NLA_U32 },
28*e7096c13SJason A. Donenfeld 	[WGDEVICE_A_LISTEN_PORT]	= { .type = NLA_U16 },
29*e7096c13SJason A. Donenfeld 	[WGDEVICE_A_FWMARK]		= { .type = NLA_U32 },
30*e7096c13SJason A. Donenfeld 	[WGDEVICE_A_PEERS]		= { .type = NLA_NESTED }
31*e7096c13SJason A. Donenfeld };
32*e7096c13SJason A. Donenfeld 
33*e7096c13SJason A. Donenfeld static const struct nla_policy peer_policy[WGPEER_A_MAX + 1] = {
34*e7096c13SJason A. Donenfeld 	[WGPEER_A_PUBLIC_KEY]				= { .type = NLA_EXACT_LEN, .len = NOISE_PUBLIC_KEY_LEN },
35*e7096c13SJason A. Donenfeld 	[WGPEER_A_PRESHARED_KEY]			= { .type = NLA_EXACT_LEN, .len = NOISE_SYMMETRIC_KEY_LEN },
36*e7096c13SJason A. Donenfeld 	[WGPEER_A_FLAGS]				= { .type = NLA_U32 },
37*e7096c13SJason A. Donenfeld 	[WGPEER_A_ENDPOINT]				= { .type = NLA_MIN_LEN, .len = sizeof(struct sockaddr) },
38*e7096c13SJason A. Donenfeld 	[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]	= { .type = NLA_U16 },
39*e7096c13SJason A. Donenfeld 	[WGPEER_A_LAST_HANDSHAKE_TIME]			= { .type = NLA_EXACT_LEN, .len = sizeof(struct __kernel_timespec) },
40*e7096c13SJason A. Donenfeld 	[WGPEER_A_RX_BYTES]				= { .type = NLA_U64 },
41*e7096c13SJason A. Donenfeld 	[WGPEER_A_TX_BYTES]				= { .type = NLA_U64 },
42*e7096c13SJason A. Donenfeld 	[WGPEER_A_ALLOWEDIPS]				= { .type = NLA_NESTED },
43*e7096c13SJason A. Donenfeld 	[WGPEER_A_PROTOCOL_VERSION]			= { .type = NLA_U32 }
44*e7096c13SJason A. Donenfeld };
45*e7096c13SJason A. Donenfeld 
46*e7096c13SJason A. Donenfeld static const struct nla_policy allowedip_policy[WGALLOWEDIP_A_MAX + 1] = {
47*e7096c13SJason A. Donenfeld 	[WGALLOWEDIP_A_FAMILY]		= { .type = NLA_U16 },
48*e7096c13SJason A. Donenfeld 	[WGALLOWEDIP_A_IPADDR]		= { .type = NLA_MIN_LEN, .len = sizeof(struct in_addr) },
49*e7096c13SJason A. Donenfeld 	[WGALLOWEDIP_A_CIDR_MASK]	= { .type = NLA_U8 }
50*e7096c13SJason A. Donenfeld };
51*e7096c13SJason A. Donenfeld 
52*e7096c13SJason A. Donenfeld static struct wg_device *lookup_interface(struct nlattr **attrs,
53*e7096c13SJason A. Donenfeld 					  struct sk_buff *skb)
54*e7096c13SJason A. Donenfeld {
55*e7096c13SJason A. Donenfeld 	struct net_device *dev = NULL;
56*e7096c13SJason A. Donenfeld 
57*e7096c13SJason A. Donenfeld 	if (!attrs[WGDEVICE_A_IFINDEX] == !attrs[WGDEVICE_A_IFNAME])
58*e7096c13SJason A. Donenfeld 		return ERR_PTR(-EBADR);
59*e7096c13SJason A. Donenfeld 	if (attrs[WGDEVICE_A_IFINDEX])
60*e7096c13SJason A. Donenfeld 		dev = dev_get_by_index(sock_net(skb->sk),
61*e7096c13SJason A. Donenfeld 				       nla_get_u32(attrs[WGDEVICE_A_IFINDEX]));
62*e7096c13SJason A. Donenfeld 	else if (attrs[WGDEVICE_A_IFNAME])
63*e7096c13SJason A. Donenfeld 		dev = dev_get_by_name(sock_net(skb->sk),
64*e7096c13SJason A. Donenfeld 				      nla_data(attrs[WGDEVICE_A_IFNAME]));
65*e7096c13SJason A. Donenfeld 	if (!dev)
66*e7096c13SJason A. Donenfeld 		return ERR_PTR(-ENODEV);
67*e7096c13SJason A. Donenfeld 	if (!dev->rtnl_link_ops || !dev->rtnl_link_ops->kind ||
68*e7096c13SJason A. Donenfeld 	    strcmp(dev->rtnl_link_ops->kind, KBUILD_MODNAME)) {
69*e7096c13SJason A. Donenfeld 		dev_put(dev);
70*e7096c13SJason A. Donenfeld 		return ERR_PTR(-EOPNOTSUPP);
71*e7096c13SJason A. Donenfeld 	}
72*e7096c13SJason A. Donenfeld 	return netdev_priv(dev);
73*e7096c13SJason A. Donenfeld }
74*e7096c13SJason A. Donenfeld 
75*e7096c13SJason A. Donenfeld static int get_allowedips(struct sk_buff *skb, const u8 *ip, u8 cidr,
76*e7096c13SJason A. Donenfeld 			  int family)
77*e7096c13SJason A. Donenfeld {
78*e7096c13SJason A. Donenfeld 	struct nlattr *allowedip_nest;
79*e7096c13SJason A. Donenfeld 
80*e7096c13SJason A. Donenfeld 	allowedip_nest = nla_nest_start(skb, 0);
81*e7096c13SJason A. Donenfeld 	if (!allowedip_nest)
82*e7096c13SJason A. Donenfeld 		return -EMSGSIZE;
83*e7096c13SJason A. Donenfeld 
84*e7096c13SJason A. Donenfeld 	if (nla_put_u8(skb, WGALLOWEDIP_A_CIDR_MASK, cidr) ||
85*e7096c13SJason A. Donenfeld 	    nla_put_u16(skb, WGALLOWEDIP_A_FAMILY, family) ||
86*e7096c13SJason A. Donenfeld 	    nla_put(skb, WGALLOWEDIP_A_IPADDR, family == AF_INET6 ?
87*e7096c13SJason A. Donenfeld 		    sizeof(struct in6_addr) : sizeof(struct in_addr), ip)) {
88*e7096c13SJason A. Donenfeld 		nla_nest_cancel(skb, allowedip_nest);
89*e7096c13SJason A. Donenfeld 		return -EMSGSIZE;
90*e7096c13SJason A. Donenfeld 	}
91*e7096c13SJason A. Donenfeld 
92*e7096c13SJason A. Donenfeld 	nla_nest_end(skb, allowedip_nest);
93*e7096c13SJason A. Donenfeld 	return 0;
94*e7096c13SJason A. Donenfeld }
95*e7096c13SJason A. Donenfeld 
96*e7096c13SJason A. Donenfeld struct dump_ctx {
97*e7096c13SJason A. Donenfeld 	struct wg_device *wg;
98*e7096c13SJason A. Donenfeld 	struct wg_peer *next_peer;
99*e7096c13SJason A. Donenfeld 	u64 allowedips_seq;
100*e7096c13SJason A. Donenfeld 	struct allowedips_node *next_allowedip;
101*e7096c13SJason A. Donenfeld };
102*e7096c13SJason A. Donenfeld 
103*e7096c13SJason A. Donenfeld #define DUMP_CTX(cb) ((struct dump_ctx *)(cb)->args)
104*e7096c13SJason A. Donenfeld 
105*e7096c13SJason A. Donenfeld static int
106*e7096c13SJason A. Donenfeld get_peer(struct wg_peer *peer, struct sk_buff *skb, struct dump_ctx *ctx)
107*e7096c13SJason A. Donenfeld {
108*e7096c13SJason A. Donenfeld 
109*e7096c13SJason A. Donenfeld 	struct nlattr *allowedips_nest, *peer_nest = nla_nest_start(skb, 0);
110*e7096c13SJason A. Donenfeld 	struct allowedips_node *allowedips_node = ctx->next_allowedip;
111*e7096c13SJason A. Donenfeld 	bool fail;
112*e7096c13SJason A. Donenfeld 
113*e7096c13SJason A. Donenfeld 	if (!peer_nest)
114*e7096c13SJason A. Donenfeld 		return -EMSGSIZE;
115*e7096c13SJason A. Donenfeld 
116*e7096c13SJason A. Donenfeld 	down_read(&peer->handshake.lock);
117*e7096c13SJason A. Donenfeld 	fail = nla_put(skb, WGPEER_A_PUBLIC_KEY, NOISE_PUBLIC_KEY_LEN,
118*e7096c13SJason A. Donenfeld 		       peer->handshake.remote_static);
119*e7096c13SJason A. Donenfeld 	up_read(&peer->handshake.lock);
120*e7096c13SJason A. Donenfeld 	if (fail)
121*e7096c13SJason A. Donenfeld 		goto err;
122*e7096c13SJason A. Donenfeld 
123*e7096c13SJason A. Donenfeld 	if (!allowedips_node) {
124*e7096c13SJason A. Donenfeld 		const struct __kernel_timespec last_handshake = {
125*e7096c13SJason A. Donenfeld 			.tv_sec = peer->walltime_last_handshake.tv_sec,
126*e7096c13SJason A. Donenfeld 			.tv_nsec = peer->walltime_last_handshake.tv_nsec
127*e7096c13SJason A. Donenfeld 		};
128*e7096c13SJason A. Donenfeld 
129*e7096c13SJason A. Donenfeld 		down_read(&peer->handshake.lock);
130*e7096c13SJason A. Donenfeld 		fail = nla_put(skb, WGPEER_A_PRESHARED_KEY,
131*e7096c13SJason A. Donenfeld 			       NOISE_SYMMETRIC_KEY_LEN,
132*e7096c13SJason A. Donenfeld 			       peer->handshake.preshared_key);
133*e7096c13SJason A. Donenfeld 		up_read(&peer->handshake.lock);
134*e7096c13SJason A. Donenfeld 		if (fail)
135*e7096c13SJason A. Donenfeld 			goto err;
136*e7096c13SJason A. Donenfeld 
137*e7096c13SJason A. Donenfeld 		if (nla_put(skb, WGPEER_A_LAST_HANDSHAKE_TIME,
138*e7096c13SJason A. Donenfeld 			    sizeof(last_handshake), &last_handshake) ||
139*e7096c13SJason A. Donenfeld 		    nla_put_u16(skb, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,
140*e7096c13SJason A. Donenfeld 				peer->persistent_keepalive_interval) ||
141*e7096c13SJason A. Donenfeld 		    nla_put_u64_64bit(skb, WGPEER_A_TX_BYTES, peer->tx_bytes,
142*e7096c13SJason A. Donenfeld 				      WGPEER_A_UNSPEC) ||
143*e7096c13SJason A. Donenfeld 		    nla_put_u64_64bit(skb, WGPEER_A_RX_BYTES, peer->rx_bytes,
144*e7096c13SJason A. Donenfeld 				      WGPEER_A_UNSPEC) ||
145*e7096c13SJason A. Donenfeld 		    nla_put_u32(skb, WGPEER_A_PROTOCOL_VERSION, 1))
146*e7096c13SJason A. Donenfeld 			goto err;
147*e7096c13SJason A. Donenfeld 
148*e7096c13SJason A. Donenfeld 		read_lock_bh(&peer->endpoint_lock);
149*e7096c13SJason A. Donenfeld 		if (peer->endpoint.addr.sa_family == AF_INET)
150*e7096c13SJason A. Donenfeld 			fail = nla_put(skb, WGPEER_A_ENDPOINT,
151*e7096c13SJason A. Donenfeld 				       sizeof(peer->endpoint.addr4),
152*e7096c13SJason A. Donenfeld 				       &peer->endpoint.addr4);
153*e7096c13SJason A. Donenfeld 		else if (peer->endpoint.addr.sa_family == AF_INET6)
154*e7096c13SJason A. Donenfeld 			fail = nla_put(skb, WGPEER_A_ENDPOINT,
155*e7096c13SJason A. Donenfeld 				       sizeof(peer->endpoint.addr6),
156*e7096c13SJason A. Donenfeld 				       &peer->endpoint.addr6);
157*e7096c13SJason A. Donenfeld 		read_unlock_bh(&peer->endpoint_lock);
158*e7096c13SJason A. Donenfeld 		if (fail)
159*e7096c13SJason A. Donenfeld 			goto err;
160*e7096c13SJason A. Donenfeld 		allowedips_node =
161*e7096c13SJason A. Donenfeld 			list_first_entry_or_null(&peer->allowedips_list,
162*e7096c13SJason A. Donenfeld 					struct allowedips_node, peer_list);
163*e7096c13SJason A. Donenfeld 	}
164*e7096c13SJason A. Donenfeld 	if (!allowedips_node)
165*e7096c13SJason A. Donenfeld 		goto no_allowedips;
166*e7096c13SJason A. Donenfeld 	if (!ctx->allowedips_seq)
167*e7096c13SJason A. Donenfeld 		ctx->allowedips_seq = peer->device->peer_allowedips.seq;
168*e7096c13SJason A. Donenfeld 	else if (ctx->allowedips_seq != peer->device->peer_allowedips.seq)
169*e7096c13SJason A. Donenfeld 		goto no_allowedips;
170*e7096c13SJason A. Donenfeld 
171*e7096c13SJason A. Donenfeld 	allowedips_nest = nla_nest_start(skb, WGPEER_A_ALLOWEDIPS);
172*e7096c13SJason A. Donenfeld 	if (!allowedips_nest)
173*e7096c13SJason A. Donenfeld 		goto err;
174*e7096c13SJason A. Donenfeld 
175*e7096c13SJason A. Donenfeld 	list_for_each_entry_from(allowedips_node, &peer->allowedips_list,
176*e7096c13SJason A. Donenfeld 				 peer_list) {
177*e7096c13SJason A. Donenfeld 		u8 cidr, ip[16] __aligned(__alignof(u64));
178*e7096c13SJason A. Donenfeld 		int family;
179*e7096c13SJason A. Donenfeld 
180*e7096c13SJason A. Donenfeld 		family = wg_allowedips_read_node(allowedips_node, ip, &cidr);
181*e7096c13SJason A. Donenfeld 		if (get_allowedips(skb, ip, cidr, family)) {
182*e7096c13SJason A. Donenfeld 			nla_nest_end(skb, allowedips_nest);
183*e7096c13SJason A. Donenfeld 			nla_nest_end(skb, peer_nest);
184*e7096c13SJason A. Donenfeld 			ctx->next_allowedip = allowedips_node;
185*e7096c13SJason A. Donenfeld 			return -EMSGSIZE;
186*e7096c13SJason A. Donenfeld 		}
187*e7096c13SJason A. Donenfeld 	}
188*e7096c13SJason A. Donenfeld 	nla_nest_end(skb, allowedips_nest);
189*e7096c13SJason A. Donenfeld no_allowedips:
190*e7096c13SJason A. Donenfeld 	nla_nest_end(skb, peer_nest);
191*e7096c13SJason A. Donenfeld 	ctx->next_allowedip = NULL;
192*e7096c13SJason A. Donenfeld 	ctx->allowedips_seq = 0;
193*e7096c13SJason A. Donenfeld 	return 0;
194*e7096c13SJason A. Donenfeld err:
195*e7096c13SJason A. Donenfeld 	nla_nest_cancel(skb, peer_nest);
196*e7096c13SJason A. Donenfeld 	return -EMSGSIZE;
197*e7096c13SJason A. Donenfeld }
198*e7096c13SJason A. Donenfeld 
199*e7096c13SJason A. Donenfeld static int wg_get_device_start(struct netlink_callback *cb)
200*e7096c13SJason A. Donenfeld {
201*e7096c13SJason A. Donenfeld 	struct wg_device *wg;
202*e7096c13SJason A. Donenfeld 
203*e7096c13SJason A. Donenfeld 	wg = lookup_interface(genl_dumpit_info(cb)->attrs, cb->skb);
204*e7096c13SJason A. Donenfeld 	if (IS_ERR(wg))
205*e7096c13SJason A. Donenfeld 		return PTR_ERR(wg);
206*e7096c13SJason A. Donenfeld 	DUMP_CTX(cb)->wg = wg;
207*e7096c13SJason A. Donenfeld 	return 0;
208*e7096c13SJason A. Donenfeld }
209*e7096c13SJason A. Donenfeld 
210*e7096c13SJason A. Donenfeld static int wg_get_device_dump(struct sk_buff *skb, struct netlink_callback *cb)
211*e7096c13SJason A. Donenfeld {
212*e7096c13SJason A. Donenfeld 	struct wg_peer *peer, *next_peer_cursor;
213*e7096c13SJason A. Donenfeld 	struct dump_ctx *ctx = DUMP_CTX(cb);
214*e7096c13SJason A. Donenfeld 	struct wg_device *wg = ctx->wg;
215*e7096c13SJason A. Donenfeld 	struct nlattr *peers_nest;
216*e7096c13SJason A. Donenfeld 	int ret = -EMSGSIZE;
217*e7096c13SJason A. Donenfeld 	bool done = true;
218*e7096c13SJason A. Donenfeld 	void *hdr;
219*e7096c13SJason A. Donenfeld 
220*e7096c13SJason A. Donenfeld 	rtnl_lock();
221*e7096c13SJason A. Donenfeld 	mutex_lock(&wg->device_update_lock);
222*e7096c13SJason A. Donenfeld 	cb->seq = wg->device_update_gen;
223*e7096c13SJason A. Donenfeld 	next_peer_cursor = ctx->next_peer;
224*e7096c13SJason A. Donenfeld 
225*e7096c13SJason A. Donenfeld 	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
226*e7096c13SJason A. Donenfeld 			  &genl_family, NLM_F_MULTI, WG_CMD_GET_DEVICE);
227*e7096c13SJason A. Donenfeld 	if (!hdr)
228*e7096c13SJason A. Donenfeld 		goto out;
229*e7096c13SJason A. Donenfeld 	genl_dump_check_consistent(cb, hdr);
230*e7096c13SJason A. Donenfeld 
231*e7096c13SJason A. Donenfeld 	if (!ctx->next_peer) {
232*e7096c13SJason A. Donenfeld 		if (nla_put_u16(skb, WGDEVICE_A_LISTEN_PORT,
233*e7096c13SJason A. Donenfeld 				wg->incoming_port) ||
234*e7096c13SJason A. Donenfeld 		    nla_put_u32(skb, WGDEVICE_A_FWMARK, wg->fwmark) ||
235*e7096c13SJason A. Donenfeld 		    nla_put_u32(skb, WGDEVICE_A_IFINDEX, wg->dev->ifindex) ||
236*e7096c13SJason A. Donenfeld 		    nla_put_string(skb, WGDEVICE_A_IFNAME, wg->dev->name))
237*e7096c13SJason A. Donenfeld 			goto out;
238*e7096c13SJason A. Donenfeld 
239*e7096c13SJason A. Donenfeld 		down_read(&wg->static_identity.lock);
240*e7096c13SJason A. Donenfeld 		if (wg->static_identity.has_identity) {
241*e7096c13SJason A. Donenfeld 			if (nla_put(skb, WGDEVICE_A_PRIVATE_KEY,
242*e7096c13SJason A. Donenfeld 				    NOISE_PUBLIC_KEY_LEN,
243*e7096c13SJason A. Donenfeld 				    wg->static_identity.static_private) ||
244*e7096c13SJason A. Donenfeld 			    nla_put(skb, WGDEVICE_A_PUBLIC_KEY,
245*e7096c13SJason A. Donenfeld 				    NOISE_PUBLIC_KEY_LEN,
246*e7096c13SJason A. Donenfeld 				    wg->static_identity.static_public)) {
247*e7096c13SJason A. Donenfeld 				up_read(&wg->static_identity.lock);
248*e7096c13SJason A. Donenfeld 				goto out;
249*e7096c13SJason A. Donenfeld 			}
250*e7096c13SJason A. Donenfeld 		}
251*e7096c13SJason A. Donenfeld 		up_read(&wg->static_identity.lock);
252*e7096c13SJason A. Donenfeld 	}
253*e7096c13SJason A. Donenfeld 
254*e7096c13SJason A. Donenfeld 	peers_nest = nla_nest_start(skb, WGDEVICE_A_PEERS);
255*e7096c13SJason A. Donenfeld 	if (!peers_nest)
256*e7096c13SJason A. Donenfeld 		goto out;
257*e7096c13SJason A. Donenfeld 	ret = 0;
258*e7096c13SJason A. Donenfeld 	/* If the last cursor was removed via list_del_init in peer_remove, then
259*e7096c13SJason A. Donenfeld 	 * we just treat this the same as there being no more peers left. The
260*e7096c13SJason A. Donenfeld 	 * reason is that seq_nr should indicate to userspace that this isn't a
261*e7096c13SJason A. Donenfeld 	 * coherent dump anyway, so they'll try again.
262*e7096c13SJason A. Donenfeld 	 */
263*e7096c13SJason A. Donenfeld 	if (list_empty(&wg->peer_list) ||
264*e7096c13SJason A. Donenfeld 	    (ctx->next_peer && list_empty(&ctx->next_peer->peer_list))) {
265*e7096c13SJason A. Donenfeld 		nla_nest_cancel(skb, peers_nest);
266*e7096c13SJason A. Donenfeld 		goto out;
267*e7096c13SJason A. Donenfeld 	}
268*e7096c13SJason A. Donenfeld 	lockdep_assert_held(&wg->device_update_lock);
269*e7096c13SJason A. Donenfeld 	peer = list_prepare_entry(ctx->next_peer, &wg->peer_list, peer_list);
270*e7096c13SJason A. Donenfeld 	list_for_each_entry_continue(peer, &wg->peer_list, peer_list) {
271*e7096c13SJason A. Donenfeld 		if (get_peer(peer, skb, ctx)) {
272*e7096c13SJason A. Donenfeld 			done = false;
273*e7096c13SJason A. Donenfeld 			break;
274*e7096c13SJason A. Donenfeld 		}
275*e7096c13SJason A. Donenfeld 		next_peer_cursor = peer;
276*e7096c13SJason A. Donenfeld 	}
277*e7096c13SJason A. Donenfeld 	nla_nest_end(skb, peers_nest);
278*e7096c13SJason A. Donenfeld 
279*e7096c13SJason A. Donenfeld out:
280*e7096c13SJason A. Donenfeld 	if (!ret && !done && next_peer_cursor)
281*e7096c13SJason A. Donenfeld 		wg_peer_get(next_peer_cursor);
282*e7096c13SJason A. Donenfeld 	wg_peer_put(ctx->next_peer);
283*e7096c13SJason A. Donenfeld 	mutex_unlock(&wg->device_update_lock);
284*e7096c13SJason A. Donenfeld 	rtnl_unlock();
285*e7096c13SJason A. Donenfeld 
286*e7096c13SJason A. Donenfeld 	if (ret) {
287*e7096c13SJason A. Donenfeld 		genlmsg_cancel(skb, hdr);
288*e7096c13SJason A. Donenfeld 		return ret;
289*e7096c13SJason A. Donenfeld 	}
290*e7096c13SJason A. Donenfeld 	genlmsg_end(skb, hdr);
291*e7096c13SJason A. Donenfeld 	if (done) {
292*e7096c13SJason A. Donenfeld 		ctx->next_peer = NULL;
293*e7096c13SJason A. Donenfeld 		return 0;
294*e7096c13SJason A. Donenfeld 	}
295*e7096c13SJason A. Donenfeld 	ctx->next_peer = next_peer_cursor;
296*e7096c13SJason A. Donenfeld 	return skb->len;
297*e7096c13SJason A. Donenfeld 
298*e7096c13SJason A. Donenfeld 	/* At this point, we can't really deal ourselves with safely zeroing out
299*e7096c13SJason A. Donenfeld 	 * the private key material after usage. This will need an additional API
300*e7096c13SJason A. Donenfeld 	 * in the kernel for marking skbs as zero_on_free.
301*e7096c13SJason A. Donenfeld 	 */
302*e7096c13SJason A. Donenfeld }
303*e7096c13SJason A. Donenfeld 
304*e7096c13SJason A. Donenfeld static int wg_get_device_done(struct netlink_callback *cb)
305*e7096c13SJason A. Donenfeld {
306*e7096c13SJason A. Donenfeld 	struct dump_ctx *ctx = DUMP_CTX(cb);
307*e7096c13SJason A. Donenfeld 
308*e7096c13SJason A. Donenfeld 	if (ctx->wg)
309*e7096c13SJason A. Donenfeld 		dev_put(ctx->wg->dev);
310*e7096c13SJason A. Donenfeld 	wg_peer_put(ctx->next_peer);
311*e7096c13SJason A. Donenfeld 	return 0;
312*e7096c13SJason A. Donenfeld }
313*e7096c13SJason A. Donenfeld 
314*e7096c13SJason A. Donenfeld static int set_port(struct wg_device *wg, u16 port)
315*e7096c13SJason A. Donenfeld {
316*e7096c13SJason A. Donenfeld 	struct wg_peer *peer;
317*e7096c13SJason A. Donenfeld 
318*e7096c13SJason A. Donenfeld 	if (wg->incoming_port == port)
319*e7096c13SJason A. Donenfeld 		return 0;
320*e7096c13SJason A. Donenfeld 	list_for_each_entry(peer, &wg->peer_list, peer_list)
321*e7096c13SJason A. Donenfeld 		wg_socket_clear_peer_endpoint_src(peer);
322*e7096c13SJason A. Donenfeld 	if (!netif_running(wg->dev)) {
323*e7096c13SJason A. Donenfeld 		wg->incoming_port = port;
324*e7096c13SJason A. Donenfeld 		return 0;
325*e7096c13SJason A. Donenfeld 	}
326*e7096c13SJason A. Donenfeld 	return wg_socket_init(wg, port);
327*e7096c13SJason A. Donenfeld }
328*e7096c13SJason A. Donenfeld 
329*e7096c13SJason A. Donenfeld static int set_allowedip(struct wg_peer *peer, struct nlattr **attrs)
330*e7096c13SJason A. Donenfeld {
331*e7096c13SJason A. Donenfeld 	int ret = -EINVAL;
332*e7096c13SJason A. Donenfeld 	u16 family;
333*e7096c13SJason A. Donenfeld 	u8 cidr;
334*e7096c13SJason A. Donenfeld 
335*e7096c13SJason A. Donenfeld 	if (!attrs[WGALLOWEDIP_A_FAMILY] || !attrs[WGALLOWEDIP_A_IPADDR] ||
336*e7096c13SJason A. Donenfeld 	    !attrs[WGALLOWEDIP_A_CIDR_MASK])
337*e7096c13SJason A. Donenfeld 		return ret;
338*e7096c13SJason A. Donenfeld 	family = nla_get_u16(attrs[WGALLOWEDIP_A_FAMILY]);
339*e7096c13SJason A. Donenfeld 	cidr = nla_get_u8(attrs[WGALLOWEDIP_A_CIDR_MASK]);
340*e7096c13SJason A. Donenfeld 
341*e7096c13SJason A. Donenfeld 	if (family == AF_INET && cidr <= 32 &&
342*e7096c13SJason A. Donenfeld 	    nla_len(attrs[WGALLOWEDIP_A_IPADDR]) == sizeof(struct in_addr))
343*e7096c13SJason A. Donenfeld 		ret = wg_allowedips_insert_v4(
344*e7096c13SJason A. Donenfeld 			&peer->device->peer_allowedips,
345*e7096c13SJason A. Donenfeld 			nla_data(attrs[WGALLOWEDIP_A_IPADDR]), cidr, peer,
346*e7096c13SJason A. Donenfeld 			&peer->device->device_update_lock);
347*e7096c13SJason A. Donenfeld 	else if (family == AF_INET6 && cidr <= 128 &&
348*e7096c13SJason A. Donenfeld 		 nla_len(attrs[WGALLOWEDIP_A_IPADDR]) == sizeof(struct in6_addr))
349*e7096c13SJason A. Donenfeld 		ret = wg_allowedips_insert_v6(
350*e7096c13SJason A. Donenfeld 			&peer->device->peer_allowedips,
351*e7096c13SJason A. Donenfeld 			nla_data(attrs[WGALLOWEDIP_A_IPADDR]), cidr, peer,
352*e7096c13SJason A. Donenfeld 			&peer->device->device_update_lock);
353*e7096c13SJason A. Donenfeld 
354*e7096c13SJason A. Donenfeld 	return ret;
355*e7096c13SJason A. Donenfeld }
356*e7096c13SJason A. Donenfeld 
357*e7096c13SJason A. Donenfeld static int set_peer(struct wg_device *wg, struct nlattr **attrs)
358*e7096c13SJason A. Donenfeld {
359*e7096c13SJason A. Donenfeld 	u8 *public_key = NULL, *preshared_key = NULL;
360*e7096c13SJason A. Donenfeld 	struct wg_peer *peer = NULL;
361*e7096c13SJason A. Donenfeld 	u32 flags = 0;
362*e7096c13SJason A. Donenfeld 	int ret;
363*e7096c13SJason A. Donenfeld 
364*e7096c13SJason A. Donenfeld 	ret = -EINVAL;
365*e7096c13SJason A. Donenfeld 	if (attrs[WGPEER_A_PUBLIC_KEY] &&
366*e7096c13SJason A. Donenfeld 	    nla_len(attrs[WGPEER_A_PUBLIC_KEY]) == NOISE_PUBLIC_KEY_LEN)
367*e7096c13SJason A. Donenfeld 		public_key = nla_data(attrs[WGPEER_A_PUBLIC_KEY]);
368*e7096c13SJason A. Donenfeld 	else
369*e7096c13SJason A. Donenfeld 		goto out;
370*e7096c13SJason A. Donenfeld 	if (attrs[WGPEER_A_PRESHARED_KEY] &&
371*e7096c13SJason A. Donenfeld 	    nla_len(attrs[WGPEER_A_PRESHARED_KEY]) == NOISE_SYMMETRIC_KEY_LEN)
372*e7096c13SJason A. Donenfeld 		preshared_key = nla_data(attrs[WGPEER_A_PRESHARED_KEY]);
373*e7096c13SJason A. Donenfeld 
374*e7096c13SJason A. Donenfeld 	if (attrs[WGPEER_A_FLAGS])
375*e7096c13SJason A. Donenfeld 		flags = nla_get_u32(attrs[WGPEER_A_FLAGS]);
376*e7096c13SJason A. Donenfeld 	ret = -EOPNOTSUPP;
377*e7096c13SJason A. Donenfeld 	if (flags & ~__WGPEER_F_ALL)
378*e7096c13SJason A. Donenfeld 		goto out;
379*e7096c13SJason A. Donenfeld 
380*e7096c13SJason A. Donenfeld 	ret = -EPFNOSUPPORT;
381*e7096c13SJason A. Donenfeld 	if (attrs[WGPEER_A_PROTOCOL_VERSION]) {
382*e7096c13SJason A. Donenfeld 		if (nla_get_u32(attrs[WGPEER_A_PROTOCOL_VERSION]) != 1)
383*e7096c13SJason A. Donenfeld 			goto out;
384*e7096c13SJason A. Donenfeld 	}
385*e7096c13SJason A. Donenfeld 
386*e7096c13SJason A. Donenfeld 	peer = wg_pubkey_hashtable_lookup(wg->peer_hashtable,
387*e7096c13SJason A. Donenfeld 					  nla_data(attrs[WGPEER_A_PUBLIC_KEY]));
388*e7096c13SJason A. Donenfeld 	ret = 0;
389*e7096c13SJason A. Donenfeld 	if (!peer) { /* Peer doesn't exist yet. Add a new one. */
390*e7096c13SJason A. Donenfeld 		if (flags & (WGPEER_F_REMOVE_ME | WGPEER_F_UPDATE_ONLY))
391*e7096c13SJason A. Donenfeld 			goto out;
392*e7096c13SJason A. Donenfeld 
393*e7096c13SJason A. Donenfeld 		/* The peer is new, so there aren't allowed IPs to remove. */
394*e7096c13SJason A. Donenfeld 		flags &= ~WGPEER_F_REPLACE_ALLOWEDIPS;
395*e7096c13SJason A. Donenfeld 
396*e7096c13SJason A. Donenfeld 		down_read(&wg->static_identity.lock);
397*e7096c13SJason A. Donenfeld 		if (wg->static_identity.has_identity &&
398*e7096c13SJason A. Donenfeld 		    !memcmp(nla_data(attrs[WGPEER_A_PUBLIC_KEY]),
399*e7096c13SJason A. Donenfeld 			    wg->static_identity.static_public,
400*e7096c13SJason A. Donenfeld 			    NOISE_PUBLIC_KEY_LEN)) {
401*e7096c13SJason A. Donenfeld 			/* We silently ignore peers that have the same public
402*e7096c13SJason A. Donenfeld 			 * key as the device. The reason we do it silently is
403*e7096c13SJason A. Donenfeld 			 * that we'd like for people to be able to reuse the
404*e7096c13SJason A. Donenfeld 			 * same set of API calls across peers.
405*e7096c13SJason A. Donenfeld 			 */
406*e7096c13SJason A. Donenfeld 			up_read(&wg->static_identity.lock);
407*e7096c13SJason A. Donenfeld 			ret = 0;
408*e7096c13SJason A. Donenfeld 			goto out;
409*e7096c13SJason A. Donenfeld 		}
410*e7096c13SJason A. Donenfeld 		up_read(&wg->static_identity.lock);
411*e7096c13SJason A. Donenfeld 
412*e7096c13SJason A. Donenfeld 		peer = wg_peer_create(wg, public_key, preshared_key);
413*e7096c13SJason A. Donenfeld 		if (IS_ERR(peer)) {
414*e7096c13SJason A. Donenfeld 			/* Similar to the above, if the key is invalid, we skip
415*e7096c13SJason A. Donenfeld 			 * it without fanfare, so that services don't need to
416*e7096c13SJason A. Donenfeld 			 * worry about doing key validation themselves.
417*e7096c13SJason A. Donenfeld 			 */
418*e7096c13SJason A. Donenfeld 			ret = PTR_ERR(peer) == -EKEYREJECTED ? 0 : PTR_ERR(peer);
419*e7096c13SJason A. Donenfeld 			peer = NULL;
420*e7096c13SJason A. Donenfeld 			goto out;
421*e7096c13SJason A. Donenfeld 		}
422*e7096c13SJason A. Donenfeld 		/* Take additional reference, as though we've just been
423*e7096c13SJason A. Donenfeld 		 * looked up.
424*e7096c13SJason A. Donenfeld 		 */
425*e7096c13SJason A. Donenfeld 		wg_peer_get(peer);
426*e7096c13SJason A. Donenfeld 	}
427*e7096c13SJason A. Donenfeld 
428*e7096c13SJason A. Donenfeld 	if (flags & WGPEER_F_REMOVE_ME) {
429*e7096c13SJason A. Donenfeld 		wg_peer_remove(peer);
430*e7096c13SJason A. Donenfeld 		goto out;
431*e7096c13SJason A. Donenfeld 	}
432*e7096c13SJason A. Donenfeld 
433*e7096c13SJason A. Donenfeld 	if (preshared_key) {
434*e7096c13SJason A. Donenfeld 		down_write(&peer->handshake.lock);
435*e7096c13SJason A. Donenfeld 		memcpy(&peer->handshake.preshared_key, preshared_key,
436*e7096c13SJason A. Donenfeld 		       NOISE_SYMMETRIC_KEY_LEN);
437*e7096c13SJason A. Donenfeld 		up_write(&peer->handshake.lock);
438*e7096c13SJason A. Donenfeld 	}
439*e7096c13SJason A. Donenfeld 
440*e7096c13SJason A. Donenfeld 	if (attrs[WGPEER_A_ENDPOINT]) {
441*e7096c13SJason A. Donenfeld 		struct sockaddr *addr = nla_data(attrs[WGPEER_A_ENDPOINT]);
442*e7096c13SJason A. Donenfeld 		size_t len = nla_len(attrs[WGPEER_A_ENDPOINT]);
443*e7096c13SJason A. Donenfeld 
444*e7096c13SJason A. Donenfeld 		if ((len == sizeof(struct sockaddr_in) &&
445*e7096c13SJason A. Donenfeld 		     addr->sa_family == AF_INET) ||
446*e7096c13SJason A. Donenfeld 		    (len == sizeof(struct sockaddr_in6) &&
447*e7096c13SJason A. Donenfeld 		     addr->sa_family == AF_INET6)) {
448*e7096c13SJason A. Donenfeld 			struct endpoint endpoint = { { { 0 } } };
449*e7096c13SJason A. Donenfeld 
450*e7096c13SJason A. Donenfeld 			memcpy(&endpoint.addr, addr, len);
451*e7096c13SJason A. Donenfeld 			wg_socket_set_peer_endpoint(peer, &endpoint);
452*e7096c13SJason A. Donenfeld 		}
453*e7096c13SJason A. Donenfeld 	}
454*e7096c13SJason A. Donenfeld 
455*e7096c13SJason A. Donenfeld 	if (flags & WGPEER_F_REPLACE_ALLOWEDIPS)
456*e7096c13SJason A. Donenfeld 		wg_allowedips_remove_by_peer(&wg->peer_allowedips, peer,
457*e7096c13SJason A. Donenfeld 					     &wg->device_update_lock);
458*e7096c13SJason A. Donenfeld 
459*e7096c13SJason A. Donenfeld 	if (attrs[WGPEER_A_ALLOWEDIPS]) {
460*e7096c13SJason A. Donenfeld 		struct nlattr *attr, *allowedip[WGALLOWEDIP_A_MAX + 1];
461*e7096c13SJason A. Donenfeld 		int rem;
462*e7096c13SJason A. Donenfeld 
463*e7096c13SJason A. Donenfeld 		nla_for_each_nested(attr, attrs[WGPEER_A_ALLOWEDIPS], rem) {
464*e7096c13SJason A. Donenfeld 			ret = nla_parse_nested(allowedip, WGALLOWEDIP_A_MAX,
465*e7096c13SJason A. Donenfeld 					       attr, allowedip_policy, NULL);
466*e7096c13SJason A. Donenfeld 			if (ret < 0)
467*e7096c13SJason A. Donenfeld 				goto out;
468*e7096c13SJason A. Donenfeld 			ret = set_allowedip(peer, allowedip);
469*e7096c13SJason A. Donenfeld 			if (ret < 0)
470*e7096c13SJason A. Donenfeld 				goto out;
471*e7096c13SJason A. Donenfeld 		}
472*e7096c13SJason A. Donenfeld 	}
473*e7096c13SJason A. Donenfeld 
474*e7096c13SJason A. Donenfeld 	if (attrs[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]) {
475*e7096c13SJason A. Donenfeld 		const u16 persistent_keepalive_interval = nla_get_u16(
476*e7096c13SJason A. Donenfeld 				attrs[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]);
477*e7096c13SJason A. Donenfeld 		const bool send_keepalive =
478*e7096c13SJason A. Donenfeld 			!peer->persistent_keepalive_interval &&
479*e7096c13SJason A. Donenfeld 			persistent_keepalive_interval &&
480*e7096c13SJason A. Donenfeld 			netif_running(wg->dev);
481*e7096c13SJason A. Donenfeld 
482*e7096c13SJason A. Donenfeld 		peer->persistent_keepalive_interval = persistent_keepalive_interval;
483*e7096c13SJason A. Donenfeld 		if (send_keepalive)
484*e7096c13SJason A. Donenfeld 			wg_packet_send_keepalive(peer);
485*e7096c13SJason A. Donenfeld 	}
486*e7096c13SJason A. Donenfeld 
487*e7096c13SJason A. Donenfeld 	if (netif_running(wg->dev))
488*e7096c13SJason A. Donenfeld 		wg_packet_send_staged_packets(peer);
489*e7096c13SJason A. Donenfeld 
490*e7096c13SJason A. Donenfeld out:
491*e7096c13SJason A. Donenfeld 	wg_peer_put(peer);
492*e7096c13SJason A. Donenfeld 	if (attrs[WGPEER_A_PRESHARED_KEY])
493*e7096c13SJason A. Donenfeld 		memzero_explicit(nla_data(attrs[WGPEER_A_PRESHARED_KEY]),
494*e7096c13SJason A. Donenfeld 				 nla_len(attrs[WGPEER_A_PRESHARED_KEY]));
495*e7096c13SJason A. Donenfeld 	return ret;
496*e7096c13SJason A. Donenfeld }
497*e7096c13SJason A. Donenfeld 
498*e7096c13SJason A. Donenfeld static int wg_set_device(struct sk_buff *skb, struct genl_info *info)
499*e7096c13SJason A. Donenfeld {
500*e7096c13SJason A. Donenfeld 	struct wg_device *wg = lookup_interface(info->attrs, skb);
501*e7096c13SJason A. Donenfeld 	u32 flags = 0;
502*e7096c13SJason A. Donenfeld 	int ret;
503*e7096c13SJason A. Donenfeld 
504*e7096c13SJason A. Donenfeld 	if (IS_ERR(wg)) {
505*e7096c13SJason A. Donenfeld 		ret = PTR_ERR(wg);
506*e7096c13SJason A. Donenfeld 		goto out_nodev;
507*e7096c13SJason A. Donenfeld 	}
508*e7096c13SJason A. Donenfeld 
509*e7096c13SJason A. Donenfeld 	rtnl_lock();
510*e7096c13SJason A. Donenfeld 	mutex_lock(&wg->device_update_lock);
511*e7096c13SJason A. Donenfeld 
512*e7096c13SJason A. Donenfeld 	if (info->attrs[WGDEVICE_A_FLAGS])
513*e7096c13SJason A. Donenfeld 		flags = nla_get_u32(info->attrs[WGDEVICE_A_FLAGS]);
514*e7096c13SJason A. Donenfeld 	ret = -EOPNOTSUPP;
515*e7096c13SJason A. Donenfeld 	if (flags & ~__WGDEVICE_F_ALL)
516*e7096c13SJason A. Donenfeld 		goto out;
517*e7096c13SJason A. Donenfeld 
518*e7096c13SJason A. Donenfeld 	ret = -EPERM;
519*e7096c13SJason A. Donenfeld 	if ((info->attrs[WGDEVICE_A_LISTEN_PORT] ||
520*e7096c13SJason A. Donenfeld 	     info->attrs[WGDEVICE_A_FWMARK]) &&
521*e7096c13SJason A. Donenfeld 	    !ns_capable(wg->creating_net->user_ns, CAP_NET_ADMIN))
522*e7096c13SJason A. Donenfeld 		goto out;
523*e7096c13SJason A. Donenfeld 
524*e7096c13SJason A. Donenfeld 	++wg->device_update_gen;
525*e7096c13SJason A. Donenfeld 
526*e7096c13SJason A. Donenfeld 	if (info->attrs[WGDEVICE_A_FWMARK]) {
527*e7096c13SJason A. Donenfeld 		struct wg_peer *peer;
528*e7096c13SJason A. Donenfeld 
529*e7096c13SJason A. Donenfeld 		wg->fwmark = nla_get_u32(info->attrs[WGDEVICE_A_FWMARK]);
530*e7096c13SJason A. Donenfeld 		list_for_each_entry(peer, &wg->peer_list, peer_list)
531*e7096c13SJason A. Donenfeld 			wg_socket_clear_peer_endpoint_src(peer);
532*e7096c13SJason A. Donenfeld 	}
533*e7096c13SJason A. Donenfeld 
534*e7096c13SJason A. Donenfeld 	if (info->attrs[WGDEVICE_A_LISTEN_PORT]) {
535*e7096c13SJason A. Donenfeld 		ret = set_port(wg,
536*e7096c13SJason A. Donenfeld 			nla_get_u16(info->attrs[WGDEVICE_A_LISTEN_PORT]));
537*e7096c13SJason A. Donenfeld 		if (ret)
538*e7096c13SJason A. Donenfeld 			goto out;
539*e7096c13SJason A. Donenfeld 	}
540*e7096c13SJason A. Donenfeld 
541*e7096c13SJason A. Donenfeld 	if (flags & WGDEVICE_F_REPLACE_PEERS)
542*e7096c13SJason A. Donenfeld 		wg_peer_remove_all(wg);
543*e7096c13SJason A. Donenfeld 
544*e7096c13SJason A. Donenfeld 	if (info->attrs[WGDEVICE_A_PRIVATE_KEY] &&
545*e7096c13SJason A. Donenfeld 	    nla_len(info->attrs[WGDEVICE_A_PRIVATE_KEY]) ==
546*e7096c13SJason A. Donenfeld 		    NOISE_PUBLIC_KEY_LEN) {
547*e7096c13SJason A. Donenfeld 		u8 *private_key = nla_data(info->attrs[WGDEVICE_A_PRIVATE_KEY]);
548*e7096c13SJason A. Donenfeld 		u8 public_key[NOISE_PUBLIC_KEY_LEN];
549*e7096c13SJason A. Donenfeld 		struct wg_peer *peer, *temp;
550*e7096c13SJason A. Donenfeld 
551*e7096c13SJason A. Donenfeld 		if (!crypto_memneq(wg->static_identity.static_private,
552*e7096c13SJason A. Donenfeld 				   private_key, NOISE_PUBLIC_KEY_LEN))
553*e7096c13SJason A. Donenfeld 			goto skip_set_private_key;
554*e7096c13SJason A. Donenfeld 
555*e7096c13SJason A. Donenfeld 		/* We remove before setting, to prevent race, which means doing
556*e7096c13SJason A. Donenfeld 		 * two 25519-genpub ops.
557*e7096c13SJason A. Donenfeld 		 */
558*e7096c13SJason A. Donenfeld 		if (curve25519_generate_public(public_key, private_key)) {
559*e7096c13SJason A. Donenfeld 			peer = wg_pubkey_hashtable_lookup(wg->peer_hashtable,
560*e7096c13SJason A. Donenfeld 							  public_key);
561*e7096c13SJason A. Donenfeld 			if (peer) {
562*e7096c13SJason A. Donenfeld 				wg_peer_put(peer);
563*e7096c13SJason A. Donenfeld 				wg_peer_remove(peer);
564*e7096c13SJason A. Donenfeld 			}
565*e7096c13SJason A. Donenfeld 		}
566*e7096c13SJason A. Donenfeld 
567*e7096c13SJason A. Donenfeld 		down_write(&wg->static_identity.lock);
568*e7096c13SJason A. Donenfeld 		wg_noise_set_static_identity_private_key(&wg->static_identity,
569*e7096c13SJason A. Donenfeld 							 private_key);
570*e7096c13SJason A. Donenfeld 		list_for_each_entry_safe(peer, temp, &wg->peer_list,
571*e7096c13SJason A. Donenfeld 					 peer_list) {
572*e7096c13SJason A. Donenfeld 			if (wg_noise_precompute_static_static(peer))
573*e7096c13SJason A. Donenfeld 				wg_noise_expire_current_peer_keypairs(peer);
574*e7096c13SJason A. Donenfeld 			else
575*e7096c13SJason A. Donenfeld 				wg_peer_remove(peer);
576*e7096c13SJason A. Donenfeld 		}
577*e7096c13SJason A. Donenfeld 		wg_cookie_checker_precompute_device_keys(&wg->cookie_checker);
578*e7096c13SJason A. Donenfeld 		up_write(&wg->static_identity.lock);
579*e7096c13SJason A. Donenfeld 	}
580*e7096c13SJason A. Donenfeld skip_set_private_key:
581*e7096c13SJason A. Donenfeld 
582*e7096c13SJason A. Donenfeld 	if (info->attrs[WGDEVICE_A_PEERS]) {
583*e7096c13SJason A. Donenfeld 		struct nlattr *attr, *peer[WGPEER_A_MAX + 1];
584*e7096c13SJason A. Donenfeld 		int rem;
585*e7096c13SJason A. Donenfeld 
586*e7096c13SJason A. Donenfeld 		nla_for_each_nested(attr, info->attrs[WGDEVICE_A_PEERS], rem) {
587*e7096c13SJason A. Donenfeld 			ret = nla_parse_nested(peer, WGPEER_A_MAX, attr,
588*e7096c13SJason A. Donenfeld 					       peer_policy, NULL);
589*e7096c13SJason A. Donenfeld 			if (ret < 0)
590*e7096c13SJason A. Donenfeld 				goto out;
591*e7096c13SJason A. Donenfeld 			ret = set_peer(wg, peer);
592*e7096c13SJason A. Donenfeld 			if (ret < 0)
593*e7096c13SJason A. Donenfeld 				goto out;
594*e7096c13SJason A. Donenfeld 		}
595*e7096c13SJason A. Donenfeld 	}
596*e7096c13SJason A. Donenfeld 	ret = 0;
597*e7096c13SJason A. Donenfeld 
598*e7096c13SJason A. Donenfeld out:
599*e7096c13SJason A. Donenfeld 	mutex_unlock(&wg->device_update_lock);
600*e7096c13SJason A. Donenfeld 	rtnl_unlock();
601*e7096c13SJason A. Donenfeld 	dev_put(wg->dev);
602*e7096c13SJason A. Donenfeld out_nodev:
603*e7096c13SJason A. Donenfeld 	if (info->attrs[WGDEVICE_A_PRIVATE_KEY])
604*e7096c13SJason A. Donenfeld 		memzero_explicit(nla_data(info->attrs[WGDEVICE_A_PRIVATE_KEY]),
605*e7096c13SJason A. Donenfeld 				 nla_len(info->attrs[WGDEVICE_A_PRIVATE_KEY]));
606*e7096c13SJason A. Donenfeld 	return ret;
607*e7096c13SJason A. Donenfeld }
608*e7096c13SJason A. Donenfeld 
609*e7096c13SJason A. Donenfeld static const struct genl_ops genl_ops[] = {
610*e7096c13SJason A. Donenfeld 	{
611*e7096c13SJason A. Donenfeld 		.cmd = WG_CMD_GET_DEVICE,
612*e7096c13SJason A. Donenfeld 		.start = wg_get_device_start,
613*e7096c13SJason A. Donenfeld 		.dumpit = wg_get_device_dump,
614*e7096c13SJason A. Donenfeld 		.done = wg_get_device_done,
615*e7096c13SJason A. Donenfeld 		.flags = GENL_UNS_ADMIN_PERM
616*e7096c13SJason A. Donenfeld 	}, {
617*e7096c13SJason A. Donenfeld 		.cmd = WG_CMD_SET_DEVICE,
618*e7096c13SJason A. Donenfeld 		.doit = wg_set_device,
619*e7096c13SJason A. Donenfeld 		.flags = GENL_UNS_ADMIN_PERM
620*e7096c13SJason A. Donenfeld 	}
621*e7096c13SJason A. Donenfeld };
622*e7096c13SJason A. Donenfeld 
623*e7096c13SJason A. Donenfeld static struct genl_family genl_family __ro_after_init = {
624*e7096c13SJason A. Donenfeld 	.ops = genl_ops,
625*e7096c13SJason A. Donenfeld 	.n_ops = ARRAY_SIZE(genl_ops),
626*e7096c13SJason A. Donenfeld 	.name = WG_GENL_NAME,
627*e7096c13SJason A. Donenfeld 	.version = WG_GENL_VERSION,
628*e7096c13SJason A. Donenfeld 	.maxattr = WGDEVICE_A_MAX,
629*e7096c13SJason A. Donenfeld 	.module = THIS_MODULE,
630*e7096c13SJason A. Donenfeld 	.policy = device_policy,
631*e7096c13SJason A. Donenfeld 	.netnsok = true
632*e7096c13SJason A. Donenfeld };
633*e7096c13SJason A. Donenfeld 
634*e7096c13SJason A. Donenfeld int __init wg_genetlink_init(void)
635*e7096c13SJason A. Donenfeld {
636*e7096c13SJason A. Donenfeld 	return genl_register_family(&genl_family);
637*e7096c13SJason A. Donenfeld }
638*e7096c13SJason A. Donenfeld 
639*e7096c13SJason A. Donenfeld void __exit wg_genetlink_uninit(void)
640*e7096c13SJason A. Donenfeld {
641*e7096c13SJason A. Donenfeld 	genl_unregister_family(&genl_family);
642*e7096c13SJason A. Donenfeld }
643