xref: /freebsd/sys/dev/wg/if_wg.c (revision 2bef0d54f74dad6962ef7d1dfa407e95cb4fb4ad)
1744bfb21SJohn Baldwin /* SPDX-License-Identifier: ISC
2744bfb21SJohn Baldwin  *
3744bfb21SJohn Baldwin  * Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4744bfb21SJohn Baldwin  * Copyright (C) 2019-2021 Matt Dunwoodie <ncon@noconroy.net>
5744bfb21SJohn Baldwin  * Copyright (c) 2019-2020 Rubicon Communications, LLC (Netgate)
6744bfb21SJohn Baldwin  * Copyright (c) 2021 Kyle Evans <kevans@FreeBSD.org>
7744bfb21SJohn Baldwin  * Copyright (c) 2022 The FreeBSD Foundation
8744bfb21SJohn Baldwin  */
9744bfb21SJohn Baldwin 
10744bfb21SJohn Baldwin #include "opt_inet.h"
11744bfb21SJohn Baldwin #include "opt_inet6.h"
12744bfb21SJohn Baldwin 
13744bfb21SJohn Baldwin #include <sys/param.h>
14744bfb21SJohn Baldwin #include <sys/systm.h>
15744bfb21SJohn Baldwin #include <sys/counter.h>
16744bfb21SJohn Baldwin #include <sys/gtaskqueue.h>
17744bfb21SJohn Baldwin #include <sys/jail.h>
18744bfb21SJohn Baldwin #include <sys/kernel.h>
19744bfb21SJohn Baldwin #include <sys/lock.h>
20744bfb21SJohn Baldwin #include <sys/mbuf.h>
21744bfb21SJohn Baldwin #include <sys/module.h>
22744bfb21SJohn Baldwin #include <sys/nv.h>
23744bfb21SJohn Baldwin #include <sys/priv.h>
24744bfb21SJohn Baldwin #include <sys/protosw.h>
25744bfb21SJohn Baldwin #include <sys/rmlock.h>
26744bfb21SJohn Baldwin #include <sys/rwlock.h>
27744bfb21SJohn Baldwin #include <sys/smp.h>
28744bfb21SJohn Baldwin #include <sys/socket.h>
29744bfb21SJohn Baldwin #include <sys/socketvar.h>
30744bfb21SJohn Baldwin #include <sys/sockio.h>
31744bfb21SJohn Baldwin #include <sys/sysctl.h>
32744bfb21SJohn Baldwin #include <sys/sx.h>
33744bfb21SJohn Baldwin #include <machine/_inttypes.h>
34744bfb21SJohn Baldwin #include <net/bpf.h>
35744bfb21SJohn Baldwin #include <net/ethernet.h>
36744bfb21SJohn Baldwin #include <net/if.h>
37744bfb21SJohn Baldwin #include <net/if_clone.h>
38744bfb21SJohn Baldwin #include <net/if_types.h>
39744bfb21SJohn Baldwin #include <net/if_var.h>
40744bfb21SJohn Baldwin #include <net/netisr.h>
41744bfb21SJohn Baldwin #include <net/radix.h>
42744bfb21SJohn Baldwin #include <netinet/in.h>
43744bfb21SJohn Baldwin #include <netinet6/in6_var.h>
44744bfb21SJohn Baldwin #include <netinet/ip.h>
45744bfb21SJohn Baldwin #include <netinet/ip6.h>
46744bfb21SJohn Baldwin #include <netinet/ip_icmp.h>
47744bfb21SJohn Baldwin #include <netinet/icmp6.h>
48744bfb21SJohn Baldwin #include <netinet/udp_var.h>
49744bfb21SJohn Baldwin #include <netinet6/nd6.h>
50744bfb21SJohn Baldwin 
51744bfb21SJohn Baldwin #include "wg_noise.h"
52744bfb21SJohn Baldwin #include "wg_cookie.h"
53744bfb21SJohn Baldwin #include "version.h"
54744bfb21SJohn Baldwin #include "if_wg.h"
55744bfb21SJohn Baldwin 
56744bfb21SJohn Baldwin #define DEFAULT_MTU		(ETHERMTU - 80)
57744bfb21SJohn Baldwin #define MAX_MTU			(IF_MAXMTU - 80)
58744bfb21SJohn Baldwin 
59744bfb21SJohn Baldwin #define MAX_STAGED_PKT		128
60744bfb21SJohn Baldwin #define MAX_QUEUED_PKT		1024
61744bfb21SJohn Baldwin #define MAX_QUEUED_PKT_MASK	(MAX_QUEUED_PKT - 1)
62744bfb21SJohn Baldwin 
63744bfb21SJohn Baldwin #define MAX_QUEUED_HANDSHAKES	4096
64744bfb21SJohn Baldwin 
65744bfb21SJohn Baldwin #define REKEY_TIMEOUT_JITTER	334 /* 1/3 sec, round for arc4random_uniform */
66744bfb21SJohn Baldwin #define MAX_TIMER_HANDSHAKES	(90 / REKEY_TIMEOUT)
67744bfb21SJohn Baldwin #define NEW_HANDSHAKE_TIMEOUT	(REKEY_TIMEOUT + KEEPALIVE_TIMEOUT)
68744bfb21SJohn Baldwin #define UNDERLOAD_TIMEOUT	1
69744bfb21SJohn Baldwin 
7087e72834SJustin Hibbits #define DPRINTF(sc, ...) if (if_getflags(sc->sc_ifp) & IFF_DEBUG) if_printf(sc->sc_ifp, ##__VA_ARGS__)
71744bfb21SJohn Baldwin 
72744bfb21SJohn Baldwin /* First byte indicating packet type on the wire */
73744bfb21SJohn Baldwin #define WG_PKT_INITIATION htole32(1)
74744bfb21SJohn Baldwin #define WG_PKT_RESPONSE htole32(2)
75744bfb21SJohn Baldwin #define WG_PKT_COOKIE htole32(3)
76744bfb21SJohn Baldwin #define WG_PKT_DATA htole32(4)
77744bfb21SJohn Baldwin 
78744bfb21SJohn Baldwin #define WG_PKT_PADDING		16
79744bfb21SJohn Baldwin #define WG_KEY_SIZE		32
80744bfb21SJohn Baldwin 
81744bfb21SJohn Baldwin struct wg_pkt_initiation {
82744bfb21SJohn Baldwin 	uint32_t		t;
83744bfb21SJohn Baldwin 	uint32_t		s_idx;
84744bfb21SJohn Baldwin 	uint8_t			ue[NOISE_PUBLIC_KEY_LEN];
85744bfb21SJohn Baldwin 	uint8_t			es[NOISE_PUBLIC_KEY_LEN + NOISE_AUTHTAG_LEN];
86744bfb21SJohn Baldwin 	uint8_t			ets[NOISE_TIMESTAMP_LEN + NOISE_AUTHTAG_LEN];
87744bfb21SJohn Baldwin 	struct cookie_macs	m;
88744bfb21SJohn Baldwin };
89744bfb21SJohn Baldwin 
90744bfb21SJohn Baldwin struct wg_pkt_response {
91744bfb21SJohn Baldwin 	uint32_t		t;
92744bfb21SJohn Baldwin 	uint32_t		s_idx;
93744bfb21SJohn Baldwin 	uint32_t		r_idx;
94744bfb21SJohn Baldwin 	uint8_t			ue[NOISE_PUBLIC_KEY_LEN];
95744bfb21SJohn Baldwin 	uint8_t			en[0 + NOISE_AUTHTAG_LEN];
96744bfb21SJohn Baldwin 	struct cookie_macs	m;
97744bfb21SJohn Baldwin };
98744bfb21SJohn Baldwin 
99744bfb21SJohn Baldwin struct wg_pkt_cookie {
100744bfb21SJohn Baldwin 	uint32_t		t;
101744bfb21SJohn Baldwin 	uint32_t		r_idx;
102744bfb21SJohn Baldwin 	uint8_t			nonce[COOKIE_NONCE_SIZE];
103744bfb21SJohn Baldwin 	uint8_t			ec[COOKIE_ENCRYPTED_SIZE];
104744bfb21SJohn Baldwin };
105744bfb21SJohn Baldwin 
106744bfb21SJohn Baldwin struct wg_pkt_data {
107744bfb21SJohn Baldwin 	uint32_t		t;
108744bfb21SJohn Baldwin 	uint32_t		r_idx;
109744bfb21SJohn Baldwin 	uint64_t		nonce;
110744bfb21SJohn Baldwin 	uint8_t			buf[];
111744bfb21SJohn Baldwin };
112744bfb21SJohn Baldwin 
113744bfb21SJohn Baldwin struct wg_endpoint {
114744bfb21SJohn Baldwin 	union {
115744bfb21SJohn Baldwin 		struct sockaddr		r_sa;
116744bfb21SJohn Baldwin 		struct sockaddr_in	r_sin;
117744bfb21SJohn Baldwin #ifdef INET6
118744bfb21SJohn Baldwin 		struct sockaddr_in6	r_sin6;
119744bfb21SJohn Baldwin #endif
120744bfb21SJohn Baldwin 	} e_remote;
121744bfb21SJohn Baldwin 	union {
122744bfb21SJohn Baldwin 		struct in_addr		l_in;
123744bfb21SJohn Baldwin #ifdef INET6
124744bfb21SJohn Baldwin 		struct in6_pktinfo	l_pktinfo6;
125744bfb21SJohn Baldwin #define l_in6 l_pktinfo6.ipi6_addr
126744bfb21SJohn Baldwin #endif
127744bfb21SJohn Baldwin 	} e_local;
128744bfb21SJohn Baldwin };
129744bfb21SJohn Baldwin 
130744bfb21SJohn Baldwin struct aip_addr {
131744bfb21SJohn Baldwin 	uint8_t		length;
132744bfb21SJohn Baldwin 	union {
133744bfb21SJohn Baldwin 		uint8_t		bytes[16];
134744bfb21SJohn Baldwin 		uint32_t	ip;
135744bfb21SJohn Baldwin 		uint32_t	ip6[4];
136744bfb21SJohn Baldwin 		struct in_addr	in;
137744bfb21SJohn Baldwin 		struct in6_addr	in6;
138744bfb21SJohn Baldwin 	};
139744bfb21SJohn Baldwin };
140744bfb21SJohn Baldwin 
141744bfb21SJohn Baldwin struct wg_aip {
142744bfb21SJohn Baldwin 	struct radix_node	 a_nodes[2];
143744bfb21SJohn Baldwin 	LIST_ENTRY(wg_aip)	 a_entry;
144744bfb21SJohn Baldwin 	struct aip_addr		 a_addr;
145744bfb21SJohn Baldwin 	struct aip_addr		 a_mask;
146744bfb21SJohn Baldwin 	struct wg_peer		*a_peer;
147744bfb21SJohn Baldwin 	sa_family_t		 a_af;
148744bfb21SJohn Baldwin };
149744bfb21SJohn Baldwin 
150744bfb21SJohn Baldwin struct wg_packet {
151744bfb21SJohn Baldwin 	STAILQ_ENTRY(wg_packet)	 p_serial;
152744bfb21SJohn Baldwin 	STAILQ_ENTRY(wg_packet)	 p_parallel;
153744bfb21SJohn Baldwin 	struct wg_endpoint	 p_endpoint;
154744bfb21SJohn Baldwin 	struct noise_keypair	*p_keypair;
155744bfb21SJohn Baldwin 	uint64_t		 p_nonce;
156744bfb21SJohn Baldwin 	struct mbuf		*p_mbuf;
157744bfb21SJohn Baldwin 	int			 p_mtu;
158744bfb21SJohn Baldwin 	sa_family_t		 p_af;
159744bfb21SJohn Baldwin 	enum wg_ring_state {
160744bfb21SJohn Baldwin 		WG_PACKET_UNCRYPTED,
161744bfb21SJohn Baldwin 		WG_PACKET_CRYPTED,
162744bfb21SJohn Baldwin 		WG_PACKET_DEAD,
163744bfb21SJohn Baldwin 	}			 p_state;
164744bfb21SJohn Baldwin };
165744bfb21SJohn Baldwin 
166744bfb21SJohn Baldwin STAILQ_HEAD(wg_packet_list, wg_packet);
167744bfb21SJohn Baldwin 
168744bfb21SJohn Baldwin struct wg_queue {
169744bfb21SJohn Baldwin 	struct mtx		 q_mtx;
170744bfb21SJohn Baldwin 	struct wg_packet_list	 q_queue;
171744bfb21SJohn Baldwin 	size_t			 q_len;
172744bfb21SJohn Baldwin };
173744bfb21SJohn Baldwin 
174744bfb21SJohn Baldwin struct wg_peer {
175744bfb21SJohn Baldwin 	TAILQ_ENTRY(wg_peer)		 p_entry;
176744bfb21SJohn Baldwin 	uint64_t			 p_id;
177744bfb21SJohn Baldwin 	struct wg_softc			*p_sc;
178744bfb21SJohn Baldwin 
179744bfb21SJohn Baldwin 	struct noise_remote		*p_remote;
180744bfb21SJohn Baldwin 	struct cookie_maker		 p_cookie;
181744bfb21SJohn Baldwin 
182744bfb21SJohn Baldwin 	struct rwlock			 p_endpoint_lock;
183744bfb21SJohn Baldwin 	struct wg_endpoint		 p_endpoint;
184744bfb21SJohn Baldwin 
185744bfb21SJohn Baldwin 	struct wg_queue	 		 p_stage_queue;
186744bfb21SJohn Baldwin 	struct wg_queue	 		 p_encrypt_serial;
187744bfb21SJohn Baldwin 	struct wg_queue	 		 p_decrypt_serial;
188744bfb21SJohn Baldwin 
189744bfb21SJohn Baldwin 	bool				 p_enabled;
190744bfb21SJohn Baldwin 	bool				 p_need_another_keepalive;
191744bfb21SJohn Baldwin 	uint16_t			 p_persistent_keepalive_interval;
192744bfb21SJohn Baldwin 	struct callout			 p_new_handshake;
193744bfb21SJohn Baldwin 	struct callout			 p_send_keepalive;
194744bfb21SJohn Baldwin 	struct callout			 p_retry_handshake;
195744bfb21SJohn Baldwin 	struct callout			 p_zero_key_material;
196744bfb21SJohn Baldwin 	struct callout			 p_persistent_keepalive;
197744bfb21SJohn Baldwin 
198744bfb21SJohn Baldwin 	struct mtx			 p_handshake_mtx;
199744bfb21SJohn Baldwin 	struct timespec			 p_handshake_complete;	/* nanotime */
200744bfb21SJohn Baldwin 	int				 p_handshake_retries;
201744bfb21SJohn Baldwin 
202744bfb21SJohn Baldwin 	struct grouptask		 p_send;
203744bfb21SJohn Baldwin 	struct grouptask		 p_recv;
204744bfb21SJohn Baldwin 
205744bfb21SJohn Baldwin 	counter_u64_t			 p_tx_bytes;
206744bfb21SJohn Baldwin 	counter_u64_t			 p_rx_bytes;
207744bfb21SJohn Baldwin 
208744bfb21SJohn Baldwin 	LIST_HEAD(, wg_aip)		 p_aips;
209744bfb21SJohn Baldwin 	size_t				 p_aips_num;
210744bfb21SJohn Baldwin };
211744bfb21SJohn Baldwin 
212744bfb21SJohn Baldwin struct wg_socket {
213744bfb21SJohn Baldwin 	struct socket	*so_so4;
214744bfb21SJohn Baldwin 	struct socket	*so_so6;
215744bfb21SJohn Baldwin 	uint32_t	 so_user_cookie;
216744bfb21SJohn Baldwin 	int		 so_fibnum;
217744bfb21SJohn Baldwin 	in_port_t	 so_port;
218744bfb21SJohn Baldwin };
219744bfb21SJohn Baldwin 
220744bfb21SJohn Baldwin struct wg_softc {
221744bfb21SJohn Baldwin 	LIST_ENTRY(wg_softc)	 sc_entry;
22287e72834SJustin Hibbits 	if_t			 sc_ifp;
223744bfb21SJohn Baldwin 	int			 sc_flags;
224744bfb21SJohn Baldwin 
225744bfb21SJohn Baldwin 	struct ucred		*sc_ucred;
226744bfb21SJohn Baldwin 	struct wg_socket	 sc_socket;
227744bfb21SJohn Baldwin 
228744bfb21SJohn Baldwin 	TAILQ_HEAD(,wg_peer)	 sc_peers;
229744bfb21SJohn Baldwin 	size_t			 sc_peers_num;
230744bfb21SJohn Baldwin 
231744bfb21SJohn Baldwin 	struct noise_local	*sc_local;
232744bfb21SJohn Baldwin 	struct cookie_checker	 sc_cookie;
233744bfb21SJohn Baldwin 
234744bfb21SJohn Baldwin 	struct radix_node_head	*sc_aip4;
235744bfb21SJohn Baldwin 	struct radix_node_head	*sc_aip6;
236744bfb21SJohn Baldwin 
237744bfb21SJohn Baldwin 	struct grouptask	 sc_handshake;
238744bfb21SJohn Baldwin 	struct wg_queue		 sc_handshake_queue;
239744bfb21SJohn Baldwin 
240744bfb21SJohn Baldwin 	struct grouptask	*sc_encrypt;
241744bfb21SJohn Baldwin 	struct grouptask	*sc_decrypt;
242744bfb21SJohn Baldwin 	struct wg_queue		 sc_encrypt_parallel;
243744bfb21SJohn Baldwin 	struct wg_queue		 sc_decrypt_parallel;
244744bfb21SJohn Baldwin 	u_int			 sc_encrypt_last_cpu;
245744bfb21SJohn Baldwin 	u_int			 sc_decrypt_last_cpu;
246744bfb21SJohn Baldwin 
247744bfb21SJohn Baldwin 	struct sx		 sc_lock;
248744bfb21SJohn Baldwin };
249744bfb21SJohn Baldwin 
250744bfb21SJohn Baldwin #define	WGF_DYING	0x0001
251744bfb21SJohn Baldwin 
252744bfb21SJohn Baldwin #define MAX_LOOPS	8
253744bfb21SJohn Baldwin #define MTAG_WGLOOP	0x77676c70 /* wglp */
254744bfb21SJohn Baldwin 
255744bfb21SJohn Baldwin #define	GROUPTASK_DRAIN(gtask)			\
256744bfb21SJohn Baldwin 	gtaskqueue_drain((gtask)->gt_taskqueue, &(gtask)->gt_task)
257744bfb21SJohn Baldwin 
258744bfb21SJohn Baldwin #define BPF_MTAP2_AF(ifp, m, af) do { \
259744bfb21SJohn Baldwin 		uint32_t __bpf_tap_af = (af); \
260744bfb21SJohn Baldwin 		BPF_MTAP2(ifp, &__bpf_tap_af, sizeof(__bpf_tap_af), m); \
261744bfb21SJohn Baldwin 	} while (0)
262744bfb21SJohn Baldwin 
263744bfb21SJohn Baldwin static int clone_count;
264744bfb21SJohn Baldwin static uma_zone_t wg_packet_zone;
265744bfb21SJohn Baldwin static volatile unsigned long peer_counter = 0;
266744bfb21SJohn Baldwin static const char wgname[] = "wg";
267744bfb21SJohn Baldwin static unsigned wg_osd_jail_slot;
268744bfb21SJohn Baldwin 
269744bfb21SJohn Baldwin static struct sx wg_sx;
270744bfb21SJohn Baldwin SX_SYSINIT(wg_sx, &wg_sx, "wg_sx");
271744bfb21SJohn Baldwin 
272744bfb21SJohn Baldwin static LIST_HEAD(, wg_softc) wg_list = LIST_HEAD_INITIALIZER(wg_list);
273744bfb21SJohn Baldwin 
274744bfb21SJohn Baldwin static TASKQGROUP_DEFINE(wg_tqg, mp_ncpus, 1);
275744bfb21SJohn Baldwin 
276744bfb21SJohn Baldwin MALLOC_DEFINE(M_WG, "WG", "wireguard");
277744bfb21SJohn Baldwin 
278744bfb21SJohn Baldwin VNET_DEFINE_STATIC(struct if_clone *, wg_cloner);
279744bfb21SJohn Baldwin 
280744bfb21SJohn Baldwin #define	V_wg_cloner	VNET(wg_cloner)
281744bfb21SJohn Baldwin #define	WG_CAPS		IFCAP_LINKSTATE
282744bfb21SJohn Baldwin 
283744bfb21SJohn Baldwin struct wg_timespec64 {
284744bfb21SJohn Baldwin 	uint64_t	tv_sec;
285744bfb21SJohn Baldwin 	uint64_t	tv_nsec;
286744bfb21SJohn Baldwin };
287744bfb21SJohn Baldwin 
288744bfb21SJohn Baldwin static int wg_socket_init(struct wg_softc *, in_port_t);
289744bfb21SJohn Baldwin static int wg_socket_bind(struct socket **, struct socket **, in_port_t *);
290744bfb21SJohn Baldwin static void wg_socket_set(struct wg_softc *, struct socket *, struct socket *);
291744bfb21SJohn Baldwin static void wg_socket_uninit(struct wg_softc *);
292744bfb21SJohn Baldwin static int wg_socket_set_sockopt(struct socket *, struct socket *, int, void *, size_t);
293744bfb21SJohn Baldwin static int wg_socket_set_cookie(struct wg_softc *, uint32_t);
294744bfb21SJohn Baldwin static int wg_socket_set_fibnum(struct wg_softc *, int);
295744bfb21SJohn Baldwin static int wg_send(struct wg_softc *, struct wg_endpoint *, struct mbuf *);
296744bfb21SJohn Baldwin static void wg_timers_enable(struct wg_peer *);
297744bfb21SJohn Baldwin static void wg_timers_disable(struct wg_peer *);
298744bfb21SJohn Baldwin static void wg_timers_set_persistent_keepalive(struct wg_peer *, uint16_t);
299744bfb21SJohn Baldwin static void wg_timers_get_last_handshake(struct wg_peer *, struct wg_timespec64 *);
300744bfb21SJohn Baldwin static void wg_timers_event_data_sent(struct wg_peer *);
301744bfb21SJohn Baldwin static void wg_timers_event_data_received(struct wg_peer *);
302744bfb21SJohn Baldwin static void wg_timers_event_any_authenticated_packet_sent(struct wg_peer *);
303744bfb21SJohn Baldwin static void wg_timers_event_any_authenticated_packet_received(struct wg_peer *);
304744bfb21SJohn Baldwin static void wg_timers_event_any_authenticated_packet_traversal(struct wg_peer *);
305744bfb21SJohn Baldwin static void wg_timers_event_handshake_initiated(struct wg_peer *);
306744bfb21SJohn Baldwin static void wg_timers_event_handshake_complete(struct wg_peer *);
307744bfb21SJohn Baldwin static void wg_timers_event_session_derived(struct wg_peer *);
308744bfb21SJohn Baldwin static void wg_timers_event_want_initiation(struct wg_peer *);
309744bfb21SJohn Baldwin static void wg_timers_run_send_initiation(struct wg_peer *, bool);
310744bfb21SJohn Baldwin static void wg_timers_run_retry_handshake(void *);
311744bfb21SJohn Baldwin static void wg_timers_run_send_keepalive(void *);
312744bfb21SJohn Baldwin static void wg_timers_run_new_handshake(void *);
313744bfb21SJohn Baldwin static void wg_timers_run_zero_key_material(void *);
314744bfb21SJohn Baldwin static void wg_timers_run_persistent_keepalive(void *);
315744bfb21SJohn Baldwin static int wg_aip_add(struct wg_softc *, struct wg_peer *, sa_family_t, const void *, uint8_t);
316744bfb21SJohn Baldwin static struct wg_peer *wg_aip_lookup(struct wg_softc *, sa_family_t, void *);
317744bfb21SJohn Baldwin static void wg_aip_remove_all(struct wg_softc *, struct wg_peer *);
318744bfb21SJohn Baldwin static struct wg_peer *wg_peer_alloc(struct wg_softc *, const uint8_t [WG_KEY_SIZE]);
319744bfb21SJohn Baldwin static void wg_peer_free_deferred(struct noise_remote *);
320744bfb21SJohn Baldwin static void wg_peer_destroy(struct wg_peer *);
321744bfb21SJohn Baldwin static void wg_peer_destroy_all(struct wg_softc *);
322744bfb21SJohn Baldwin static void wg_peer_send_buf(struct wg_peer *, uint8_t *, size_t);
323744bfb21SJohn Baldwin static void wg_send_initiation(struct wg_peer *);
324744bfb21SJohn Baldwin static void wg_send_response(struct wg_peer *);
325744bfb21SJohn Baldwin static void wg_send_cookie(struct wg_softc *, struct cookie_macs *, uint32_t, struct wg_endpoint *);
326744bfb21SJohn Baldwin static void wg_peer_set_endpoint(struct wg_peer *, struct wg_endpoint *);
327744bfb21SJohn Baldwin static void wg_peer_clear_src(struct wg_peer *);
328744bfb21SJohn Baldwin static void wg_peer_get_endpoint(struct wg_peer *, struct wg_endpoint *);
329744bfb21SJohn Baldwin static void wg_send_buf(struct wg_softc *, struct wg_endpoint *, uint8_t *, size_t);
330744bfb21SJohn Baldwin static void wg_send_keepalive(struct wg_peer *);
331744bfb21SJohn Baldwin static void wg_handshake(struct wg_softc *, struct wg_packet *);
332744bfb21SJohn Baldwin static void wg_encrypt(struct wg_softc *, struct wg_packet *);
333744bfb21SJohn Baldwin static void wg_decrypt(struct wg_softc *, struct wg_packet *);
334744bfb21SJohn Baldwin static void wg_softc_handshake_receive(struct wg_softc *);
335744bfb21SJohn Baldwin static void wg_softc_decrypt(struct wg_softc *);
336744bfb21SJohn Baldwin static void wg_softc_encrypt(struct wg_softc *);
337744bfb21SJohn Baldwin static void wg_encrypt_dispatch(struct wg_softc *);
338744bfb21SJohn Baldwin static void wg_decrypt_dispatch(struct wg_softc *);
339744bfb21SJohn Baldwin static void wg_deliver_out(struct wg_peer *);
340744bfb21SJohn Baldwin static void wg_deliver_in(struct wg_peer *);
341744bfb21SJohn Baldwin static struct wg_packet *wg_packet_alloc(struct mbuf *);
342744bfb21SJohn Baldwin static void wg_packet_free(struct wg_packet *);
343744bfb21SJohn Baldwin static void wg_queue_init(struct wg_queue *, const char *);
344744bfb21SJohn Baldwin static void wg_queue_deinit(struct wg_queue *);
345744bfb21SJohn Baldwin static size_t wg_queue_len(struct wg_queue *);
346744bfb21SJohn Baldwin static int wg_queue_enqueue_handshake(struct wg_queue *, struct wg_packet *);
347744bfb21SJohn Baldwin static struct wg_packet *wg_queue_dequeue_handshake(struct wg_queue *);
348744bfb21SJohn Baldwin static void wg_queue_push_staged(struct wg_queue *, struct wg_packet *);
349744bfb21SJohn Baldwin static void wg_queue_enlist_staged(struct wg_queue *, struct wg_packet_list *);
350744bfb21SJohn Baldwin static void wg_queue_delist_staged(struct wg_queue *, struct wg_packet_list *);
351744bfb21SJohn Baldwin static void wg_queue_purge(struct wg_queue *);
352744bfb21SJohn Baldwin static int wg_queue_both(struct wg_queue *, struct wg_queue *, struct wg_packet *);
353744bfb21SJohn Baldwin static struct wg_packet *wg_queue_dequeue_serial(struct wg_queue *);
354744bfb21SJohn Baldwin static struct wg_packet *wg_queue_dequeue_parallel(struct wg_queue *);
355744bfb21SJohn Baldwin static bool wg_input(struct mbuf *, int, struct inpcb *, const struct sockaddr *, void *);
356744bfb21SJohn Baldwin static void wg_peer_send_staged(struct wg_peer *);
357eb3f9a7aSAlan Somers static int wg_clone_create(struct if_clone *ifc, char *name, size_t len,
35887e72834SJustin Hibbits 	struct ifc_data *ifd, if_t *ifpp);
35987e72834SJustin Hibbits static void wg_qflush(if_t);
360744bfb21SJohn Baldwin static inline int determine_af_and_pullup(struct mbuf **m, sa_family_t *af);
36187e72834SJustin Hibbits static int wg_xmit(if_t, struct mbuf *, sa_family_t, uint32_t);
36287e72834SJustin Hibbits static int wg_transmit(if_t, struct mbuf *);
36387e72834SJustin Hibbits static int wg_output(if_t, struct mbuf *, const struct sockaddr *, struct route *);
36487e72834SJustin Hibbits static int wg_clone_destroy(struct if_clone *ifc, if_t ifp,
365eb3f9a7aSAlan Somers 	uint32_t flags);
366744bfb21SJohn Baldwin static bool wgc_privileged(struct wg_softc *);
367744bfb21SJohn Baldwin static int wgc_get(struct wg_softc *, struct wg_data_io *);
368744bfb21SJohn Baldwin static int wgc_set(struct wg_softc *, struct wg_data_io *);
369744bfb21SJohn Baldwin static int wg_up(struct wg_softc *);
370744bfb21SJohn Baldwin static void wg_down(struct wg_softc *);
37187e72834SJustin Hibbits static void wg_reassign(if_t, struct vnet *, char *unused);
372744bfb21SJohn Baldwin static void wg_init(void *);
37387e72834SJustin Hibbits static int wg_ioctl(if_t, u_long, caddr_t);
374744bfb21SJohn Baldwin static void vnet_wg_init(const void *);
375744bfb21SJohn Baldwin static void vnet_wg_uninit(const void *);
376744bfb21SJohn Baldwin static int wg_module_init(void);
377744bfb21SJohn Baldwin static void wg_module_deinit(void);
378744bfb21SJohn Baldwin 
379744bfb21SJohn Baldwin /* TODO Peer */
380744bfb21SJohn Baldwin static struct wg_peer *
wg_peer_alloc(struct wg_softc * sc,const uint8_t pub_key[WG_KEY_SIZE])381744bfb21SJohn Baldwin wg_peer_alloc(struct wg_softc *sc, const uint8_t pub_key[WG_KEY_SIZE])
382744bfb21SJohn Baldwin {
383744bfb21SJohn Baldwin 	struct wg_peer *peer;
384744bfb21SJohn Baldwin 
385744bfb21SJohn Baldwin 	sx_assert(&sc->sc_lock, SX_XLOCKED);
386744bfb21SJohn Baldwin 
387744bfb21SJohn Baldwin 	peer = malloc(sizeof(*peer), M_WG, M_WAITOK | M_ZERO);
388744bfb21SJohn Baldwin 	peer->p_remote = noise_remote_alloc(sc->sc_local, peer, pub_key);
389744bfb21SJohn Baldwin 	peer->p_tx_bytes = counter_u64_alloc(M_WAITOK);
390744bfb21SJohn Baldwin 	peer->p_rx_bytes = counter_u64_alloc(M_WAITOK);
391744bfb21SJohn Baldwin 	peer->p_id = peer_counter++;
392744bfb21SJohn Baldwin 	peer->p_sc = sc;
393744bfb21SJohn Baldwin 
394744bfb21SJohn Baldwin 	cookie_maker_init(&peer->p_cookie, pub_key);
395744bfb21SJohn Baldwin 
396744bfb21SJohn Baldwin 	rw_init(&peer->p_endpoint_lock, "wg_peer_endpoint");
397744bfb21SJohn Baldwin 
398744bfb21SJohn Baldwin 	wg_queue_init(&peer->p_stage_queue, "stageq");
399744bfb21SJohn Baldwin 	wg_queue_init(&peer->p_encrypt_serial, "txq");
400744bfb21SJohn Baldwin 	wg_queue_init(&peer->p_decrypt_serial, "rxq");
401744bfb21SJohn Baldwin 
402744bfb21SJohn Baldwin 	peer->p_enabled = false;
403744bfb21SJohn Baldwin 	peer->p_need_another_keepalive = false;
404744bfb21SJohn Baldwin 	peer->p_persistent_keepalive_interval = 0;
405744bfb21SJohn Baldwin 	callout_init(&peer->p_new_handshake, true);
406744bfb21SJohn Baldwin 	callout_init(&peer->p_send_keepalive, true);
407744bfb21SJohn Baldwin 	callout_init(&peer->p_retry_handshake, true);
408744bfb21SJohn Baldwin 	callout_init(&peer->p_persistent_keepalive, true);
409744bfb21SJohn Baldwin 	callout_init(&peer->p_zero_key_material, true);
410744bfb21SJohn Baldwin 
411744bfb21SJohn Baldwin 	mtx_init(&peer->p_handshake_mtx, "peer handshake", NULL, MTX_DEF);
412744bfb21SJohn Baldwin 	bzero(&peer->p_handshake_complete, sizeof(peer->p_handshake_complete));
413744bfb21SJohn Baldwin 	peer->p_handshake_retries = 0;
414744bfb21SJohn Baldwin 
415744bfb21SJohn Baldwin 	GROUPTASK_INIT(&peer->p_send, 0, (gtask_fn_t *)wg_deliver_out, peer);
416744bfb21SJohn Baldwin 	taskqgroup_attach(qgroup_wg_tqg, &peer->p_send, peer, NULL, NULL, "wg send");
417744bfb21SJohn Baldwin 	GROUPTASK_INIT(&peer->p_recv, 0, (gtask_fn_t *)wg_deliver_in, peer);
418744bfb21SJohn Baldwin 	taskqgroup_attach(qgroup_wg_tqg, &peer->p_recv, peer, NULL, NULL, "wg recv");
419744bfb21SJohn Baldwin 
420744bfb21SJohn Baldwin 	LIST_INIT(&peer->p_aips);
421744bfb21SJohn Baldwin 	peer->p_aips_num = 0;
422744bfb21SJohn Baldwin 
423744bfb21SJohn Baldwin 	return (peer);
424744bfb21SJohn Baldwin }
425744bfb21SJohn Baldwin 
426744bfb21SJohn Baldwin static void
wg_peer_free_deferred(struct noise_remote * r)427744bfb21SJohn Baldwin wg_peer_free_deferred(struct noise_remote *r)
428744bfb21SJohn Baldwin {
429744bfb21SJohn Baldwin 	struct wg_peer *peer = noise_remote_arg(r);
430744bfb21SJohn Baldwin 
431744bfb21SJohn Baldwin 	/* While there are no references remaining, we may still have
432744bfb21SJohn Baldwin 	 * p_{send,recv} executing (think empty queue, but wg_deliver_{in,out}
433744bfb21SJohn Baldwin 	 * needs to check the queue. We should wait for them and then free. */
434744bfb21SJohn Baldwin 	GROUPTASK_DRAIN(&peer->p_recv);
435744bfb21SJohn Baldwin 	GROUPTASK_DRAIN(&peer->p_send);
436744bfb21SJohn Baldwin 	taskqgroup_detach(qgroup_wg_tqg, &peer->p_recv);
437744bfb21SJohn Baldwin 	taskqgroup_detach(qgroup_wg_tqg, &peer->p_send);
438744bfb21SJohn Baldwin 
439744bfb21SJohn Baldwin 	wg_queue_deinit(&peer->p_decrypt_serial);
440744bfb21SJohn Baldwin 	wg_queue_deinit(&peer->p_encrypt_serial);
441744bfb21SJohn Baldwin 	wg_queue_deinit(&peer->p_stage_queue);
442744bfb21SJohn Baldwin 
443744bfb21SJohn Baldwin 	counter_u64_free(peer->p_tx_bytes);
444744bfb21SJohn Baldwin 	counter_u64_free(peer->p_rx_bytes);
445744bfb21SJohn Baldwin 	rw_destroy(&peer->p_endpoint_lock);
446744bfb21SJohn Baldwin 	mtx_destroy(&peer->p_handshake_mtx);
447744bfb21SJohn Baldwin 
448744bfb21SJohn Baldwin 	cookie_maker_free(&peer->p_cookie);
449744bfb21SJohn Baldwin 
450744bfb21SJohn Baldwin 	free(peer, M_WG);
451744bfb21SJohn Baldwin }
452744bfb21SJohn Baldwin 
453744bfb21SJohn Baldwin static void
wg_peer_destroy(struct wg_peer * peer)454744bfb21SJohn Baldwin wg_peer_destroy(struct wg_peer *peer)
455744bfb21SJohn Baldwin {
456744bfb21SJohn Baldwin 	struct wg_softc *sc = peer->p_sc;
457744bfb21SJohn Baldwin 	sx_assert(&sc->sc_lock, SX_XLOCKED);
458744bfb21SJohn Baldwin 
459744bfb21SJohn Baldwin 	/* Disable remote and timers. This will prevent any new handshakes
460744bfb21SJohn Baldwin 	 * occuring. */
461744bfb21SJohn Baldwin 	noise_remote_disable(peer->p_remote);
462744bfb21SJohn Baldwin 	wg_timers_disable(peer);
463744bfb21SJohn Baldwin 
464744bfb21SJohn Baldwin 	/* Now we can remove all allowed IPs so no more packets will be routed
465744bfb21SJohn Baldwin 	 * to the peer. */
466744bfb21SJohn Baldwin 	wg_aip_remove_all(sc, peer);
467744bfb21SJohn Baldwin 
468744bfb21SJohn Baldwin 	/* Remove peer from the interface, then free. Some references may still
469744bfb21SJohn Baldwin 	 * exist to p_remote, so noise_remote_free will wait until they're all
470744bfb21SJohn Baldwin 	 * put to call wg_peer_free_deferred. */
471744bfb21SJohn Baldwin 	sc->sc_peers_num--;
472744bfb21SJohn Baldwin 	TAILQ_REMOVE(&sc->sc_peers, peer, p_entry);
473744bfb21SJohn Baldwin 	DPRINTF(sc, "Peer %" PRIu64 " destroyed\n", peer->p_id);
474744bfb21SJohn Baldwin 	noise_remote_free(peer->p_remote, wg_peer_free_deferred);
475744bfb21SJohn Baldwin }
476744bfb21SJohn Baldwin 
477744bfb21SJohn Baldwin static void
wg_peer_destroy_all(struct wg_softc * sc)478744bfb21SJohn Baldwin wg_peer_destroy_all(struct wg_softc *sc)
479744bfb21SJohn Baldwin {
480744bfb21SJohn Baldwin 	struct wg_peer *peer, *tpeer;
481744bfb21SJohn Baldwin 	TAILQ_FOREACH_SAFE(peer, &sc->sc_peers, p_entry, tpeer)
482744bfb21SJohn Baldwin 		wg_peer_destroy(peer);
483744bfb21SJohn Baldwin }
484744bfb21SJohn Baldwin 
485744bfb21SJohn Baldwin static void
wg_peer_set_endpoint(struct wg_peer * peer,struct wg_endpoint * e)486744bfb21SJohn Baldwin wg_peer_set_endpoint(struct wg_peer *peer, struct wg_endpoint *e)
487744bfb21SJohn Baldwin {
488744bfb21SJohn Baldwin 	MPASS(e->e_remote.r_sa.sa_family != 0);
489744bfb21SJohn Baldwin 	if (memcmp(e, &peer->p_endpoint, sizeof(*e)) == 0)
490744bfb21SJohn Baldwin 		return;
491744bfb21SJohn Baldwin 
492744bfb21SJohn Baldwin 	rw_wlock(&peer->p_endpoint_lock);
493744bfb21SJohn Baldwin 	peer->p_endpoint = *e;
494744bfb21SJohn Baldwin 	rw_wunlock(&peer->p_endpoint_lock);
495744bfb21SJohn Baldwin }
496744bfb21SJohn Baldwin 
497744bfb21SJohn Baldwin static void
wg_peer_clear_src(struct wg_peer * peer)498744bfb21SJohn Baldwin wg_peer_clear_src(struct wg_peer *peer)
499744bfb21SJohn Baldwin {
500744bfb21SJohn Baldwin 	rw_wlock(&peer->p_endpoint_lock);
501744bfb21SJohn Baldwin 	bzero(&peer->p_endpoint.e_local, sizeof(peer->p_endpoint.e_local));
502744bfb21SJohn Baldwin 	rw_wunlock(&peer->p_endpoint_lock);
503744bfb21SJohn Baldwin }
504744bfb21SJohn Baldwin 
505744bfb21SJohn Baldwin static void
wg_peer_get_endpoint(struct wg_peer * peer,struct wg_endpoint * e)506744bfb21SJohn Baldwin wg_peer_get_endpoint(struct wg_peer *peer, struct wg_endpoint *e)
507744bfb21SJohn Baldwin {
508744bfb21SJohn Baldwin 	rw_rlock(&peer->p_endpoint_lock);
509744bfb21SJohn Baldwin 	*e = peer->p_endpoint;
510744bfb21SJohn Baldwin 	rw_runlock(&peer->p_endpoint_lock);
511744bfb21SJohn Baldwin }
512744bfb21SJohn Baldwin 
513744bfb21SJohn Baldwin /* Allowed IP */
514744bfb21SJohn Baldwin static int
wg_aip_add(struct wg_softc * sc,struct wg_peer * peer,sa_family_t af,const void * addr,uint8_t cidr)515744bfb21SJohn Baldwin wg_aip_add(struct wg_softc *sc, struct wg_peer *peer, sa_family_t af, const void *addr, uint8_t cidr)
516744bfb21SJohn Baldwin {
517744bfb21SJohn Baldwin 	struct radix_node_head	*root;
518744bfb21SJohn Baldwin 	struct radix_node	*node;
519744bfb21SJohn Baldwin 	struct wg_aip		*aip;
520744bfb21SJohn Baldwin 	int			 ret = 0;
521744bfb21SJohn Baldwin 
522744bfb21SJohn Baldwin 	aip = malloc(sizeof(*aip), M_WG, M_WAITOK | M_ZERO);
523744bfb21SJohn Baldwin 	aip->a_peer = peer;
524744bfb21SJohn Baldwin 	aip->a_af = af;
525744bfb21SJohn Baldwin 
526744bfb21SJohn Baldwin 	switch (af) {
527744bfb21SJohn Baldwin #ifdef INET
528744bfb21SJohn Baldwin 	case AF_INET:
529744bfb21SJohn Baldwin 		if (cidr > 32) cidr = 32;
530744bfb21SJohn Baldwin 		root = sc->sc_aip4;
531744bfb21SJohn Baldwin 		aip->a_addr.in = *(const struct in_addr *)addr;
532744bfb21SJohn Baldwin 		aip->a_mask.ip = htonl(~((1LL << (32 - cidr)) - 1) & 0xffffffff);
533744bfb21SJohn Baldwin 		aip->a_addr.ip &= aip->a_mask.ip;
534744bfb21SJohn Baldwin 		aip->a_addr.length = aip->a_mask.length = offsetof(struct aip_addr, in) + sizeof(struct in_addr);
535744bfb21SJohn Baldwin 		break;
536744bfb21SJohn Baldwin #endif
537744bfb21SJohn Baldwin #ifdef INET6
538744bfb21SJohn Baldwin 	case AF_INET6:
539744bfb21SJohn Baldwin 		if (cidr > 128) cidr = 128;
540744bfb21SJohn Baldwin 		root = sc->sc_aip6;
541744bfb21SJohn Baldwin 		aip->a_addr.in6 = *(const struct in6_addr *)addr;
542744bfb21SJohn Baldwin 		in6_prefixlen2mask(&aip->a_mask.in6, cidr);
543744bfb21SJohn Baldwin 		for (int i = 0; i < 4; i++)
544744bfb21SJohn Baldwin 			aip->a_addr.ip6[i] &= aip->a_mask.ip6[i];
545744bfb21SJohn Baldwin 		aip->a_addr.length = aip->a_mask.length = offsetof(struct aip_addr, in6) + sizeof(struct in6_addr);
546744bfb21SJohn Baldwin 		break;
547744bfb21SJohn Baldwin #endif
548744bfb21SJohn Baldwin 	default:
549744bfb21SJohn Baldwin 		free(aip, M_WG);
550744bfb21SJohn Baldwin 		return (EAFNOSUPPORT);
551744bfb21SJohn Baldwin 	}
552744bfb21SJohn Baldwin 
553744bfb21SJohn Baldwin 	RADIX_NODE_HEAD_LOCK(root);
554744bfb21SJohn Baldwin 	node = root->rnh_addaddr(&aip->a_addr, &aip->a_mask, &root->rh, aip->a_nodes);
555744bfb21SJohn Baldwin 	if (node == aip->a_nodes) {
556744bfb21SJohn Baldwin 		LIST_INSERT_HEAD(&peer->p_aips, aip, a_entry);
557744bfb21SJohn Baldwin 		peer->p_aips_num++;
558744bfb21SJohn Baldwin 	} else if (!node)
559744bfb21SJohn Baldwin 		node = root->rnh_lookup(&aip->a_addr, &aip->a_mask, &root->rh);
560744bfb21SJohn Baldwin 	if (!node) {
561744bfb21SJohn Baldwin 		free(aip, M_WG);
562dcc4d293SAaron LI 		ret = ENOMEM;
563744bfb21SJohn Baldwin 	} else if (node != aip->a_nodes) {
564744bfb21SJohn Baldwin 		free(aip, M_WG);
565744bfb21SJohn Baldwin 		aip = (struct wg_aip *)node;
566744bfb21SJohn Baldwin 		if (aip->a_peer != peer) {
567744bfb21SJohn Baldwin 			LIST_REMOVE(aip, a_entry);
568744bfb21SJohn Baldwin 			aip->a_peer->p_aips_num--;
569744bfb21SJohn Baldwin 			aip->a_peer = peer;
570744bfb21SJohn Baldwin 			LIST_INSERT_HEAD(&peer->p_aips, aip, a_entry);
571744bfb21SJohn Baldwin 			aip->a_peer->p_aips_num++;
572744bfb21SJohn Baldwin 		}
573744bfb21SJohn Baldwin 	}
574744bfb21SJohn Baldwin 	RADIX_NODE_HEAD_UNLOCK(root);
575744bfb21SJohn Baldwin 	return (ret);
576744bfb21SJohn Baldwin }
577744bfb21SJohn Baldwin 
578744bfb21SJohn Baldwin static struct wg_peer *
wg_aip_lookup(struct wg_softc * sc,sa_family_t af,void * a)579744bfb21SJohn Baldwin wg_aip_lookup(struct wg_softc *sc, sa_family_t af, void *a)
580744bfb21SJohn Baldwin {
581744bfb21SJohn Baldwin 	struct radix_node_head	*root;
582744bfb21SJohn Baldwin 	struct radix_node	*node;
583744bfb21SJohn Baldwin 	struct wg_peer		*peer;
584744bfb21SJohn Baldwin 	struct aip_addr		 addr;
585744bfb21SJohn Baldwin 	RADIX_NODE_HEAD_RLOCK_TRACKER;
586744bfb21SJohn Baldwin 
587744bfb21SJohn Baldwin 	switch (af) {
588744bfb21SJohn Baldwin 	case AF_INET:
589744bfb21SJohn Baldwin 		root = sc->sc_aip4;
590744bfb21SJohn Baldwin 		memcpy(&addr.in, a, sizeof(addr.in));
591744bfb21SJohn Baldwin 		addr.length = offsetof(struct aip_addr, in) + sizeof(struct in_addr);
592744bfb21SJohn Baldwin 		break;
593744bfb21SJohn Baldwin 	case AF_INET6:
594744bfb21SJohn Baldwin 		root = sc->sc_aip6;
595744bfb21SJohn Baldwin 		memcpy(&addr.in6, a, sizeof(addr.in6));
596744bfb21SJohn Baldwin 		addr.length = offsetof(struct aip_addr, in6) + sizeof(struct in6_addr);
597744bfb21SJohn Baldwin 		break;
598744bfb21SJohn Baldwin 	default:
599744bfb21SJohn Baldwin 		return NULL;
600744bfb21SJohn Baldwin 	}
601744bfb21SJohn Baldwin 
602744bfb21SJohn Baldwin 	RADIX_NODE_HEAD_RLOCK(root);
603744bfb21SJohn Baldwin 	node = root->rnh_matchaddr(&addr, &root->rh);
604744bfb21SJohn Baldwin 	if (node != NULL) {
605744bfb21SJohn Baldwin 		peer = ((struct wg_aip *)node)->a_peer;
606744bfb21SJohn Baldwin 		noise_remote_ref(peer->p_remote);
607744bfb21SJohn Baldwin 	} else {
608744bfb21SJohn Baldwin 		peer = NULL;
609744bfb21SJohn Baldwin 	}
610744bfb21SJohn Baldwin 	RADIX_NODE_HEAD_RUNLOCK(root);
611744bfb21SJohn Baldwin 
612744bfb21SJohn Baldwin 	return (peer);
613744bfb21SJohn Baldwin }
614744bfb21SJohn Baldwin 
615744bfb21SJohn Baldwin static void
wg_aip_remove_all(struct wg_softc * sc,struct wg_peer * peer)616744bfb21SJohn Baldwin wg_aip_remove_all(struct wg_softc *sc, struct wg_peer *peer)
617744bfb21SJohn Baldwin {
618744bfb21SJohn Baldwin 	struct wg_aip		*aip, *taip;
619744bfb21SJohn Baldwin 
620744bfb21SJohn Baldwin 	RADIX_NODE_HEAD_LOCK(sc->sc_aip4);
621744bfb21SJohn Baldwin 	LIST_FOREACH_SAFE(aip, &peer->p_aips, a_entry, taip) {
622744bfb21SJohn Baldwin 		if (aip->a_af == AF_INET) {
623744bfb21SJohn Baldwin 			if (sc->sc_aip4->rnh_deladdr(&aip->a_addr, &aip->a_mask, &sc->sc_aip4->rh) == NULL)
624744bfb21SJohn Baldwin 				panic("failed to delete aip %p", aip);
625744bfb21SJohn Baldwin 			LIST_REMOVE(aip, a_entry);
626744bfb21SJohn Baldwin 			peer->p_aips_num--;
627744bfb21SJohn Baldwin 			free(aip, M_WG);
628744bfb21SJohn Baldwin 		}
629744bfb21SJohn Baldwin 	}
630744bfb21SJohn Baldwin 	RADIX_NODE_HEAD_UNLOCK(sc->sc_aip4);
631744bfb21SJohn Baldwin 
632744bfb21SJohn Baldwin 	RADIX_NODE_HEAD_LOCK(sc->sc_aip6);
633744bfb21SJohn Baldwin 	LIST_FOREACH_SAFE(aip, &peer->p_aips, a_entry, taip) {
634744bfb21SJohn Baldwin 		if (aip->a_af == AF_INET6) {
635744bfb21SJohn Baldwin 			if (sc->sc_aip6->rnh_deladdr(&aip->a_addr, &aip->a_mask, &sc->sc_aip6->rh) == NULL)
636744bfb21SJohn Baldwin 				panic("failed to delete aip %p", aip);
637744bfb21SJohn Baldwin 			LIST_REMOVE(aip, a_entry);
638744bfb21SJohn Baldwin 			peer->p_aips_num--;
639744bfb21SJohn Baldwin 			free(aip, M_WG);
640744bfb21SJohn Baldwin 		}
641744bfb21SJohn Baldwin 	}
642744bfb21SJohn Baldwin 	RADIX_NODE_HEAD_UNLOCK(sc->sc_aip6);
643744bfb21SJohn Baldwin 
644744bfb21SJohn Baldwin 	if (!LIST_EMPTY(&peer->p_aips) || peer->p_aips_num != 0)
645744bfb21SJohn Baldwin 		panic("wg_aip_remove_all could not delete all %p", peer);
646744bfb21SJohn Baldwin }
647744bfb21SJohn Baldwin 
648744bfb21SJohn Baldwin static int
wg_socket_init(struct wg_softc * sc,in_port_t port)649744bfb21SJohn Baldwin wg_socket_init(struct wg_softc *sc, in_port_t port)
650744bfb21SJohn Baldwin {
651744bfb21SJohn Baldwin 	struct ucred *cred = sc->sc_ucred;
652744bfb21SJohn Baldwin 	struct socket *so4 = NULL, *so6 = NULL;
653744bfb21SJohn Baldwin 	int rc;
654744bfb21SJohn Baldwin 
655744bfb21SJohn Baldwin 	sx_assert(&sc->sc_lock, SX_XLOCKED);
656744bfb21SJohn Baldwin 
657744bfb21SJohn Baldwin 	if (!cred)
658744bfb21SJohn Baldwin 		return (EBUSY);
659744bfb21SJohn Baldwin 
660744bfb21SJohn Baldwin 	/*
661744bfb21SJohn Baldwin 	 * For socket creation, we use the creds of the thread that created the
662744bfb21SJohn Baldwin 	 * tunnel rather than the current thread to maintain the semantics that
663744bfb21SJohn Baldwin 	 * WireGuard has on Linux with network namespaces -- that the sockets
664744bfb21SJohn Baldwin 	 * are created in their home vnet so that they can be configured and
665744bfb21SJohn Baldwin 	 * functionally attached to a foreign vnet as the jail's only interface
666744bfb21SJohn Baldwin 	 * to the network.
667744bfb21SJohn Baldwin 	 */
668744bfb21SJohn Baldwin #ifdef INET
669744bfb21SJohn Baldwin 	rc = socreate(AF_INET, &so4, SOCK_DGRAM, IPPROTO_UDP, cred, curthread);
670744bfb21SJohn Baldwin 	if (rc)
671744bfb21SJohn Baldwin 		goto out;
672744bfb21SJohn Baldwin 
673744bfb21SJohn Baldwin 	rc = udp_set_kernel_tunneling(so4, wg_input, NULL, sc);
674744bfb21SJohn Baldwin 	/*
675744bfb21SJohn Baldwin 	 * udp_set_kernel_tunneling can only fail if there is already a tunneling function set.
676744bfb21SJohn Baldwin 	 * This should never happen with a new socket.
677744bfb21SJohn Baldwin 	 */
678744bfb21SJohn Baldwin 	MPASS(rc == 0);
679744bfb21SJohn Baldwin #endif
680744bfb21SJohn Baldwin 
681744bfb21SJohn Baldwin #ifdef INET6
682744bfb21SJohn Baldwin 	rc = socreate(AF_INET6, &so6, SOCK_DGRAM, IPPROTO_UDP, cred, curthread);
683744bfb21SJohn Baldwin 	if (rc)
684744bfb21SJohn Baldwin 		goto out;
685744bfb21SJohn Baldwin 	rc = udp_set_kernel_tunneling(so6, wg_input, NULL, sc);
686744bfb21SJohn Baldwin 	MPASS(rc == 0);
687744bfb21SJohn Baldwin #endif
688744bfb21SJohn Baldwin 
689744bfb21SJohn Baldwin 	if (sc->sc_socket.so_user_cookie) {
690744bfb21SJohn Baldwin 		rc = wg_socket_set_sockopt(so4, so6, SO_USER_COOKIE, &sc->sc_socket.so_user_cookie, sizeof(sc->sc_socket.so_user_cookie));
691744bfb21SJohn Baldwin 		if (rc)
692744bfb21SJohn Baldwin 			goto out;
693744bfb21SJohn Baldwin 	}
694744bfb21SJohn Baldwin 	rc = wg_socket_set_sockopt(so4, so6, SO_SETFIB, &sc->sc_socket.so_fibnum, sizeof(sc->sc_socket.so_fibnum));
695744bfb21SJohn Baldwin 	if (rc)
696744bfb21SJohn Baldwin 		goto out;
697744bfb21SJohn Baldwin 
698744bfb21SJohn Baldwin 	rc = wg_socket_bind(&so4, &so6, &port);
699744bfb21SJohn Baldwin 	if (!rc) {
700744bfb21SJohn Baldwin 		sc->sc_socket.so_port = port;
701744bfb21SJohn Baldwin 		wg_socket_set(sc, so4, so6);
702744bfb21SJohn Baldwin 	}
703744bfb21SJohn Baldwin out:
704744bfb21SJohn Baldwin 	if (rc) {
705744bfb21SJohn Baldwin 		if (so4 != NULL)
706744bfb21SJohn Baldwin 			soclose(so4);
707744bfb21SJohn Baldwin 		if (so6 != NULL)
708744bfb21SJohn Baldwin 			soclose(so6);
709744bfb21SJohn Baldwin 	}
710744bfb21SJohn Baldwin 	return (rc);
711744bfb21SJohn Baldwin }
712744bfb21SJohn Baldwin 
wg_socket_set_sockopt(struct socket * so4,struct socket * so6,int name,void * val,size_t len)713744bfb21SJohn Baldwin static int wg_socket_set_sockopt(struct socket *so4, struct socket *so6, int name, void *val, size_t len)
714744bfb21SJohn Baldwin {
715744bfb21SJohn Baldwin 	int ret4 = 0, ret6 = 0;
716744bfb21SJohn Baldwin 	struct sockopt sopt = {
717744bfb21SJohn Baldwin 		.sopt_dir = SOPT_SET,
718744bfb21SJohn Baldwin 		.sopt_level = SOL_SOCKET,
719744bfb21SJohn Baldwin 		.sopt_name = name,
720744bfb21SJohn Baldwin 		.sopt_val = val,
721744bfb21SJohn Baldwin 		.sopt_valsize = len
722744bfb21SJohn Baldwin 	};
723744bfb21SJohn Baldwin 
724744bfb21SJohn Baldwin 	if (so4)
725744bfb21SJohn Baldwin 		ret4 = sosetopt(so4, &sopt);
726744bfb21SJohn Baldwin 	if (so6)
727744bfb21SJohn Baldwin 		ret6 = sosetopt(so6, &sopt);
728744bfb21SJohn Baldwin 	return (ret4 ?: ret6);
729744bfb21SJohn Baldwin }
730744bfb21SJohn Baldwin 
wg_socket_set_cookie(struct wg_softc * sc,uint32_t user_cookie)731744bfb21SJohn Baldwin static int wg_socket_set_cookie(struct wg_softc *sc, uint32_t user_cookie)
732744bfb21SJohn Baldwin {
733744bfb21SJohn Baldwin 	struct wg_socket *so = &sc->sc_socket;
734744bfb21SJohn Baldwin 	int ret;
735744bfb21SJohn Baldwin 
736744bfb21SJohn Baldwin 	sx_assert(&sc->sc_lock, SX_XLOCKED);
737744bfb21SJohn Baldwin 	ret = wg_socket_set_sockopt(so->so_so4, so->so_so6, SO_USER_COOKIE, &user_cookie, sizeof(user_cookie));
738744bfb21SJohn Baldwin 	if (!ret)
739744bfb21SJohn Baldwin 		so->so_user_cookie = user_cookie;
740744bfb21SJohn Baldwin 	return (ret);
741744bfb21SJohn Baldwin }
742744bfb21SJohn Baldwin 
wg_socket_set_fibnum(struct wg_softc * sc,int fibnum)743744bfb21SJohn Baldwin static int wg_socket_set_fibnum(struct wg_softc *sc, int fibnum)
744744bfb21SJohn Baldwin {
745744bfb21SJohn Baldwin 	struct wg_socket *so = &sc->sc_socket;
746744bfb21SJohn Baldwin 	int ret;
747744bfb21SJohn Baldwin 
748744bfb21SJohn Baldwin 	sx_assert(&sc->sc_lock, SX_XLOCKED);
749744bfb21SJohn Baldwin 
750744bfb21SJohn Baldwin 	ret = wg_socket_set_sockopt(so->so_so4, so->so_so6, SO_SETFIB, &fibnum, sizeof(fibnum));
751744bfb21SJohn Baldwin 	if (!ret)
752744bfb21SJohn Baldwin 		so->so_fibnum = fibnum;
753744bfb21SJohn Baldwin 	return (ret);
754744bfb21SJohn Baldwin }
755744bfb21SJohn Baldwin 
756744bfb21SJohn Baldwin static void
wg_socket_uninit(struct wg_softc * sc)757744bfb21SJohn Baldwin wg_socket_uninit(struct wg_softc *sc)
758744bfb21SJohn Baldwin {
759744bfb21SJohn Baldwin 	wg_socket_set(sc, NULL, NULL);
760744bfb21SJohn Baldwin }
761744bfb21SJohn Baldwin 
762744bfb21SJohn Baldwin static void
wg_socket_set(struct wg_softc * sc,struct socket * new_so4,struct socket * new_so6)763744bfb21SJohn Baldwin wg_socket_set(struct wg_softc *sc, struct socket *new_so4, struct socket *new_so6)
764744bfb21SJohn Baldwin {
765744bfb21SJohn Baldwin 	struct wg_socket *so = &sc->sc_socket;
766744bfb21SJohn Baldwin 	struct socket *so4, *so6;
767744bfb21SJohn Baldwin 
768744bfb21SJohn Baldwin 	sx_assert(&sc->sc_lock, SX_XLOCKED);
769744bfb21SJohn Baldwin 
770e32e1a16SJohn Baldwin 	so4 = atomic_load_ptr(&so->so_so4);
771e32e1a16SJohn Baldwin 	so6 = atomic_load_ptr(&so->so_so6);
772e32e1a16SJohn Baldwin 	atomic_store_ptr(&so->so_so4, new_so4);
773e32e1a16SJohn Baldwin 	atomic_store_ptr(&so->so_so6, new_so6);
774744bfb21SJohn Baldwin 
775744bfb21SJohn Baldwin 	if (!so4 && !so6)
776744bfb21SJohn Baldwin 		return;
777744bfb21SJohn Baldwin 	NET_EPOCH_WAIT();
778744bfb21SJohn Baldwin 	if (so4)
779744bfb21SJohn Baldwin 		soclose(so4);
780744bfb21SJohn Baldwin 	if (so6)
781744bfb21SJohn Baldwin 		soclose(so6);
782744bfb21SJohn Baldwin }
783744bfb21SJohn Baldwin 
784744bfb21SJohn Baldwin static int
wg_socket_bind(struct socket ** in_so4,struct socket ** in_so6,in_port_t * requested_port)785744bfb21SJohn Baldwin wg_socket_bind(struct socket **in_so4, struct socket **in_so6, in_port_t *requested_port)
786744bfb21SJohn Baldwin {
787744bfb21SJohn Baldwin 	struct socket *so4 = *in_so4, *so6 = *in_so6;
788744bfb21SJohn Baldwin 	int ret4 = 0, ret6 = 0;
789744bfb21SJohn Baldwin 	in_port_t port = *requested_port;
790744bfb21SJohn Baldwin 	struct sockaddr_in sin = {
791744bfb21SJohn Baldwin 		.sin_len = sizeof(struct sockaddr_in),
792744bfb21SJohn Baldwin 		.sin_family = AF_INET,
793744bfb21SJohn Baldwin 		.sin_port = htons(port)
794744bfb21SJohn Baldwin 	};
795744bfb21SJohn Baldwin 	struct sockaddr_in6 sin6 = {
796744bfb21SJohn Baldwin 		.sin6_len = sizeof(struct sockaddr_in6),
797744bfb21SJohn Baldwin 		.sin6_family = AF_INET6,
798744bfb21SJohn Baldwin 		.sin6_port = htons(port)
799744bfb21SJohn Baldwin 	};
800744bfb21SJohn Baldwin 
801744bfb21SJohn Baldwin 	if (so4) {
802744bfb21SJohn Baldwin 		ret4 = sobind(so4, (struct sockaddr *)&sin, curthread);
803744bfb21SJohn Baldwin 		if (ret4 && ret4 != EADDRNOTAVAIL)
804744bfb21SJohn Baldwin 			return (ret4);
805744bfb21SJohn Baldwin 		if (!ret4 && !sin.sin_port) {
8060fac350cSGleb Smirnoff 			struct sockaddr_in bound_sin =
8070fac350cSGleb Smirnoff 			    { .sin_len = sizeof(bound_sin) };
8080fac350cSGleb Smirnoff 			int ret;
8090fac350cSGleb Smirnoff 
8100fac350cSGleb Smirnoff 			ret = sosockaddr(so4, (struct sockaddr *)&bound_sin);
811744bfb21SJohn Baldwin 			if (ret)
812744bfb21SJohn Baldwin 				return (ret);
8130fac350cSGleb Smirnoff 			port = ntohs(bound_sin.sin_port);
8140fac350cSGleb Smirnoff 			sin6.sin6_port = bound_sin.sin_port;
815744bfb21SJohn Baldwin 		}
816744bfb21SJohn Baldwin 	}
817744bfb21SJohn Baldwin 
818744bfb21SJohn Baldwin 	if (so6) {
819744bfb21SJohn Baldwin 		ret6 = sobind(so6, (struct sockaddr *)&sin6, curthread);
820744bfb21SJohn Baldwin 		if (ret6 && ret6 != EADDRNOTAVAIL)
821744bfb21SJohn Baldwin 			return (ret6);
822744bfb21SJohn Baldwin 		if (!ret6 && !sin6.sin6_port) {
8230fac350cSGleb Smirnoff 			struct sockaddr_in6 bound_sin6 =
8240fac350cSGleb Smirnoff 			    { .sin6_len = sizeof(bound_sin6) };
8250fac350cSGleb Smirnoff 			int ret;
8260fac350cSGleb Smirnoff 
8270fac350cSGleb Smirnoff 			ret = sosockaddr(so6, (struct sockaddr *)&bound_sin6);
828744bfb21SJohn Baldwin 			if (ret)
829744bfb21SJohn Baldwin 				return (ret);
8300fac350cSGleb Smirnoff 			port = ntohs(bound_sin6.sin6_port);
831744bfb21SJohn Baldwin 		}
832744bfb21SJohn Baldwin 	}
833744bfb21SJohn Baldwin 
834744bfb21SJohn Baldwin 	if (ret4 && ret6)
835744bfb21SJohn Baldwin 		return (ret4);
836744bfb21SJohn Baldwin 	*requested_port = port;
837744bfb21SJohn Baldwin 	if (ret4 && !ret6 && so4) {
838744bfb21SJohn Baldwin 		soclose(so4);
839744bfb21SJohn Baldwin 		*in_so4 = NULL;
840744bfb21SJohn Baldwin 	} else if (ret6 && !ret4 && so6) {
841744bfb21SJohn Baldwin 		soclose(so6);
842744bfb21SJohn Baldwin 		*in_so6 = NULL;
843744bfb21SJohn Baldwin 	}
844744bfb21SJohn Baldwin 	return (0);
845744bfb21SJohn Baldwin }
846744bfb21SJohn Baldwin 
847744bfb21SJohn Baldwin static int
wg_send(struct wg_softc * sc,struct wg_endpoint * e,struct mbuf * m)848744bfb21SJohn Baldwin wg_send(struct wg_softc *sc, struct wg_endpoint *e, struct mbuf *m)
849744bfb21SJohn Baldwin {
850744bfb21SJohn Baldwin 	struct epoch_tracker et;
851744bfb21SJohn Baldwin 	struct sockaddr *sa;
852744bfb21SJohn Baldwin 	struct wg_socket *so = &sc->sc_socket;
853744bfb21SJohn Baldwin 	struct socket *so4, *so6;
854744bfb21SJohn Baldwin 	struct mbuf *control = NULL;
855744bfb21SJohn Baldwin 	int ret = 0;
856744bfb21SJohn Baldwin 	size_t len = m->m_pkthdr.len;
857744bfb21SJohn Baldwin 
858744bfb21SJohn Baldwin 	/* Get local control address before locking */
859744bfb21SJohn Baldwin 	if (e->e_remote.r_sa.sa_family == AF_INET) {
860744bfb21SJohn Baldwin 		if (e->e_local.l_in.s_addr != INADDR_ANY)
861744bfb21SJohn Baldwin 			control = sbcreatecontrol((caddr_t)&e->e_local.l_in,
862744bfb21SJohn Baldwin 			    sizeof(struct in_addr), IP_SENDSRCADDR,
863744bfb21SJohn Baldwin 			    IPPROTO_IP, M_NOWAIT);
864744bfb21SJohn Baldwin #ifdef INET6
865744bfb21SJohn Baldwin 	} else if (e->e_remote.r_sa.sa_family == AF_INET6) {
866744bfb21SJohn Baldwin 		if (!IN6_IS_ADDR_UNSPECIFIED(&e->e_local.l_in6))
867744bfb21SJohn Baldwin 			control = sbcreatecontrol((caddr_t)&e->e_local.l_pktinfo6,
868744bfb21SJohn Baldwin 			    sizeof(struct in6_pktinfo), IPV6_PKTINFO,
869744bfb21SJohn Baldwin 			    IPPROTO_IPV6, M_NOWAIT);
870744bfb21SJohn Baldwin #endif
871744bfb21SJohn Baldwin 	} else {
872744bfb21SJohn Baldwin 		m_freem(m);
873744bfb21SJohn Baldwin 		return (EAFNOSUPPORT);
874744bfb21SJohn Baldwin 	}
875744bfb21SJohn Baldwin 
876744bfb21SJohn Baldwin 	/* Get remote address */
877744bfb21SJohn Baldwin 	sa = &e->e_remote.r_sa;
878744bfb21SJohn Baldwin 
879744bfb21SJohn Baldwin 	NET_EPOCH_ENTER(et);
880e32e1a16SJohn Baldwin 	so4 = atomic_load_ptr(&so->so_so4);
881e32e1a16SJohn Baldwin 	so6 = atomic_load_ptr(&so->so_so6);
882744bfb21SJohn Baldwin 	if (e->e_remote.r_sa.sa_family == AF_INET && so4 != NULL)
883744bfb21SJohn Baldwin 		ret = sosend(so4, sa, NULL, m, control, 0, curthread);
884744bfb21SJohn Baldwin 	else if (e->e_remote.r_sa.sa_family == AF_INET6 && so6 != NULL)
885744bfb21SJohn Baldwin 		ret = sosend(so6, sa, NULL, m, control, 0, curthread);
886744bfb21SJohn Baldwin 	else {
887744bfb21SJohn Baldwin 		ret = ENOTCONN;
888744bfb21SJohn Baldwin 		m_freem(control);
889744bfb21SJohn Baldwin 		m_freem(m);
890744bfb21SJohn Baldwin 	}
891744bfb21SJohn Baldwin 	NET_EPOCH_EXIT(et);
892744bfb21SJohn Baldwin 	if (ret == 0) {
893744bfb21SJohn Baldwin 		if_inc_counter(sc->sc_ifp, IFCOUNTER_OPACKETS, 1);
894744bfb21SJohn Baldwin 		if_inc_counter(sc->sc_ifp, IFCOUNTER_OBYTES, len);
895744bfb21SJohn Baldwin 	}
896744bfb21SJohn Baldwin 	return (ret);
897744bfb21SJohn Baldwin }
898744bfb21SJohn Baldwin 
899744bfb21SJohn Baldwin static void
wg_send_buf(struct wg_softc * sc,struct wg_endpoint * e,uint8_t * buf,size_t len)900744bfb21SJohn Baldwin wg_send_buf(struct wg_softc *sc, struct wg_endpoint *e, uint8_t *buf, size_t len)
901744bfb21SJohn Baldwin {
902744bfb21SJohn Baldwin 	struct mbuf	*m;
903744bfb21SJohn Baldwin 	int		 ret = 0;
904744bfb21SJohn Baldwin 	bool		 retried = false;
905744bfb21SJohn Baldwin 
906744bfb21SJohn Baldwin retry:
907744bfb21SJohn Baldwin 	m = m_get2(len, M_NOWAIT, MT_DATA, M_PKTHDR);
908744bfb21SJohn Baldwin 	if (!m) {
909744bfb21SJohn Baldwin 		ret = ENOMEM;
910744bfb21SJohn Baldwin 		goto out;
911744bfb21SJohn Baldwin 	}
912744bfb21SJohn Baldwin 	m_copyback(m, 0, len, buf);
913744bfb21SJohn Baldwin 
914744bfb21SJohn Baldwin 	if (ret == 0) {
915744bfb21SJohn Baldwin 		ret = wg_send(sc, e, m);
916744bfb21SJohn Baldwin 		/* Retry if we couldn't bind to e->e_local */
917744bfb21SJohn Baldwin 		if (ret == EADDRNOTAVAIL && !retried) {
918744bfb21SJohn Baldwin 			bzero(&e->e_local, sizeof(e->e_local));
919744bfb21SJohn Baldwin 			retried = true;
920744bfb21SJohn Baldwin 			goto retry;
921744bfb21SJohn Baldwin 		}
922744bfb21SJohn Baldwin 	} else {
923744bfb21SJohn Baldwin 		ret = wg_send(sc, e, m);
924744bfb21SJohn Baldwin 	}
925744bfb21SJohn Baldwin out:
926744bfb21SJohn Baldwin 	if (ret)
927744bfb21SJohn Baldwin 		DPRINTF(sc, "Unable to send packet: %d\n", ret);
928744bfb21SJohn Baldwin }
929744bfb21SJohn Baldwin 
930744bfb21SJohn Baldwin /* Timers */
931744bfb21SJohn Baldwin static void
wg_timers_enable(struct wg_peer * peer)932744bfb21SJohn Baldwin wg_timers_enable(struct wg_peer *peer)
933744bfb21SJohn Baldwin {
934e32e1a16SJohn Baldwin 	atomic_store_bool(&peer->p_enabled, true);
935744bfb21SJohn Baldwin 	wg_timers_run_persistent_keepalive(peer);
936744bfb21SJohn Baldwin }
937744bfb21SJohn Baldwin 
938744bfb21SJohn Baldwin static void
wg_timers_disable(struct wg_peer * peer)939744bfb21SJohn Baldwin wg_timers_disable(struct wg_peer *peer)
940744bfb21SJohn Baldwin {
941744bfb21SJohn Baldwin 	/* By setting p_enabled = false, then calling NET_EPOCH_WAIT, we can be
942744bfb21SJohn Baldwin 	 * sure no new handshakes are created after the wait. This is because
943744bfb21SJohn Baldwin 	 * all callout_resets (scheduling the callout) are guarded by
944744bfb21SJohn Baldwin 	 * p_enabled. We can be sure all sections that read p_enabled and then
945744bfb21SJohn Baldwin 	 * optionally call callout_reset are finished as they are surrounded by
946744bfb21SJohn Baldwin 	 * NET_EPOCH_{ENTER,EXIT}.
947744bfb21SJohn Baldwin 	 *
948744bfb21SJohn Baldwin 	 * However, as new callouts may be scheduled during NET_EPOCH_WAIT (but
949744bfb21SJohn Baldwin 	 * not after), we stop all callouts leaving no callouts active.
950744bfb21SJohn Baldwin 	 *
951744bfb21SJohn Baldwin 	 * We should also pull NET_EPOCH_WAIT out of the FOREACH(peer) loops, but the
952744bfb21SJohn Baldwin 	 * performance impact is acceptable for the time being. */
953e32e1a16SJohn Baldwin 	atomic_store_bool(&peer->p_enabled, false);
954744bfb21SJohn Baldwin 	NET_EPOCH_WAIT();
955e32e1a16SJohn Baldwin 	atomic_store_bool(&peer->p_need_another_keepalive, false);
956744bfb21SJohn Baldwin 
957744bfb21SJohn Baldwin 	callout_stop(&peer->p_new_handshake);
958744bfb21SJohn Baldwin 	callout_stop(&peer->p_send_keepalive);
959744bfb21SJohn Baldwin 	callout_stop(&peer->p_retry_handshake);
960744bfb21SJohn Baldwin 	callout_stop(&peer->p_persistent_keepalive);
961744bfb21SJohn Baldwin 	callout_stop(&peer->p_zero_key_material);
962744bfb21SJohn Baldwin }
963744bfb21SJohn Baldwin 
964744bfb21SJohn Baldwin static void
wg_timers_set_persistent_keepalive(struct wg_peer * peer,uint16_t interval)965744bfb21SJohn Baldwin wg_timers_set_persistent_keepalive(struct wg_peer *peer, uint16_t interval)
966744bfb21SJohn Baldwin {
967744bfb21SJohn Baldwin 	struct epoch_tracker et;
968744bfb21SJohn Baldwin 	if (interval != peer->p_persistent_keepalive_interval) {
969e32e1a16SJohn Baldwin 		atomic_store_16(&peer->p_persistent_keepalive_interval, interval);
970744bfb21SJohn Baldwin 		NET_EPOCH_ENTER(et);
971e32e1a16SJohn Baldwin 		if (atomic_load_bool(&peer->p_enabled))
972744bfb21SJohn Baldwin 			wg_timers_run_persistent_keepalive(peer);
973744bfb21SJohn Baldwin 		NET_EPOCH_EXIT(et);
974744bfb21SJohn Baldwin 	}
975744bfb21SJohn Baldwin }
976744bfb21SJohn Baldwin 
977744bfb21SJohn Baldwin static void
wg_timers_get_last_handshake(struct wg_peer * peer,struct wg_timespec64 * time)978744bfb21SJohn Baldwin wg_timers_get_last_handshake(struct wg_peer *peer, struct wg_timespec64 *time)
979744bfb21SJohn Baldwin {
980744bfb21SJohn Baldwin 	mtx_lock(&peer->p_handshake_mtx);
981744bfb21SJohn Baldwin 	time->tv_sec = peer->p_handshake_complete.tv_sec;
982744bfb21SJohn Baldwin 	time->tv_nsec = peer->p_handshake_complete.tv_nsec;
983744bfb21SJohn Baldwin 	mtx_unlock(&peer->p_handshake_mtx);
984744bfb21SJohn Baldwin }
985744bfb21SJohn Baldwin 
986744bfb21SJohn Baldwin static void
wg_timers_event_data_sent(struct wg_peer * peer)987744bfb21SJohn Baldwin wg_timers_event_data_sent(struct wg_peer *peer)
988744bfb21SJohn Baldwin {
989744bfb21SJohn Baldwin 	struct epoch_tracker et;
990744bfb21SJohn Baldwin 	NET_EPOCH_ENTER(et);
991e32e1a16SJohn Baldwin 	if (atomic_load_bool(&peer->p_enabled) &&
992e32e1a16SJohn Baldwin 	    !callout_pending(&peer->p_new_handshake))
993744bfb21SJohn Baldwin 		callout_reset(&peer->p_new_handshake, MSEC_2_TICKS(
994744bfb21SJohn Baldwin 		    NEW_HANDSHAKE_TIMEOUT * 1000 +
995744bfb21SJohn Baldwin 		    arc4random_uniform(REKEY_TIMEOUT_JITTER)),
996744bfb21SJohn Baldwin 		    wg_timers_run_new_handshake, peer);
997744bfb21SJohn Baldwin 	NET_EPOCH_EXIT(et);
998744bfb21SJohn Baldwin }
999744bfb21SJohn Baldwin 
1000744bfb21SJohn Baldwin static void
wg_timers_event_data_received(struct wg_peer * peer)1001744bfb21SJohn Baldwin wg_timers_event_data_received(struct wg_peer *peer)
1002744bfb21SJohn Baldwin {
1003744bfb21SJohn Baldwin 	struct epoch_tracker et;
1004744bfb21SJohn Baldwin 	NET_EPOCH_ENTER(et);
1005e32e1a16SJohn Baldwin 	if (atomic_load_bool(&peer->p_enabled)) {
1006744bfb21SJohn Baldwin 		if (!callout_pending(&peer->p_send_keepalive))
1007744bfb21SJohn Baldwin 			callout_reset(&peer->p_send_keepalive,
1008744bfb21SJohn Baldwin 			    MSEC_2_TICKS(KEEPALIVE_TIMEOUT * 1000),
1009744bfb21SJohn Baldwin 			    wg_timers_run_send_keepalive, peer);
1010744bfb21SJohn Baldwin 		else
1011e32e1a16SJohn Baldwin 			atomic_store_bool(&peer->p_need_another_keepalive,
1012e32e1a16SJohn Baldwin 			    true);
1013744bfb21SJohn Baldwin 	}
1014744bfb21SJohn Baldwin 	NET_EPOCH_EXIT(et);
1015744bfb21SJohn Baldwin }
1016744bfb21SJohn Baldwin 
1017744bfb21SJohn Baldwin static void
wg_timers_event_any_authenticated_packet_sent(struct wg_peer * peer)1018744bfb21SJohn Baldwin wg_timers_event_any_authenticated_packet_sent(struct wg_peer *peer)
1019744bfb21SJohn Baldwin {
1020744bfb21SJohn Baldwin 	callout_stop(&peer->p_send_keepalive);
1021744bfb21SJohn Baldwin }
1022744bfb21SJohn Baldwin 
1023744bfb21SJohn Baldwin static void
wg_timers_event_any_authenticated_packet_received(struct wg_peer * peer)1024744bfb21SJohn Baldwin wg_timers_event_any_authenticated_packet_received(struct wg_peer *peer)
1025744bfb21SJohn Baldwin {
1026744bfb21SJohn Baldwin 	callout_stop(&peer->p_new_handshake);
1027744bfb21SJohn Baldwin }
1028744bfb21SJohn Baldwin 
1029744bfb21SJohn Baldwin static void
wg_timers_event_any_authenticated_packet_traversal(struct wg_peer * peer)1030744bfb21SJohn Baldwin wg_timers_event_any_authenticated_packet_traversal(struct wg_peer *peer)
1031744bfb21SJohn Baldwin {
1032744bfb21SJohn Baldwin 	struct epoch_tracker et;
1033744bfb21SJohn Baldwin 	uint16_t interval;
1034744bfb21SJohn Baldwin 	NET_EPOCH_ENTER(et);
1035e32e1a16SJohn Baldwin 	interval = atomic_load_16(&peer->p_persistent_keepalive_interval);
1036e32e1a16SJohn Baldwin 	if (atomic_load_bool(&peer->p_enabled) && interval > 0)
1037744bfb21SJohn Baldwin 		callout_reset(&peer->p_persistent_keepalive,
1038744bfb21SJohn Baldwin 		     MSEC_2_TICKS(interval * 1000),
1039744bfb21SJohn Baldwin 		     wg_timers_run_persistent_keepalive, peer);
1040744bfb21SJohn Baldwin 	NET_EPOCH_EXIT(et);
1041744bfb21SJohn Baldwin }
1042744bfb21SJohn Baldwin 
1043744bfb21SJohn Baldwin static void
wg_timers_event_handshake_initiated(struct wg_peer * peer)1044744bfb21SJohn Baldwin wg_timers_event_handshake_initiated(struct wg_peer *peer)
1045744bfb21SJohn Baldwin {
1046744bfb21SJohn Baldwin 	struct epoch_tracker et;
1047744bfb21SJohn Baldwin 	NET_EPOCH_ENTER(et);
1048e32e1a16SJohn Baldwin 	if (atomic_load_bool(&peer->p_enabled))
1049744bfb21SJohn Baldwin 		callout_reset(&peer->p_retry_handshake, MSEC_2_TICKS(
1050744bfb21SJohn Baldwin 		    REKEY_TIMEOUT * 1000 +
1051744bfb21SJohn Baldwin 		    arc4random_uniform(REKEY_TIMEOUT_JITTER)),
1052744bfb21SJohn Baldwin 		    wg_timers_run_retry_handshake, peer);
1053744bfb21SJohn Baldwin 	NET_EPOCH_EXIT(et);
1054744bfb21SJohn Baldwin }
1055744bfb21SJohn Baldwin 
1056744bfb21SJohn Baldwin static void
wg_timers_event_handshake_complete(struct wg_peer * peer)1057744bfb21SJohn Baldwin wg_timers_event_handshake_complete(struct wg_peer *peer)
1058744bfb21SJohn Baldwin {
1059744bfb21SJohn Baldwin 	struct epoch_tracker et;
1060744bfb21SJohn Baldwin 	NET_EPOCH_ENTER(et);
1061e32e1a16SJohn Baldwin 	if (atomic_load_bool(&peer->p_enabled)) {
1062744bfb21SJohn Baldwin 		mtx_lock(&peer->p_handshake_mtx);
1063744bfb21SJohn Baldwin 		callout_stop(&peer->p_retry_handshake);
1064744bfb21SJohn Baldwin 		peer->p_handshake_retries = 0;
1065744bfb21SJohn Baldwin 		getnanotime(&peer->p_handshake_complete);
1066744bfb21SJohn Baldwin 		mtx_unlock(&peer->p_handshake_mtx);
1067744bfb21SJohn Baldwin 		wg_timers_run_send_keepalive(peer);
1068744bfb21SJohn Baldwin 	}
1069744bfb21SJohn Baldwin 	NET_EPOCH_EXIT(et);
1070744bfb21SJohn Baldwin }
1071744bfb21SJohn Baldwin 
1072744bfb21SJohn Baldwin static void
wg_timers_event_session_derived(struct wg_peer * peer)1073744bfb21SJohn Baldwin wg_timers_event_session_derived(struct wg_peer *peer)
1074744bfb21SJohn Baldwin {
1075744bfb21SJohn Baldwin 	struct epoch_tracker et;
1076744bfb21SJohn Baldwin 	NET_EPOCH_ENTER(et);
1077e32e1a16SJohn Baldwin 	if (atomic_load_bool(&peer->p_enabled))
1078744bfb21SJohn Baldwin 		callout_reset(&peer->p_zero_key_material,
1079744bfb21SJohn Baldwin 		    MSEC_2_TICKS(REJECT_AFTER_TIME * 3 * 1000),
1080744bfb21SJohn Baldwin 		    wg_timers_run_zero_key_material, peer);
1081744bfb21SJohn Baldwin 	NET_EPOCH_EXIT(et);
1082744bfb21SJohn Baldwin }
1083744bfb21SJohn Baldwin 
1084744bfb21SJohn Baldwin static void
wg_timers_event_want_initiation(struct wg_peer * peer)1085744bfb21SJohn Baldwin wg_timers_event_want_initiation(struct wg_peer *peer)
1086744bfb21SJohn Baldwin {
1087744bfb21SJohn Baldwin 	struct epoch_tracker et;
1088744bfb21SJohn Baldwin 	NET_EPOCH_ENTER(et);
1089e32e1a16SJohn Baldwin 	if (atomic_load_bool(&peer->p_enabled))
1090744bfb21SJohn Baldwin 		wg_timers_run_send_initiation(peer, false);
1091744bfb21SJohn Baldwin 	NET_EPOCH_EXIT(et);
1092744bfb21SJohn Baldwin }
1093744bfb21SJohn Baldwin 
1094744bfb21SJohn Baldwin static void
wg_timers_run_send_initiation(struct wg_peer * peer,bool is_retry)1095744bfb21SJohn Baldwin wg_timers_run_send_initiation(struct wg_peer *peer, bool is_retry)
1096744bfb21SJohn Baldwin {
1097744bfb21SJohn Baldwin 	if (!is_retry)
1098744bfb21SJohn Baldwin 		peer->p_handshake_retries = 0;
1099744bfb21SJohn Baldwin 	if (noise_remote_initiation_expired(peer->p_remote) == ETIMEDOUT)
1100744bfb21SJohn Baldwin 		wg_send_initiation(peer);
1101744bfb21SJohn Baldwin }
1102744bfb21SJohn Baldwin 
1103744bfb21SJohn Baldwin static void
wg_timers_run_retry_handshake(void * _peer)1104744bfb21SJohn Baldwin wg_timers_run_retry_handshake(void *_peer)
1105744bfb21SJohn Baldwin {
1106744bfb21SJohn Baldwin 	struct epoch_tracker et;
1107744bfb21SJohn Baldwin 	struct wg_peer *peer = _peer;
1108744bfb21SJohn Baldwin 
1109744bfb21SJohn Baldwin 	mtx_lock(&peer->p_handshake_mtx);
1110744bfb21SJohn Baldwin 	if (peer->p_handshake_retries <= MAX_TIMER_HANDSHAKES) {
1111744bfb21SJohn Baldwin 		peer->p_handshake_retries++;
1112744bfb21SJohn Baldwin 		mtx_unlock(&peer->p_handshake_mtx);
1113744bfb21SJohn Baldwin 
1114744bfb21SJohn Baldwin 		DPRINTF(peer->p_sc, "Handshake for peer %" PRIu64 " did not complete "
1115744bfb21SJohn Baldwin 		    "after %d seconds, retrying (try %d)\n", peer->p_id,
1116744bfb21SJohn Baldwin 		    REKEY_TIMEOUT, peer->p_handshake_retries + 1);
1117744bfb21SJohn Baldwin 		wg_peer_clear_src(peer);
1118744bfb21SJohn Baldwin 		wg_timers_run_send_initiation(peer, true);
1119744bfb21SJohn Baldwin 	} else {
1120744bfb21SJohn Baldwin 		mtx_unlock(&peer->p_handshake_mtx);
1121744bfb21SJohn Baldwin 
1122744bfb21SJohn Baldwin 		DPRINTF(peer->p_sc, "Handshake for peer %" PRIu64 " did not complete "
1123744bfb21SJohn Baldwin 		    "after %d retries, giving up\n", peer->p_id,
1124744bfb21SJohn Baldwin 		    MAX_TIMER_HANDSHAKES + 2);
1125744bfb21SJohn Baldwin 
1126744bfb21SJohn Baldwin 		callout_stop(&peer->p_send_keepalive);
1127744bfb21SJohn Baldwin 		wg_queue_purge(&peer->p_stage_queue);
1128744bfb21SJohn Baldwin 		NET_EPOCH_ENTER(et);
1129e32e1a16SJohn Baldwin 		if (atomic_load_bool(&peer->p_enabled) &&
1130744bfb21SJohn Baldwin 		    !callout_pending(&peer->p_zero_key_material))
1131744bfb21SJohn Baldwin 			callout_reset(&peer->p_zero_key_material,
1132744bfb21SJohn Baldwin 			    MSEC_2_TICKS(REJECT_AFTER_TIME * 3 * 1000),
1133744bfb21SJohn Baldwin 			    wg_timers_run_zero_key_material, peer);
1134744bfb21SJohn Baldwin 		NET_EPOCH_EXIT(et);
1135744bfb21SJohn Baldwin 	}
1136744bfb21SJohn Baldwin }
1137744bfb21SJohn Baldwin 
1138744bfb21SJohn Baldwin static void
wg_timers_run_send_keepalive(void * _peer)1139744bfb21SJohn Baldwin wg_timers_run_send_keepalive(void *_peer)
1140744bfb21SJohn Baldwin {
1141744bfb21SJohn Baldwin 	struct epoch_tracker et;
1142744bfb21SJohn Baldwin 	struct wg_peer *peer = _peer;
1143744bfb21SJohn Baldwin 
1144744bfb21SJohn Baldwin 	wg_send_keepalive(peer);
1145744bfb21SJohn Baldwin 	NET_EPOCH_ENTER(et);
1146e32e1a16SJohn Baldwin 	if (atomic_load_bool(&peer->p_enabled) &&
1147e32e1a16SJohn Baldwin 	    atomic_load_bool(&peer->p_need_another_keepalive)) {
1148e32e1a16SJohn Baldwin 		atomic_store_bool(&peer->p_need_another_keepalive, false);
1149744bfb21SJohn Baldwin 		callout_reset(&peer->p_send_keepalive,
1150744bfb21SJohn Baldwin 		    MSEC_2_TICKS(KEEPALIVE_TIMEOUT * 1000),
1151744bfb21SJohn Baldwin 		    wg_timers_run_send_keepalive, peer);
1152744bfb21SJohn Baldwin 	}
1153744bfb21SJohn Baldwin 	NET_EPOCH_EXIT(et);
1154744bfb21SJohn Baldwin }
1155744bfb21SJohn Baldwin 
1156744bfb21SJohn Baldwin static void
wg_timers_run_new_handshake(void * _peer)1157744bfb21SJohn Baldwin wg_timers_run_new_handshake(void *_peer)
1158744bfb21SJohn Baldwin {
1159744bfb21SJohn Baldwin 	struct wg_peer *peer = _peer;
1160744bfb21SJohn Baldwin 
1161744bfb21SJohn Baldwin 	DPRINTF(peer->p_sc, "Retrying handshake with peer %" PRIu64 " because we "
1162744bfb21SJohn Baldwin 	    "stopped hearing back after %d seconds\n",
1163744bfb21SJohn Baldwin 	    peer->p_id, NEW_HANDSHAKE_TIMEOUT);
1164744bfb21SJohn Baldwin 
1165744bfb21SJohn Baldwin 	wg_peer_clear_src(peer);
1166744bfb21SJohn Baldwin 	wg_timers_run_send_initiation(peer, false);
1167744bfb21SJohn Baldwin }
1168744bfb21SJohn Baldwin 
1169744bfb21SJohn Baldwin static void
wg_timers_run_zero_key_material(void * _peer)1170744bfb21SJohn Baldwin wg_timers_run_zero_key_material(void *_peer)
1171744bfb21SJohn Baldwin {
1172744bfb21SJohn Baldwin 	struct wg_peer *peer = _peer;
1173744bfb21SJohn Baldwin 
1174744bfb21SJohn Baldwin 	DPRINTF(peer->p_sc, "Zeroing out keys for peer %" PRIu64 ", since we "
1175744bfb21SJohn Baldwin 	    "haven't received a new one in %d seconds\n",
1176744bfb21SJohn Baldwin 	    peer->p_id, REJECT_AFTER_TIME * 3);
1177744bfb21SJohn Baldwin 	noise_remote_keypairs_clear(peer->p_remote);
1178744bfb21SJohn Baldwin }
1179744bfb21SJohn Baldwin 
1180744bfb21SJohn Baldwin static void
wg_timers_run_persistent_keepalive(void * _peer)1181744bfb21SJohn Baldwin wg_timers_run_persistent_keepalive(void *_peer)
1182744bfb21SJohn Baldwin {
1183744bfb21SJohn Baldwin 	struct wg_peer *peer = _peer;
1184744bfb21SJohn Baldwin 
1185e32e1a16SJohn Baldwin 	if (atomic_load_16(&peer->p_persistent_keepalive_interval) > 0)
1186744bfb21SJohn Baldwin 		wg_send_keepalive(peer);
1187744bfb21SJohn Baldwin }
1188744bfb21SJohn Baldwin 
1189744bfb21SJohn Baldwin /* TODO Handshake */
1190744bfb21SJohn Baldwin static void
wg_peer_send_buf(struct wg_peer * peer,uint8_t * buf,size_t len)1191744bfb21SJohn Baldwin wg_peer_send_buf(struct wg_peer *peer, uint8_t *buf, size_t len)
1192744bfb21SJohn Baldwin {
1193744bfb21SJohn Baldwin 	struct wg_endpoint endpoint;
1194744bfb21SJohn Baldwin 
1195744bfb21SJohn Baldwin 	counter_u64_add(peer->p_tx_bytes, len);
1196744bfb21SJohn Baldwin 	wg_timers_event_any_authenticated_packet_traversal(peer);
1197744bfb21SJohn Baldwin 	wg_timers_event_any_authenticated_packet_sent(peer);
1198744bfb21SJohn Baldwin 	wg_peer_get_endpoint(peer, &endpoint);
1199744bfb21SJohn Baldwin 	wg_send_buf(peer->p_sc, &endpoint, buf, len);
1200744bfb21SJohn Baldwin }
1201744bfb21SJohn Baldwin 
1202744bfb21SJohn Baldwin static void
wg_send_initiation(struct wg_peer * peer)1203744bfb21SJohn Baldwin wg_send_initiation(struct wg_peer *peer)
1204744bfb21SJohn Baldwin {
1205744bfb21SJohn Baldwin 	struct wg_pkt_initiation pkt;
1206744bfb21SJohn Baldwin 
1207744bfb21SJohn Baldwin 	if (noise_create_initiation(peer->p_remote, &pkt.s_idx, pkt.ue,
1208744bfb21SJohn Baldwin 	    pkt.es, pkt.ets) != 0)
1209744bfb21SJohn Baldwin 		return;
1210744bfb21SJohn Baldwin 
1211744bfb21SJohn Baldwin 	DPRINTF(peer->p_sc, "Sending handshake initiation to peer %" PRIu64 "\n", peer->p_id);
1212744bfb21SJohn Baldwin 
1213744bfb21SJohn Baldwin 	pkt.t = WG_PKT_INITIATION;
1214744bfb21SJohn Baldwin 	cookie_maker_mac(&peer->p_cookie, &pkt.m, &pkt,
1215744bfb21SJohn Baldwin 	    sizeof(pkt) - sizeof(pkt.m));
1216744bfb21SJohn Baldwin 	wg_peer_send_buf(peer, (uint8_t *)&pkt, sizeof(pkt));
1217744bfb21SJohn Baldwin 	wg_timers_event_handshake_initiated(peer);
1218744bfb21SJohn Baldwin }
1219744bfb21SJohn Baldwin 
1220744bfb21SJohn Baldwin static void
wg_send_response(struct wg_peer * peer)1221744bfb21SJohn Baldwin wg_send_response(struct wg_peer *peer)
1222744bfb21SJohn Baldwin {
1223744bfb21SJohn Baldwin 	struct wg_pkt_response pkt;
1224744bfb21SJohn Baldwin 
1225744bfb21SJohn Baldwin 	if (noise_create_response(peer->p_remote, &pkt.s_idx, &pkt.r_idx,
1226744bfb21SJohn Baldwin 	    pkt.ue, pkt.en) != 0)
1227744bfb21SJohn Baldwin 		return;
1228744bfb21SJohn Baldwin 
1229744bfb21SJohn Baldwin 	DPRINTF(peer->p_sc, "Sending handshake response to peer %" PRIu64 "\n", peer->p_id);
1230744bfb21SJohn Baldwin 
1231744bfb21SJohn Baldwin 	wg_timers_event_session_derived(peer);
1232744bfb21SJohn Baldwin 	pkt.t = WG_PKT_RESPONSE;
1233744bfb21SJohn Baldwin 	cookie_maker_mac(&peer->p_cookie, &pkt.m, &pkt,
1234744bfb21SJohn Baldwin 	     sizeof(pkt)-sizeof(pkt.m));
1235744bfb21SJohn Baldwin 	wg_peer_send_buf(peer, (uint8_t*)&pkt, sizeof(pkt));
1236744bfb21SJohn Baldwin }
1237744bfb21SJohn Baldwin 
1238744bfb21SJohn Baldwin static void
wg_send_cookie(struct wg_softc * sc,struct cookie_macs * cm,uint32_t idx,struct wg_endpoint * e)1239744bfb21SJohn Baldwin wg_send_cookie(struct wg_softc *sc, struct cookie_macs *cm, uint32_t idx,
1240744bfb21SJohn Baldwin     struct wg_endpoint *e)
1241744bfb21SJohn Baldwin {
1242744bfb21SJohn Baldwin 	struct wg_pkt_cookie	pkt;
1243744bfb21SJohn Baldwin 
1244744bfb21SJohn Baldwin 	DPRINTF(sc, "Sending cookie response for denied handshake message\n");
1245744bfb21SJohn Baldwin 
1246744bfb21SJohn Baldwin 	pkt.t = WG_PKT_COOKIE;
1247744bfb21SJohn Baldwin 	pkt.r_idx = idx;
1248744bfb21SJohn Baldwin 
1249744bfb21SJohn Baldwin 	cookie_checker_create_payload(&sc->sc_cookie, cm, pkt.nonce,
1250744bfb21SJohn Baldwin 	    pkt.ec, &e->e_remote.r_sa);
1251744bfb21SJohn Baldwin 	wg_send_buf(sc, e, (uint8_t *)&pkt, sizeof(pkt));
1252744bfb21SJohn Baldwin }
1253744bfb21SJohn Baldwin 
1254744bfb21SJohn Baldwin static void
wg_send_keepalive(struct wg_peer * peer)1255744bfb21SJohn Baldwin wg_send_keepalive(struct wg_peer *peer)
1256744bfb21SJohn Baldwin {
1257744bfb21SJohn Baldwin 	struct wg_packet *pkt;
1258744bfb21SJohn Baldwin 	struct mbuf *m;
1259744bfb21SJohn Baldwin 
1260744bfb21SJohn Baldwin 	if (wg_queue_len(&peer->p_stage_queue) > 0)
1261744bfb21SJohn Baldwin 		goto send;
1262744bfb21SJohn Baldwin 	if ((m = m_gethdr(M_NOWAIT, MT_DATA)) == NULL)
1263744bfb21SJohn Baldwin 		return;
1264744bfb21SJohn Baldwin 	if ((pkt = wg_packet_alloc(m)) == NULL) {
1265744bfb21SJohn Baldwin 		m_freem(m);
1266744bfb21SJohn Baldwin 		return;
1267744bfb21SJohn Baldwin 	}
1268744bfb21SJohn Baldwin 	wg_queue_push_staged(&peer->p_stage_queue, pkt);
1269744bfb21SJohn Baldwin 	DPRINTF(peer->p_sc, "Sending keepalive packet to peer %" PRIu64 "\n", peer->p_id);
1270744bfb21SJohn Baldwin send:
1271744bfb21SJohn Baldwin 	wg_peer_send_staged(peer);
1272744bfb21SJohn Baldwin }
1273744bfb21SJohn Baldwin 
1274744bfb21SJohn Baldwin static void
wg_handshake(struct wg_softc * sc,struct wg_packet * pkt)1275744bfb21SJohn Baldwin wg_handshake(struct wg_softc *sc, struct wg_packet *pkt)
1276744bfb21SJohn Baldwin {
1277744bfb21SJohn Baldwin 	struct wg_pkt_initiation	*init;
1278744bfb21SJohn Baldwin 	struct wg_pkt_response		*resp;
1279744bfb21SJohn Baldwin 	struct wg_pkt_cookie		*cook;
1280744bfb21SJohn Baldwin 	struct wg_endpoint		*e;
1281744bfb21SJohn Baldwin 	struct wg_peer			*peer;
1282744bfb21SJohn Baldwin 	struct mbuf			*m;
1283744bfb21SJohn Baldwin 	struct noise_remote		*remote = NULL;
1284744bfb21SJohn Baldwin 	int				 res;
1285744bfb21SJohn Baldwin 	bool				 underload = false;
1286744bfb21SJohn Baldwin 	static sbintime_t		 wg_last_underload; /* sbinuptime */
1287744bfb21SJohn Baldwin 
1288744bfb21SJohn Baldwin 	underload = wg_queue_len(&sc->sc_handshake_queue) >= MAX_QUEUED_HANDSHAKES / 8;
1289744bfb21SJohn Baldwin 	if (underload) {
1290744bfb21SJohn Baldwin 		wg_last_underload = getsbinuptime();
1291744bfb21SJohn Baldwin 	} else if (wg_last_underload) {
1292744bfb21SJohn Baldwin 		underload = wg_last_underload + UNDERLOAD_TIMEOUT * SBT_1S > getsbinuptime();
1293744bfb21SJohn Baldwin 		if (!underload)
1294744bfb21SJohn Baldwin 			wg_last_underload = 0;
1295744bfb21SJohn Baldwin 	}
1296744bfb21SJohn Baldwin 
1297744bfb21SJohn Baldwin 	m = pkt->p_mbuf;
1298744bfb21SJohn Baldwin 	e = &pkt->p_endpoint;
1299744bfb21SJohn Baldwin 
1300744bfb21SJohn Baldwin 	if ((pkt->p_mbuf = m = m_pullup(m, m->m_pkthdr.len)) == NULL)
1301744bfb21SJohn Baldwin 		goto error;
1302744bfb21SJohn Baldwin 
1303744bfb21SJohn Baldwin 	switch (*mtod(m, uint32_t *)) {
1304744bfb21SJohn Baldwin 	case WG_PKT_INITIATION:
1305744bfb21SJohn Baldwin 		init = mtod(m, struct wg_pkt_initiation *);
1306744bfb21SJohn Baldwin 
1307744bfb21SJohn Baldwin 		res = cookie_checker_validate_macs(&sc->sc_cookie, &init->m,
1308744bfb21SJohn Baldwin 				init, sizeof(*init) - sizeof(init->m),
1309744bfb21SJohn Baldwin 				underload, &e->e_remote.r_sa,
131087e72834SJustin Hibbits 				if_getvnet(sc->sc_ifp));
1311744bfb21SJohn Baldwin 
1312744bfb21SJohn Baldwin 		if (res == EINVAL) {
1313744bfb21SJohn Baldwin 			DPRINTF(sc, "Invalid initiation MAC\n");
1314744bfb21SJohn Baldwin 			goto error;
1315744bfb21SJohn Baldwin 		} else if (res == ECONNREFUSED) {
1316744bfb21SJohn Baldwin 			DPRINTF(sc, "Handshake ratelimited\n");
1317744bfb21SJohn Baldwin 			goto error;
1318744bfb21SJohn Baldwin 		} else if (res == EAGAIN) {
1319744bfb21SJohn Baldwin 			wg_send_cookie(sc, &init->m, init->s_idx, e);
1320744bfb21SJohn Baldwin 			goto error;
1321744bfb21SJohn Baldwin 		} else if (res != 0) {
1322744bfb21SJohn Baldwin 			panic("unexpected response: %d\n", res);
1323744bfb21SJohn Baldwin 		}
1324744bfb21SJohn Baldwin 
1325744bfb21SJohn Baldwin 		if (noise_consume_initiation(sc->sc_local, &remote,
1326744bfb21SJohn Baldwin 		    init->s_idx, init->ue, init->es, init->ets) != 0) {
1327744bfb21SJohn Baldwin 			DPRINTF(sc, "Invalid handshake initiation\n");
1328744bfb21SJohn Baldwin 			goto error;
1329744bfb21SJohn Baldwin 		}
1330744bfb21SJohn Baldwin 
1331744bfb21SJohn Baldwin 		peer = noise_remote_arg(remote);
1332744bfb21SJohn Baldwin 
1333744bfb21SJohn Baldwin 		DPRINTF(sc, "Receiving handshake initiation from peer %" PRIu64 "\n", peer->p_id);
1334744bfb21SJohn Baldwin 
1335744bfb21SJohn Baldwin 		wg_peer_set_endpoint(peer, e);
1336744bfb21SJohn Baldwin 		wg_send_response(peer);
1337744bfb21SJohn Baldwin 		break;
1338744bfb21SJohn Baldwin 	case WG_PKT_RESPONSE:
1339744bfb21SJohn Baldwin 		resp = mtod(m, struct wg_pkt_response *);
1340744bfb21SJohn Baldwin 
1341744bfb21SJohn Baldwin 		res = cookie_checker_validate_macs(&sc->sc_cookie, &resp->m,
1342744bfb21SJohn Baldwin 				resp, sizeof(*resp) - sizeof(resp->m),
1343744bfb21SJohn Baldwin 				underload, &e->e_remote.r_sa,
134487e72834SJustin Hibbits 				if_getvnet(sc->sc_ifp));
1345744bfb21SJohn Baldwin 
1346744bfb21SJohn Baldwin 		if (res == EINVAL) {
1347744bfb21SJohn Baldwin 			DPRINTF(sc, "Invalid response MAC\n");
1348744bfb21SJohn Baldwin 			goto error;
1349744bfb21SJohn Baldwin 		} else if (res == ECONNREFUSED) {
1350744bfb21SJohn Baldwin 			DPRINTF(sc, "Handshake ratelimited\n");
1351744bfb21SJohn Baldwin 			goto error;
1352744bfb21SJohn Baldwin 		} else if (res == EAGAIN) {
1353744bfb21SJohn Baldwin 			wg_send_cookie(sc, &resp->m, resp->s_idx, e);
1354744bfb21SJohn Baldwin 			goto error;
1355744bfb21SJohn Baldwin 		} else if (res != 0) {
1356744bfb21SJohn Baldwin 			panic("unexpected response: %d\n", res);
1357744bfb21SJohn Baldwin 		}
1358744bfb21SJohn Baldwin 
1359744bfb21SJohn Baldwin 		if (noise_consume_response(sc->sc_local, &remote,
1360744bfb21SJohn Baldwin 		    resp->s_idx, resp->r_idx, resp->ue, resp->en) != 0) {
1361744bfb21SJohn Baldwin 			DPRINTF(sc, "Invalid handshake response\n");
1362744bfb21SJohn Baldwin 			goto error;
1363744bfb21SJohn Baldwin 		}
1364744bfb21SJohn Baldwin 
1365744bfb21SJohn Baldwin 		peer = noise_remote_arg(remote);
1366744bfb21SJohn Baldwin 		DPRINTF(sc, "Receiving handshake response from peer %" PRIu64 "\n", peer->p_id);
1367744bfb21SJohn Baldwin 
1368744bfb21SJohn Baldwin 		wg_peer_set_endpoint(peer, e);
1369744bfb21SJohn Baldwin 		wg_timers_event_session_derived(peer);
1370744bfb21SJohn Baldwin 		wg_timers_event_handshake_complete(peer);
1371744bfb21SJohn Baldwin 		break;
1372744bfb21SJohn Baldwin 	case WG_PKT_COOKIE:
1373744bfb21SJohn Baldwin 		cook = mtod(m, struct wg_pkt_cookie *);
1374744bfb21SJohn Baldwin 
1375744bfb21SJohn Baldwin 		if ((remote = noise_remote_index(sc->sc_local, cook->r_idx)) == NULL) {
1376744bfb21SJohn Baldwin 			DPRINTF(sc, "Unknown cookie index\n");
1377744bfb21SJohn Baldwin 			goto error;
1378744bfb21SJohn Baldwin 		}
1379744bfb21SJohn Baldwin 
1380744bfb21SJohn Baldwin 		peer = noise_remote_arg(remote);
1381744bfb21SJohn Baldwin 
1382744bfb21SJohn Baldwin 		if (cookie_maker_consume_payload(&peer->p_cookie,
1383744bfb21SJohn Baldwin 		    cook->nonce, cook->ec) == 0) {
1384744bfb21SJohn Baldwin 			DPRINTF(sc, "Receiving cookie response\n");
1385744bfb21SJohn Baldwin 		} else {
1386744bfb21SJohn Baldwin 			DPRINTF(sc, "Could not decrypt cookie response\n");
1387744bfb21SJohn Baldwin 			goto error;
1388744bfb21SJohn Baldwin 		}
1389744bfb21SJohn Baldwin 
1390744bfb21SJohn Baldwin 		goto not_authenticated;
1391744bfb21SJohn Baldwin 	default:
1392744bfb21SJohn Baldwin 		panic("invalid packet in handshake queue");
1393744bfb21SJohn Baldwin 	}
1394744bfb21SJohn Baldwin 
1395744bfb21SJohn Baldwin 	wg_timers_event_any_authenticated_packet_received(peer);
1396744bfb21SJohn Baldwin 	wg_timers_event_any_authenticated_packet_traversal(peer);
1397744bfb21SJohn Baldwin 
1398744bfb21SJohn Baldwin not_authenticated:
1399744bfb21SJohn Baldwin 	counter_u64_add(peer->p_rx_bytes, m->m_pkthdr.len);
1400744bfb21SJohn Baldwin 	if_inc_counter(sc->sc_ifp, IFCOUNTER_IPACKETS, 1);
1401744bfb21SJohn Baldwin 	if_inc_counter(sc->sc_ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len);
1402744bfb21SJohn Baldwin error:
1403744bfb21SJohn Baldwin 	if (remote != NULL)
1404744bfb21SJohn Baldwin 		noise_remote_put(remote);
1405744bfb21SJohn Baldwin 	wg_packet_free(pkt);
1406744bfb21SJohn Baldwin }
1407744bfb21SJohn Baldwin 
1408744bfb21SJohn Baldwin static void
wg_softc_handshake_receive(struct wg_softc * sc)1409744bfb21SJohn Baldwin wg_softc_handshake_receive(struct wg_softc *sc)
1410744bfb21SJohn Baldwin {
1411744bfb21SJohn Baldwin 	struct wg_packet *pkt;
1412744bfb21SJohn Baldwin 	while ((pkt = wg_queue_dequeue_handshake(&sc->sc_handshake_queue)) != NULL)
1413744bfb21SJohn Baldwin 		wg_handshake(sc, pkt);
1414744bfb21SJohn Baldwin }
1415744bfb21SJohn Baldwin 
1416744bfb21SJohn Baldwin static void
wg_mbuf_reset(struct mbuf * m)1417744bfb21SJohn Baldwin wg_mbuf_reset(struct mbuf *m)
1418744bfb21SJohn Baldwin {
1419744bfb21SJohn Baldwin 
1420744bfb21SJohn Baldwin 	struct m_tag *t, *tmp;
1421744bfb21SJohn Baldwin 
1422744bfb21SJohn Baldwin 	/*
1423744bfb21SJohn Baldwin 	 * We want to reset the mbuf to a newly allocated state, containing
1424744bfb21SJohn Baldwin 	 * just the packet contents. Unfortunately FreeBSD doesn't seem to
1425744bfb21SJohn Baldwin 	 * offer this anywhere, so we have to make it up as we go. If we can
1426744bfb21SJohn Baldwin 	 * get this in kern/kern_mbuf.c, that would be best.
1427744bfb21SJohn Baldwin 	 *
1428744bfb21SJohn Baldwin 	 * Notice: this may break things unexpectedly but it is better to fail
1429744bfb21SJohn Baldwin 	 *         closed in the extreme case than leak informtion in every
1430744bfb21SJohn Baldwin 	 *         case.
1431744bfb21SJohn Baldwin 	 *
1432744bfb21SJohn Baldwin 	 * With that said, all this attempts to do is remove any extraneous
1433744bfb21SJohn Baldwin 	 * information that could be present.
1434744bfb21SJohn Baldwin 	 */
1435744bfb21SJohn Baldwin 
1436744bfb21SJohn Baldwin 	M_ASSERTPKTHDR(m);
1437744bfb21SJohn Baldwin 
1438744bfb21SJohn Baldwin 	m->m_flags &= ~(M_BCAST|M_MCAST|M_VLANTAG|M_PROMISC|M_PROTOFLAGS);
1439744bfb21SJohn Baldwin 
1440744bfb21SJohn Baldwin 	M_HASHTYPE_CLEAR(m);
1441744bfb21SJohn Baldwin #ifdef NUMA
1442744bfb21SJohn Baldwin         m->m_pkthdr.numa_domain = M_NODOM;
1443744bfb21SJohn Baldwin #endif
1444744bfb21SJohn Baldwin 	SLIST_FOREACH_SAFE(t, &m->m_pkthdr.tags, m_tag_link, tmp) {
1445744bfb21SJohn Baldwin 		if ((t->m_tag_id != 0 || t->m_tag_cookie != MTAG_WGLOOP) &&
1446744bfb21SJohn Baldwin 		    t->m_tag_id != PACKET_TAG_MACLABEL)
1447744bfb21SJohn Baldwin 			m_tag_delete(m, t);
1448744bfb21SJohn Baldwin 	}
1449744bfb21SJohn Baldwin 
1450744bfb21SJohn Baldwin 	KASSERT((m->m_pkthdr.csum_flags & CSUM_SND_TAG) == 0,
1451744bfb21SJohn Baldwin 	    ("%s: mbuf %p has a send tag", __func__, m));
1452744bfb21SJohn Baldwin 
1453744bfb21SJohn Baldwin 	m->m_pkthdr.csum_flags = 0;
1454744bfb21SJohn Baldwin 	m->m_pkthdr.PH_per.sixtyfour[0] = 0;
1455744bfb21SJohn Baldwin 	m->m_pkthdr.PH_loc.sixtyfour[0] = 0;
1456744bfb21SJohn Baldwin }
1457744bfb21SJohn Baldwin 
1458744bfb21SJohn Baldwin static inline unsigned int
calculate_padding(struct wg_packet * pkt)1459744bfb21SJohn Baldwin calculate_padding(struct wg_packet *pkt)
1460744bfb21SJohn Baldwin {
1461744bfb21SJohn Baldwin 	unsigned int padded_size, last_unit = pkt->p_mbuf->m_pkthdr.len;
1462744bfb21SJohn Baldwin 
1463b891f61eSAaron LI 	/* Keepalive packets don't set p_mtu, but also have a length of zero. */
1464b891f61eSAaron LI 	if (__predict_false(pkt->p_mtu == 0)) {
1465b891f61eSAaron LI 		padded_size = (last_unit + (WG_PKT_PADDING - 1)) &
1466b891f61eSAaron LI 		    ~(WG_PKT_PADDING - 1);
1467b891f61eSAaron LI 		return (padded_size - last_unit);
1468b891f61eSAaron LI 	}
1469744bfb21SJohn Baldwin 
1470744bfb21SJohn Baldwin 	if (__predict_false(last_unit > pkt->p_mtu))
1471744bfb21SJohn Baldwin 		last_unit %= pkt->p_mtu;
1472744bfb21SJohn Baldwin 
1473744bfb21SJohn Baldwin 	padded_size = (last_unit + (WG_PKT_PADDING - 1)) & ~(WG_PKT_PADDING - 1);
1474744bfb21SJohn Baldwin 	if (pkt->p_mtu < padded_size)
1475744bfb21SJohn Baldwin 		padded_size = pkt->p_mtu;
1476b891f61eSAaron LI 	return (padded_size - last_unit);
1477744bfb21SJohn Baldwin }
1478744bfb21SJohn Baldwin 
1479744bfb21SJohn Baldwin static void
wg_encrypt(struct wg_softc * sc,struct wg_packet * pkt)1480744bfb21SJohn Baldwin wg_encrypt(struct wg_softc *sc, struct wg_packet *pkt)
1481744bfb21SJohn Baldwin {
1482744bfb21SJohn Baldwin 	static const uint8_t	 padding[WG_PKT_PADDING] = { 0 };
1483744bfb21SJohn Baldwin 	struct wg_pkt_data	*data;
1484744bfb21SJohn Baldwin 	struct wg_peer		*peer;
1485744bfb21SJohn Baldwin 	struct noise_remote	*remote;
1486744bfb21SJohn Baldwin 	struct mbuf		*m;
1487744bfb21SJohn Baldwin 	uint32_t		 idx;
1488744bfb21SJohn Baldwin 	unsigned int		 padlen;
1489744bfb21SJohn Baldwin 	enum wg_ring_state	 state = WG_PACKET_DEAD;
1490744bfb21SJohn Baldwin 
1491744bfb21SJohn Baldwin 	remote = noise_keypair_remote(pkt->p_keypair);
1492744bfb21SJohn Baldwin 	peer = noise_remote_arg(remote);
1493744bfb21SJohn Baldwin 	m = pkt->p_mbuf;
1494744bfb21SJohn Baldwin 
1495744bfb21SJohn Baldwin 	/* Pad the packet */
1496744bfb21SJohn Baldwin 	padlen = calculate_padding(pkt);
1497744bfb21SJohn Baldwin 	if (padlen != 0 && !m_append(m, padlen, padding))
1498744bfb21SJohn Baldwin 		goto out;
1499744bfb21SJohn Baldwin 
1500744bfb21SJohn Baldwin 	/* Do encryption */
1501744bfb21SJohn Baldwin 	if (noise_keypair_encrypt(pkt->p_keypair, &idx, pkt->p_nonce, m) != 0)
1502744bfb21SJohn Baldwin 		goto out;
1503744bfb21SJohn Baldwin 
1504744bfb21SJohn Baldwin 	/* Put header into packet */
1505744bfb21SJohn Baldwin 	M_PREPEND(m, sizeof(struct wg_pkt_data), M_NOWAIT);
1506744bfb21SJohn Baldwin 	if (m == NULL)
1507744bfb21SJohn Baldwin 		goto out;
1508744bfb21SJohn Baldwin 	data = mtod(m, struct wg_pkt_data *);
1509744bfb21SJohn Baldwin 	data->t = WG_PKT_DATA;
1510744bfb21SJohn Baldwin 	data->r_idx = idx;
1511744bfb21SJohn Baldwin 	data->nonce = htole64(pkt->p_nonce);
1512744bfb21SJohn Baldwin 
1513744bfb21SJohn Baldwin 	wg_mbuf_reset(m);
1514744bfb21SJohn Baldwin 	state = WG_PACKET_CRYPTED;
1515744bfb21SJohn Baldwin out:
1516744bfb21SJohn Baldwin 	pkt->p_mbuf = m;
15173705d679SKyle Evans 	atomic_store_rel_int(&pkt->p_state, state);
1518744bfb21SJohn Baldwin 	GROUPTASK_ENQUEUE(&peer->p_send);
1519744bfb21SJohn Baldwin 	noise_remote_put(remote);
1520744bfb21SJohn Baldwin }
1521744bfb21SJohn Baldwin 
1522744bfb21SJohn Baldwin static void
wg_decrypt(struct wg_softc * sc,struct wg_packet * pkt)1523744bfb21SJohn Baldwin wg_decrypt(struct wg_softc *sc, struct wg_packet *pkt)
1524744bfb21SJohn Baldwin {
1525744bfb21SJohn Baldwin 	struct wg_peer		*peer, *allowed_peer;
1526744bfb21SJohn Baldwin 	struct noise_remote	*remote;
1527744bfb21SJohn Baldwin 	struct mbuf		*m;
1528744bfb21SJohn Baldwin 	int			 len;
1529744bfb21SJohn Baldwin 	enum wg_ring_state	 state = WG_PACKET_DEAD;
1530744bfb21SJohn Baldwin 
1531744bfb21SJohn Baldwin 	remote = noise_keypair_remote(pkt->p_keypair);
1532744bfb21SJohn Baldwin 	peer = noise_remote_arg(remote);
1533744bfb21SJohn Baldwin 	m = pkt->p_mbuf;
1534744bfb21SJohn Baldwin 
1535744bfb21SJohn Baldwin 	/* Read nonce and then adjust to remove the header. */
1536744bfb21SJohn Baldwin 	pkt->p_nonce = le64toh(mtod(m, struct wg_pkt_data *)->nonce);
1537744bfb21SJohn Baldwin 	m_adj(m, sizeof(struct wg_pkt_data));
1538744bfb21SJohn Baldwin 
1539744bfb21SJohn Baldwin 	if (noise_keypair_decrypt(pkt->p_keypair, pkt->p_nonce, m) != 0)
1540744bfb21SJohn Baldwin 		goto out;
1541744bfb21SJohn Baldwin 
1542744bfb21SJohn Baldwin 	/* A packet with length 0 is a keepalive packet */
1543744bfb21SJohn Baldwin 	if (__predict_false(m->m_pkthdr.len == 0)) {
1544744bfb21SJohn Baldwin 		DPRINTF(sc, "Receiving keepalive packet from peer "
1545744bfb21SJohn Baldwin 		    "%" PRIu64 "\n", peer->p_id);
1546744bfb21SJohn Baldwin 		state = WG_PACKET_CRYPTED;
1547744bfb21SJohn Baldwin 		goto out;
1548744bfb21SJohn Baldwin 	}
1549744bfb21SJohn Baldwin 
1550744bfb21SJohn Baldwin 	/*
1551744bfb21SJohn Baldwin 	 * We can let the network stack handle the intricate validation of the
1552744bfb21SJohn Baldwin 	 * IP header, we just worry about the sizeof and the version, so we can
1553744bfb21SJohn Baldwin 	 * read the source address in wg_aip_lookup.
1554744bfb21SJohn Baldwin 	 */
1555744bfb21SJohn Baldwin 
1556744bfb21SJohn Baldwin 	if (determine_af_and_pullup(&m, &pkt->p_af) == 0) {
1557744bfb21SJohn Baldwin 		if (pkt->p_af == AF_INET) {
1558744bfb21SJohn Baldwin 			struct ip *ip = mtod(m, struct ip *);
1559744bfb21SJohn Baldwin 			allowed_peer = wg_aip_lookup(sc, AF_INET, &ip->ip_src);
1560744bfb21SJohn Baldwin 			len = ntohs(ip->ip_len);
1561744bfb21SJohn Baldwin 			if (len >= sizeof(struct ip) && len < m->m_pkthdr.len)
1562744bfb21SJohn Baldwin 				m_adj(m, len - m->m_pkthdr.len);
1563744bfb21SJohn Baldwin 		} else if (pkt->p_af == AF_INET6) {
1564744bfb21SJohn Baldwin 			struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
1565744bfb21SJohn Baldwin 			allowed_peer = wg_aip_lookup(sc, AF_INET6, &ip6->ip6_src);
1566744bfb21SJohn Baldwin 			len = ntohs(ip6->ip6_plen) + sizeof(struct ip6_hdr);
1567744bfb21SJohn Baldwin 			if (len < m->m_pkthdr.len)
1568744bfb21SJohn Baldwin 				m_adj(m, len - m->m_pkthdr.len);
1569744bfb21SJohn Baldwin 		} else
1570744bfb21SJohn Baldwin 			panic("determine_af_and_pullup returned unexpected value");
1571744bfb21SJohn Baldwin 	} else {
1572744bfb21SJohn Baldwin 		DPRINTF(sc, "Packet is neither ipv4 nor ipv6 from peer %" PRIu64 "\n", peer->p_id);
1573744bfb21SJohn Baldwin 		goto out;
1574744bfb21SJohn Baldwin 	}
1575744bfb21SJohn Baldwin 
1576744bfb21SJohn Baldwin 	/* We only want to compare the address, not dereference, so drop the ref. */
1577744bfb21SJohn Baldwin 	if (allowed_peer != NULL)
1578744bfb21SJohn Baldwin 		noise_remote_put(allowed_peer->p_remote);
1579744bfb21SJohn Baldwin 
1580744bfb21SJohn Baldwin 	if (__predict_false(peer != allowed_peer)) {
1581744bfb21SJohn Baldwin 		DPRINTF(sc, "Packet has unallowed src IP from peer %" PRIu64 "\n", peer->p_id);
1582744bfb21SJohn Baldwin 		goto out;
1583744bfb21SJohn Baldwin 	}
1584744bfb21SJohn Baldwin 
1585744bfb21SJohn Baldwin 	wg_mbuf_reset(m);
1586744bfb21SJohn Baldwin 	state = WG_PACKET_CRYPTED;
1587744bfb21SJohn Baldwin out:
1588744bfb21SJohn Baldwin 	pkt->p_mbuf = m;
15893705d679SKyle Evans 	atomic_store_rel_int(&pkt->p_state, state);
1590744bfb21SJohn Baldwin 	GROUPTASK_ENQUEUE(&peer->p_recv);
1591744bfb21SJohn Baldwin 	noise_remote_put(remote);
1592744bfb21SJohn Baldwin }
1593744bfb21SJohn Baldwin 
1594744bfb21SJohn Baldwin static void
wg_softc_decrypt(struct wg_softc * sc)1595744bfb21SJohn Baldwin wg_softc_decrypt(struct wg_softc *sc)
1596744bfb21SJohn Baldwin {
1597744bfb21SJohn Baldwin 	struct wg_packet *pkt;
1598744bfb21SJohn Baldwin 
1599744bfb21SJohn Baldwin 	while ((pkt = wg_queue_dequeue_parallel(&sc->sc_decrypt_parallel)) != NULL)
1600744bfb21SJohn Baldwin 		wg_decrypt(sc, pkt);
1601744bfb21SJohn Baldwin }
1602744bfb21SJohn Baldwin 
1603744bfb21SJohn Baldwin static void
wg_softc_encrypt(struct wg_softc * sc)1604744bfb21SJohn Baldwin wg_softc_encrypt(struct wg_softc *sc)
1605744bfb21SJohn Baldwin {
1606744bfb21SJohn Baldwin 	struct wg_packet *pkt;
1607744bfb21SJohn Baldwin 
1608744bfb21SJohn Baldwin 	while ((pkt = wg_queue_dequeue_parallel(&sc->sc_encrypt_parallel)) != NULL)
1609744bfb21SJohn Baldwin 		wg_encrypt(sc, pkt);
1610744bfb21SJohn Baldwin }
1611744bfb21SJohn Baldwin 
1612744bfb21SJohn Baldwin static void
wg_encrypt_dispatch(struct wg_softc * sc)1613744bfb21SJohn Baldwin wg_encrypt_dispatch(struct wg_softc *sc)
1614744bfb21SJohn Baldwin {
1615744bfb21SJohn Baldwin 	/*
1616744bfb21SJohn Baldwin 	 * The update to encrypt_last_cpu is racey such that we may
1617744bfb21SJohn Baldwin 	 * reschedule the task for the same CPU multiple times, but
1618744bfb21SJohn Baldwin 	 * the race doesn't really matter.
1619744bfb21SJohn Baldwin 	 */
1620744bfb21SJohn Baldwin 	u_int cpu = (sc->sc_encrypt_last_cpu + 1) % mp_ncpus;
1621744bfb21SJohn Baldwin 	sc->sc_encrypt_last_cpu = cpu;
1622744bfb21SJohn Baldwin 	GROUPTASK_ENQUEUE(&sc->sc_encrypt[cpu]);
1623744bfb21SJohn Baldwin }
1624744bfb21SJohn Baldwin 
1625744bfb21SJohn Baldwin static void
wg_decrypt_dispatch(struct wg_softc * sc)1626744bfb21SJohn Baldwin wg_decrypt_dispatch(struct wg_softc *sc)
1627744bfb21SJohn Baldwin {
1628744bfb21SJohn Baldwin 	u_int cpu = (sc->sc_decrypt_last_cpu + 1) % mp_ncpus;
1629744bfb21SJohn Baldwin 	sc->sc_decrypt_last_cpu = cpu;
1630744bfb21SJohn Baldwin 	GROUPTASK_ENQUEUE(&sc->sc_decrypt[cpu]);
1631744bfb21SJohn Baldwin }
1632744bfb21SJohn Baldwin 
1633744bfb21SJohn Baldwin static void
wg_deliver_out(struct wg_peer * peer)1634744bfb21SJohn Baldwin wg_deliver_out(struct wg_peer *peer)
1635744bfb21SJohn Baldwin {
1636744bfb21SJohn Baldwin 	struct wg_endpoint	 endpoint;
1637744bfb21SJohn Baldwin 	struct wg_softc		*sc = peer->p_sc;
1638744bfb21SJohn Baldwin 	struct wg_packet	*pkt;
1639744bfb21SJohn Baldwin 	struct mbuf		*m;
1640744bfb21SJohn Baldwin 	int			 rc, len;
1641744bfb21SJohn Baldwin 
1642744bfb21SJohn Baldwin 	wg_peer_get_endpoint(peer, &endpoint);
1643744bfb21SJohn Baldwin 
1644744bfb21SJohn Baldwin 	while ((pkt = wg_queue_dequeue_serial(&peer->p_encrypt_serial)) != NULL) {
16453705d679SKyle Evans 		if (atomic_load_acq_int(&pkt->p_state) != WG_PACKET_CRYPTED)
1646744bfb21SJohn Baldwin 			goto error;
1647744bfb21SJohn Baldwin 
1648744bfb21SJohn Baldwin 		m = pkt->p_mbuf;
1649744bfb21SJohn Baldwin 		pkt->p_mbuf = NULL;
1650744bfb21SJohn Baldwin 
1651744bfb21SJohn Baldwin 		len = m->m_pkthdr.len;
1652744bfb21SJohn Baldwin 
1653744bfb21SJohn Baldwin 		wg_timers_event_any_authenticated_packet_traversal(peer);
1654744bfb21SJohn Baldwin 		wg_timers_event_any_authenticated_packet_sent(peer);
1655744bfb21SJohn Baldwin 		rc = wg_send(sc, &endpoint, m);
1656744bfb21SJohn Baldwin 		if (rc == 0) {
1657744bfb21SJohn Baldwin 			if (len > (sizeof(struct wg_pkt_data) + NOISE_AUTHTAG_LEN))
1658744bfb21SJohn Baldwin 				wg_timers_event_data_sent(peer);
1659744bfb21SJohn Baldwin 			counter_u64_add(peer->p_tx_bytes, len);
1660744bfb21SJohn Baldwin 		} else if (rc == EADDRNOTAVAIL) {
1661744bfb21SJohn Baldwin 			wg_peer_clear_src(peer);
1662744bfb21SJohn Baldwin 			wg_peer_get_endpoint(peer, &endpoint);
1663744bfb21SJohn Baldwin 			goto error;
1664744bfb21SJohn Baldwin 		} else {
1665744bfb21SJohn Baldwin 			goto error;
1666744bfb21SJohn Baldwin 		}
1667744bfb21SJohn Baldwin 		wg_packet_free(pkt);
1668744bfb21SJohn Baldwin 		if (noise_keep_key_fresh_send(peer->p_remote))
1669744bfb21SJohn Baldwin 			wg_timers_event_want_initiation(peer);
1670744bfb21SJohn Baldwin 		continue;
1671744bfb21SJohn Baldwin error:
1672744bfb21SJohn Baldwin 		if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1);
1673744bfb21SJohn Baldwin 		wg_packet_free(pkt);
1674744bfb21SJohn Baldwin 	}
1675744bfb21SJohn Baldwin }
1676744bfb21SJohn Baldwin 
1677bf454ca8SMark Johnston #ifdef DEV_NETMAP
1678bf454ca8SMark Johnston /*
1679bf454ca8SMark Johnston  * Hand a packet to the netmap RX ring, via netmap's
1680bf454ca8SMark Johnston  * freebsd_generic_rx_handler().
1681bf454ca8SMark Johnston  */
1682bf454ca8SMark Johnston static void
wg_deliver_netmap(if_t ifp,struct mbuf * m,int af)1683bf454ca8SMark Johnston wg_deliver_netmap(if_t ifp, struct mbuf *m, int af)
1684bf454ca8SMark Johnston {
1685bf454ca8SMark Johnston 	struct ether_header *eh;
1686bf454ca8SMark Johnston 
1687bf454ca8SMark Johnston 	M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT);
1688bf454ca8SMark Johnston 	if (__predict_false(m == NULL)) {
1689bf454ca8SMark Johnston 		if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
1690bf454ca8SMark Johnston 		return;
1691bf454ca8SMark Johnston 	}
1692bf454ca8SMark Johnston 
1693bf454ca8SMark Johnston 	eh = mtod(m, struct ether_header *);
1694bf454ca8SMark Johnston 	eh->ether_type = af == AF_INET ?
1695bf454ca8SMark Johnston 	    htons(ETHERTYPE_IP) : htons(ETHERTYPE_IPV6);
1696bf454ca8SMark Johnston 	memcpy(eh->ether_shost, "\x02\x02\x02\x02\x02\x02", ETHER_ADDR_LEN);
1697bf454ca8SMark Johnston 	memcpy(eh->ether_dhost, "\xff\xff\xff\xff\xff\xff", ETHER_ADDR_LEN);
1698bf454ca8SMark Johnston 	if_input(ifp, m);
1699bf454ca8SMark Johnston }
1700bf454ca8SMark Johnston #endif
1701bf454ca8SMark Johnston 
1702744bfb21SJohn Baldwin static void
wg_deliver_in(struct wg_peer * peer)1703744bfb21SJohn Baldwin wg_deliver_in(struct wg_peer *peer)
1704744bfb21SJohn Baldwin {
1705744bfb21SJohn Baldwin 	struct wg_softc		*sc = peer->p_sc;
170687e72834SJustin Hibbits 	if_t			 ifp = sc->sc_ifp;
1707744bfb21SJohn Baldwin 	struct wg_packet	*pkt;
1708744bfb21SJohn Baldwin 	struct mbuf		*m;
1709744bfb21SJohn Baldwin 	struct epoch_tracker	 et;
1710bf454ca8SMark Johnston 	int			 af;
1711744bfb21SJohn Baldwin 
1712744bfb21SJohn Baldwin 	while ((pkt = wg_queue_dequeue_serial(&peer->p_decrypt_serial)) != NULL) {
17133705d679SKyle Evans 		if (atomic_load_acq_int(&pkt->p_state) != WG_PACKET_CRYPTED)
1714744bfb21SJohn Baldwin 			goto error;
1715744bfb21SJohn Baldwin 
1716744bfb21SJohn Baldwin 		m = pkt->p_mbuf;
1717744bfb21SJohn Baldwin 		if (noise_keypair_nonce_check(pkt->p_keypair, pkt->p_nonce) != 0)
1718744bfb21SJohn Baldwin 			goto error;
1719744bfb21SJohn Baldwin 
1720744bfb21SJohn Baldwin 		if (noise_keypair_received_with(pkt->p_keypair) == ECONNRESET)
1721744bfb21SJohn Baldwin 			wg_timers_event_handshake_complete(peer);
1722744bfb21SJohn Baldwin 
1723744bfb21SJohn Baldwin 		wg_timers_event_any_authenticated_packet_received(peer);
1724744bfb21SJohn Baldwin 		wg_timers_event_any_authenticated_packet_traversal(peer);
1725744bfb21SJohn Baldwin 		wg_peer_set_endpoint(peer, &pkt->p_endpoint);
1726744bfb21SJohn Baldwin 
1727744bfb21SJohn Baldwin 		counter_u64_add(peer->p_rx_bytes, m->m_pkthdr.len +
1728744bfb21SJohn Baldwin 		    sizeof(struct wg_pkt_data) + NOISE_AUTHTAG_LEN);
1729744bfb21SJohn Baldwin 		if_inc_counter(sc->sc_ifp, IFCOUNTER_IPACKETS, 1);
1730744bfb21SJohn Baldwin 		if_inc_counter(sc->sc_ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len +
1731744bfb21SJohn Baldwin 		    sizeof(struct wg_pkt_data) + NOISE_AUTHTAG_LEN);
1732744bfb21SJohn Baldwin 
1733744bfb21SJohn Baldwin 		if (m->m_pkthdr.len == 0)
1734744bfb21SJohn Baldwin 			goto done;
1735744bfb21SJohn Baldwin 
1736bf454ca8SMark Johnston 		af = pkt->p_af;
1737bf454ca8SMark Johnston 		MPASS(af == AF_INET || af == AF_INET6);
1738744bfb21SJohn Baldwin 		pkt->p_mbuf = NULL;
1739744bfb21SJohn Baldwin 
1740744bfb21SJohn Baldwin 		m->m_pkthdr.rcvif = ifp;
1741744bfb21SJohn Baldwin 
1742744bfb21SJohn Baldwin 		NET_EPOCH_ENTER(et);
1743bf454ca8SMark Johnston 		BPF_MTAP2_AF(ifp, m, af);
1744744bfb21SJohn Baldwin 
174587e72834SJustin Hibbits 		CURVNET_SET(if_getvnet(ifp));
174687e72834SJustin Hibbits 		M_SETFIB(m, if_getfib(ifp));
1747bf454ca8SMark Johnston #ifdef DEV_NETMAP
1748bf454ca8SMark Johnston 		if ((if_getcapenable(ifp) & IFCAP_NETMAP) != 0)
1749bf454ca8SMark Johnston 			wg_deliver_netmap(ifp, m, af);
1750bf454ca8SMark Johnston 		else
1751bf454ca8SMark Johnston #endif
1752bf454ca8SMark Johnston 		if (af == AF_INET)
1753744bfb21SJohn Baldwin 			netisr_dispatch(NETISR_IP, m);
1754bf454ca8SMark Johnston 		else if (af == AF_INET6)
1755744bfb21SJohn Baldwin 			netisr_dispatch(NETISR_IPV6, m);
1756744bfb21SJohn Baldwin 		CURVNET_RESTORE();
1757744bfb21SJohn Baldwin 		NET_EPOCH_EXIT(et);
1758744bfb21SJohn Baldwin 
1759744bfb21SJohn Baldwin 		wg_timers_event_data_received(peer);
1760744bfb21SJohn Baldwin 
1761744bfb21SJohn Baldwin done:
1762744bfb21SJohn Baldwin 		if (noise_keep_key_fresh_recv(peer->p_remote))
1763744bfb21SJohn Baldwin 			wg_timers_event_want_initiation(peer);
1764744bfb21SJohn Baldwin 		wg_packet_free(pkt);
1765744bfb21SJohn Baldwin 		continue;
1766744bfb21SJohn Baldwin error:
1767744bfb21SJohn Baldwin 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
1768744bfb21SJohn Baldwin 		wg_packet_free(pkt);
1769744bfb21SJohn Baldwin 	}
1770744bfb21SJohn Baldwin }
1771744bfb21SJohn Baldwin 
1772744bfb21SJohn Baldwin static struct wg_packet *
wg_packet_alloc(struct mbuf * m)1773744bfb21SJohn Baldwin wg_packet_alloc(struct mbuf *m)
1774744bfb21SJohn Baldwin {
1775744bfb21SJohn Baldwin 	struct wg_packet *pkt;
1776744bfb21SJohn Baldwin 
1777744bfb21SJohn Baldwin 	if ((pkt = uma_zalloc(wg_packet_zone, M_NOWAIT | M_ZERO)) == NULL)
1778744bfb21SJohn Baldwin 		return (NULL);
1779744bfb21SJohn Baldwin 	pkt->p_mbuf = m;
1780744bfb21SJohn Baldwin 	return (pkt);
1781744bfb21SJohn Baldwin }
1782744bfb21SJohn Baldwin 
1783744bfb21SJohn Baldwin static void
wg_packet_free(struct wg_packet * pkt)1784744bfb21SJohn Baldwin wg_packet_free(struct wg_packet *pkt)
1785744bfb21SJohn Baldwin {
1786744bfb21SJohn Baldwin 	if (pkt->p_keypair != NULL)
1787744bfb21SJohn Baldwin 		noise_keypair_put(pkt->p_keypair);
1788744bfb21SJohn Baldwin 	if (pkt->p_mbuf != NULL)
1789744bfb21SJohn Baldwin 		m_freem(pkt->p_mbuf);
1790744bfb21SJohn Baldwin 	uma_zfree(wg_packet_zone, pkt);
1791744bfb21SJohn Baldwin }
1792744bfb21SJohn Baldwin 
1793744bfb21SJohn Baldwin static void
wg_queue_init(struct wg_queue * queue,const char * name)1794744bfb21SJohn Baldwin wg_queue_init(struct wg_queue *queue, const char *name)
1795744bfb21SJohn Baldwin {
1796744bfb21SJohn Baldwin 	mtx_init(&queue->q_mtx, name, NULL, MTX_DEF);
1797744bfb21SJohn Baldwin 	STAILQ_INIT(&queue->q_queue);
1798744bfb21SJohn Baldwin 	queue->q_len = 0;
1799744bfb21SJohn Baldwin }
1800744bfb21SJohn Baldwin 
1801744bfb21SJohn Baldwin static void
wg_queue_deinit(struct wg_queue * queue)1802744bfb21SJohn Baldwin wg_queue_deinit(struct wg_queue *queue)
1803744bfb21SJohn Baldwin {
1804744bfb21SJohn Baldwin 	wg_queue_purge(queue);
1805744bfb21SJohn Baldwin 	mtx_destroy(&queue->q_mtx);
1806744bfb21SJohn Baldwin }
1807744bfb21SJohn Baldwin 
1808744bfb21SJohn Baldwin static size_t
wg_queue_len(struct wg_queue * queue)1809744bfb21SJohn Baldwin wg_queue_len(struct wg_queue *queue)
1810744bfb21SJohn Baldwin {
1811744bfb21SJohn Baldwin 	return (queue->q_len);
1812744bfb21SJohn Baldwin }
1813744bfb21SJohn Baldwin 
1814744bfb21SJohn Baldwin static int
wg_queue_enqueue_handshake(struct wg_queue * hs,struct wg_packet * pkt)1815744bfb21SJohn Baldwin wg_queue_enqueue_handshake(struct wg_queue *hs, struct wg_packet *pkt)
1816744bfb21SJohn Baldwin {
1817744bfb21SJohn Baldwin 	int ret = 0;
1818744bfb21SJohn Baldwin 	mtx_lock(&hs->q_mtx);
1819744bfb21SJohn Baldwin 	if (hs->q_len < MAX_QUEUED_HANDSHAKES) {
1820744bfb21SJohn Baldwin 		STAILQ_INSERT_TAIL(&hs->q_queue, pkt, p_parallel);
1821744bfb21SJohn Baldwin 		hs->q_len++;
1822744bfb21SJohn Baldwin 	} else {
1823744bfb21SJohn Baldwin 		ret = ENOBUFS;
1824744bfb21SJohn Baldwin 	}
1825744bfb21SJohn Baldwin 	mtx_unlock(&hs->q_mtx);
1826744bfb21SJohn Baldwin 	if (ret != 0)
1827744bfb21SJohn Baldwin 		wg_packet_free(pkt);
1828744bfb21SJohn Baldwin 	return (ret);
1829744bfb21SJohn Baldwin }
1830744bfb21SJohn Baldwin 
1831744bfb21SJohn Baldwin static struct wg_packet *
wg_queue_dequeue_handshake(struct wg_queue * hs)1832744bfb21SJohn Baldwin wg_queue_dequeue_handshake(struct wg_queue *hs)
1833744bfb21SJohn Baldwin {
1834744bfb21SJohn Baldwin 	struct wg_packet *pkt;
1835744bfb21SJohn Baldwin 	mtx_lock(&hs->q_mtx);
1836744bfb21SJohn Baldwin 	if ((pkt = STAILQ_FIRST(&hs->q_queue)) != NULL) {
1837744bfb21SJohn Baldwin 		STAILQ_REMOVE_HEAD(&hs->q_queue, p_parallel);
1838744bfb21SJohn Baldwin 		hs->q_len--;
1839744bfb21SJohn Baldwin 	}
1840744bfb21SJohn Baldwin 	mtx_unlock(&hs->q_mtx);
1841744bfb21SJohn Baldwin 	return (pkt);
1842744bfb21SJohn Baldwin }
1843744bfb21SJohn Baldwin 
1844744bfb21SJohn Baldwin static void
wg_queue_push_staged(struct wg_queue * staged,struct wg_packet * pkt)1845744bfb21SJohn Baldwin wg_queue_push_staged(struct wg_queue *staged, struct wg_packet *pkt)
1846744bfb21SJohn Baldwin {
1847744bfb21SJohn Baldwin 	struct wg_packet *old = NULL;
1848744bfb21SJohn Baldwin 
1849744bfb21SJohn Baldwin 	mtx_lock(&staged->q_mtx);
1850744bfb21SJohn Baldwin 	if (staged->q_len >= MAX_STAGED_PKT) {
1851744bfb21SJohn Baldwin 		old = STAILQ_FIRST(&staged->q_queue);
1852744bfb21SJohn Baldwin 		STAILQ_REMOVE_HEAD(&staged->q_queue, p_parallel);
1853744bfb21SJohn Baldwin 		staged->q_len--;
1854744bfb21SJohn Baldwin 	}
1855744bfb21SJohn Baldwin 	STAILQ_INSERT_TAIL(&staged->q_queue, pkt, p_parallel);
1856744bfb21SJohn Baldwin 	staged->q_len++;
1857744bfb21SJohn Baldwin 	mtx_unlock(&staged->q_mtx);
1858744bfb21SJohn Baldwin 
1859744bfb21SJohn Baldwin 	if (old != NULL)
1860744bfb21SJohn Baldwin 		wg_packet_free(old);
1861744bfb21SJohn Baldwin }
1862744bfb21SJohn Baldwin 
1863744bfb21SJohn Baldwin static void
wg_queue_enlist_staged(struct wg_queue * staged,struct wg_packet_list * list)1864744bfb21SJohn Baldwin wg_queue_enlist_staged(struct wg_queue *staged, struct wg_packet_list *list)
1865744bfb21SJohn Baldwin {
1866744bfb21SJohn Baldwin 	struct wg_packet *pkt, *tpkt;
1867744bfb21SJohn Baldwin 	STAILQ_FOREACH_SAFE(pkt, list, p_parallel, tpkt)
1868744bfb21SJohn Baldwin 		wg_queue_push_staged(staged, pkt);
1869744bfb21SJohn Baldwin }
1870744bfb21SJohn Baldwin 
1871744bfb21SJohn Baldwin static void
wg_queue_delist_staged(struct wg_queue * staged,struct wg_packet_list * list)1872744bfb21SJohn Baldwin wg_queue_delist_staged(struct wg_queue *staged, struct wg_packet_list *list)
1873744bfb21SJohn Baldwin {
1874744bfb21SJohn Baldwin 	STAILQ_INIT(list);
1875744bfb21SJohn Baldwin 	mtx_lock(&staged->q_mtx);
1876744bfb21SJohn Baldwin 	STAILQ_CONCAT(list, &staged->q_queue);
1877744bfb21SJohn Baldwin 	staged->q_len = 0;
1878744bfb21SJohn Baldwin 	mtx_unlock(&staged->q_mtx);
1879744bfb21SJohn Baldwin }
1880744bfb21SJohn Baldwin 
1881744bfb21SJohn Baldwin static void
wg_queue_purge(struct wg_queue * staged)1882744bfb21SJohn Baldwin wg_queue_purge(struct wg_queue *staged)
1883744bfb21SJohn Baldwin {
1884744bfb21SJohn Baldwin 	struct wg_packet_list list;
1885744bfb21SJohn Baldwin 	struct wg_packet *pkt, *tpkt;
1886744bfb21SJohn Baldwin 	wg_queue_delist_staged(staged, &list);
1887744bfb21SJohn Baldwin 	STAILQ_FOREACH_SAFE(pkt, &list, p_parallel, tpkt)
1888744bfb21SJohn Baldwin 		wg_packet_free(pkt);
1889744bfb21SJohn Baldwin }
1890744bfb21SJohn Baldwin 
1891744bfb21SJohn Baldwin static int
wg_queue_both(struct wg_queue * parallel,struct wg_queue * serial,struct wg_packet * pkt)1892744bfb21SJohn Baldwin wg_queue_both(struct wg_queue *parallel, struct wg_queue *serial, struct wg_packet *pkt)
1893744bfb21SJohn Baldwin {
1894744bfb21SJohn Baldwin 	pkt->p_state = WG_PACKET_UNCRYPTED;
1895744bfb21SJohn Baldwin 
1896744bfb21SJohn Baldwin 	mtx_lock(&serial->q_mtx);
1897744bfb21SJohn Baldwin 	if (serial->q_len < MAX_QUEUED_PKT) {
1898744bfb21SJohn Baldwin 		serial->q_len++;
1899744bfb21SJohn Baldwin 		STAILQ_INSERT_TAIL(&serial->q_queue, pkt, p_serial);
1900744bfb21SJohn Baldwin 	} else {
1901744bfb21SJohn Baldwin 		mtx_unlock(&serial->q_mtx);
1902744bfb21SJohn Baldwin 		wg_packet_free(pkt);
1903744bfb21SJohn Baldwin 		return (ENOBUFS);
1904744bfb21SJohn Baldwin 	}
1905744bfb21SJohn Baldwin 	mtx_unlock(&serial->q_mtx);
1906744bfb21SJohn Baldwin 
1907744bfb21SJohn Baldwin 	mtx_lock(&parallel->q_mtx);
1908744bfb21SJohn Baldwin 	if (parallel->q_len < MAX_QUEUED_PKT) {
1909744bfb21SJohn Baldwin 		parallel->q_len++;
1910744bfb21SJohn Baldwin 		STAILQ_INSERT_TAIL(&parallel->q_queue, pkt, p_parallel);
1911744bfb21SJohn Baldwin 	} else {
1912744bfb21SJohn Baldwin 		mtx_unlock(&parallel->q_mtx);
1913744bfb21SJohn Baldwin 		pkt->p_state = WG_PACKET_DEAD;
1914744bfb21SJohn Baldwin 		return (ENOBUFS);
1915744bfb21SJohn Baldwin 	}
1916744bfb21SJohn Baldwin 	mtx_unlock(&parallel->q_mtx);
1917744bfb21SJohn Baldwin 
1918744bfb21SJohn Baldwin 	return (0);
1919744bfb21SJohn Baldwin }
1920744bfb21SJohn Baldwin 
1921744bfb21SJohn Baldwin static struct wg_packet *
wg_queue_dequeue_serial(struct wg_queue * serial)1922744bfb21SJohn Baldwin wg_queue_dequeue_serial(struct wg_queue *serial)
1923744bfb21SJohn Baldwin {
1924744bfb21SJohn Baldwin 	struct wg_packet *pkt = NULL;
1925744bfb21SJohn Baldwin 	mtx_lock(&serial->q_mtx);
1926744bfb21SJohn Baldwin 	if (serial->q_len > 0 && STAILQ_FIRST(&serial->q_queue)->p_state != WG_PACKET_UNCRYPTED) {
1927744bfb21SJohn Baldwin 		serial->q_len--;
1928744bfb21SJohn Baldwin 		pkt = STAILQ_FIRST(&serial->q_queue);
1929744bfb21SJohn Baldwin 		STAILQ_REMOVE_HEAD(&serial->q_queue, p_serial);
1930744bfb21SJohn Baldwin 	}
1931744bfb21SJohn Baldwin 	mtx_unlock(&serial->q_mtx);
1932744bfb21SJohn Baldwin 	return (pkt);
1933744bfb21SJohn Baldwin }
1934744bfb21SJohn Baldwin 
1935744bfb21SJohn Baldwin static struct wg_packet *
wg_queue_dequeue_parallel(struct wg_queue * parallel)1936744bfb21SJohn Baldwin wg_queue_dequeue_parallel(struct wg_queue *parallel)
1937744bfb21SJohn Baldwin {
1938744bfb21SJohn Baldwin 	struct wg_packet *pkt = NULL;
1939744bfb21SJohn Baldwin 	mtx_lock(&parallel->q_mtx);
1940744bfb21SJohn Baldwin 	if (parallel->q_len > 0) {
1941744bfb21SJohn Baldwin 		parallel->q_len--;
1942744bfb21SJohn Baldwin 		pkt = STAILQ_FIRST(&parallel->q_queue);
1943744bfb21SJohn Baldwin 		STAILQ_REMOVE_HEAD(&parallel->q_queue, p_parallel);
1944744bfb21SJohn Baldwin 	}
1945744bfb21SJohn Baldwin 	mtx_unlock(&parallel->q_mtx);
1946744bfb21SJohn Baldwin 	return (pkt);
1947744bfb21SJohn Baldwin }
1948744bfb21SJohn Baldwin 
1949744bfb21SJohn Baldwin static bool
wg_input(struct mbuf * m,int offset,struct inpcb * inpcb,const struct sockaddr * sa,void * _sc)1950744bfb21SJohn Baldwin wg_input(struct mbuf *m, int offset, struct inpcb *inpcb,
1951744bfb21SJohn Baldwin     const struct sockaddr *sa, void *_sc)
1952744bfb21SJohn Baldwin {
1953744bfb21SJohn Baldwin #ifdef INET
1954744bfb21SJohn Baldwin 	const struct sockaddr_in	*sin;
1955744bfb21SJohn Baldwin #endif
1956744bfb21SJohn Baldwin #ifdef INET6
1957744bfb21SJohn Baldwin 	const struct sockaddr_in6	*sin6;
1958744bfb21SJohn Baldwin #endif
1959744bfb21SJohn Baldwin 	struct noise_remote		*remote;
1960744bfb21SJohn Baldwin 	struct wg_pkt_data		*data;
1961744bfb21SJohn Baldwin 	struct wg_packet		*pkt;
1962744bfb21SJohn Baldwin 	struct wg_peer			*peer;
1963744bfb21SJohn Baldwin 	struct wg_softc			*sc = _sc;
1964744bfb21SJohn Baldwin 	struct mbuf			*defragged;
1965744bfb21SJohn Baldwin 
1966744bfb21SJohn Baldwin 	defragged = m_defrag(m, M_NOWAIT);
1967744bfb21SJohn Baldwin 	if (defragged)
1968744bfb21SJohn Baldwin 		m = defragged;
1969744bfb21SJohn Baldwin 	m = m_unshare(m, M_NOWAIT);
1970744bfb21SJohn Baldwin 	if (!m) {
1971744bfb21SJohn Baldwin 		if_inc_counter(sc->sc_ifp, IFCOUNTER_IQDROPS, 1);
1972744bfb21SJohn Baldwin 		return true;
1973744bfb21SJohn Baldwin 	}
1974744bfb21SJohn Baldwin 
1975744bfb21SJohn Baldwin 	/* Caller provided us with `sa`, no need for this header. */
1976744bfb21SJohn Baldwin 	m_adj(m, offset + sizeof(struct udphdr));
1977744bfb21SJohn Baldwin 
1978744bfb21SJohn Baldwin 	/* Pullup enough to read packet type */
1979744bfb21SJohn Baldwin 	if ((m = m_pullup(m, sizeof(uint32_t))) == NULL) {
1980744bfb21SJohn Baldwin 		if_inc_counter(sc->sc_ifp, IFCOUNTER_IQDROPS, 1);
1981744bfb21SJohn Baldwin 		return true;
1982744bfb21SJohn Baldwin 	}
1983744bfb21SJohn Baldwin 
1984744bfb21SJohn Baldwin 	if ((pkt = wg_packet_alloc(m)) == NULL) {
1985744bfb21SJohn Baldwin 		if_inc_counter(sc->sc_ifp, IFCOUNTER_IQDROPS, 1);
1986744bfb21SJohn Baldwin 		m_freem(m);
1987744bfb21SJohn Baldwin 		return true;
1988744bfb21SJohn Baldwin 	}
1989744bfb21SJohn Baldwin 
1990744bfb21SJohn Baldwin 	/* Save send/recv address and port for later. */
1991744bfb21SJohn Baldwin 	switch (sa->sa_family) {
1992744bfb21SJohn Baldwin #ifdef INET
1993744bfb21SJohn Baldwin 	case AF_INET:
1994744bfb21SJohn Baldwin 		sin = (const struct sockaddr_in *)sa;
1995744bfb21SJohn Baldwin 		pkt->p_endpoint.e_remote.r_sin = sin[0];
1996744bfb21SJohn Baldwin 		pkt->p_endpoint.e_local.l_in = sin[1].sin_addr;
1997744bfb21SJohn Baldwin 		break;
1998744bfb21SJohn Baldwin #endif
1999744bfb21SJohn Baldwin #ifdef INET6
2000744bfb21SJohn Baldwin 	case AF_INET6:
2001744bfb21SJohn Baldwin 		sin6 = (const struct sockaddr_in6 *)sa;
2002744bfb21SJohn Baldwin 		pkt->p_endpoint.e_remote.r_sin6 = sin6[0];
2003744bfb21SJohn Baldwin 		pkt->p_endpoint.e_local.l_in6 = sin6[1].sin6_addr;
2004744bfb21SJohn Baldwin 		break;
2005744bfb21SJohn Baldwin #endif
2006744bfb21SJohn Baldwin 	default:
2007744bfb21SJohn Baldwin 		goto error;
2008744bfb21SJohn Baldwin 	}
2009744bfb21SJohn Baldwin 
2010744bfb21SJohn Baldwin 	if ((m->m_pkthdr.len == sizeof(struct wg_pkt_initiation) &&
2011744bfb21SJohn Baldwin 		*mtod(m, uint32_t *) == WG_PKT_INITIATION) ||
2012744bfb21SJohn Baldwin 	    (m->m_pkthdr.len == sizeof(struct wg_pkt_response) &&
2013744bfb21SJohn Baldwin 		*mtod(m, uint32_t *) == WG_PKT_RESPONSE) ||
2014744bfb21SJohn Baldwin 	    (m->m_pkthdr.len == sizeof(struct wg_pkt_cookie) &&
2015744bfb21SJohn Baldwin 		*mtod(m, uint32_t *) == WG_PKT_COOKIE)) {
2016744bfb21SJohn Baldwin 
2017744bfb21SJohn Baldwin 		if (wg_queue_enqueue_handshake(&sc->sc_handshake_queue, pkt) != 0) {
2018744bfb21SJohn Baldwin 			if_inc_counter(sc->sc_ifp, IFCOUNTER_IQDROPS, 1);
2019744bfb21SJohn Baldwin 			DPRINTF(sc, "Dropping handshake packet\n");
2020744bfb21SJohn Baldwin 		}
2021744bfb21SJohn Baldwin 		GROUPTASK_ENQUEUE(&sc->sc_handshake);
2022744bfb21SJohn Baldwin 	} else if (m->m_pkthdr.len >= sizeof(struct wg_pkt_data) +
2023744bfb21SJohn Baldwin 	    NOISE_AUTHTAG_LEN && *mtod(m, uint32_t *) == WG_PKT_DATA) {
2024744bfb21SJohn Baldwin 
2025744bfb21SJohn Baldwin 		/* Pullup whole header to read r_idx below. */
2026744bfb21SJohn Baldwin 		if ((pkt->p_mbuf = m_pullup(m, sizeof(struct wg_pkt_data))) == NULL)
2027744bfb21SJohn Baldwin 			goto error;
2028744bfb21SJohn Baldwin 
2029744bfb21SJohn Baldwin 		data = mtod(pkt->p_mbuf, struct wg_pkt_data *);
2030744bfb21SJohn Baldwin 		if ((pkt->p_keypair = noise_keypair_lookup(sc->sc_local, data->r_idx)) == NULL)
2031744bfb21SJohn Baldwin 			goto error;
2032744bfb21SJohn Baldwin 
2033744bfb21SJohn Baldwin 		remote = noise_keypair_remote(pkt->p_keypair);
2034744bfb21SJohn Baldwin 		peer = noise_remote_arg(remote);
2035744bfb21SJohn Baldwin 		if (wg_queue_both(&sc->sc_decrypt_parallel, &peer->p_decrypt_serial, pkt) != 0)
2036744bfb21SJohn Baldwin 			if_inc_counter(sc->sc_ifp, IFCOUNTER_IQDROPS, 1);
2037744bfb21SJohn Baldwin 		wg_decrypt_dispatch(sc);
2038744bfb21SJohn Baldwin 		noise_remote_put(remote);
2039744bfb21SJohn Baldwin 	} else {
2040744bfb21SJohn Baldwin 		goto error;
2041744bfb21SJohn Baldwin 	}
2042744bfb21SJohn Baldwin 	return true;
2043744bfb21SJohn Baldwin error:
2044744bfb21SJohn Baldwin 	if_inc_counter(sc->sc_ifp, IFCOUNTER_IERRORS, 1);
2045744bfb21SJohn Baldwin 	wg_packet_free(pkt);
2046744bfb21SJohn Baldwin 	return true;
2047744bfb21SJohn Baldwin }
2048744bfb21SJohn Baldwin 
2049744bfb21SJohn Baldwin static void
wg_peer_send_staged(struct wg_peer * peer)2050744bfb21SJohn Baldwin wg_peer_send_staged(struct wg_peer *peer)
2051744bfb21SJohn Baldwin {
2052744bfb21SJohn Baldwin 	struct wg_packet_list	 list;
2053744bfb21SJohn Baldwin 	struct noise_keypair	*keypair;
2054744bfb21SJohn Baldwin 	struct wg_packet	*pkt, *tpkt;
2055744bfb21SJohn Baldwin 	struct wg_softc		*sc = peer->p_sc;
2056744bfb21SJohn Baldwin 
2057744bfb21SJohn Baldwin 	wg_queue_delist_staged(&peer->p_stage_queue, &list);
2058744bfb21SJohn Baldwin 
2059744bfb21SJohn Baldwin 	if (STAILQ_EMPTY(&list))
2060744bfb21SJohn Baldwin 		return;
2061744bfb21SJohn Baldwin 
2062744bfb21SJohn Baldwin 	if ((keypair = noise_keypair_current(peer->p_remote)) == NULL)
2063744bfb21SJohn Baldwin 		goto error;
2064744bfb21SJohn Baldwin 
2065744bfb21SJohn Baldwin 	STAILQ_FOREACH(pkt, &list, p_parallel) {
2066744bfb21SJohn Baldwin 		if (noise_keypair_nonce_next(keypair, &pkt->p_nonce) != 0)
2067744bfb21SJohn Baldwin 			goto error_keypair;
2068744bfb21SJohn Baldwin 	}
2069744bfb21SJohn Baldwin 	STAILQ_FOREACH_SAFE(pkt, &list, p_parallel, tpkt) {
2070744bfb21SJohn Baldwin 		pkt->p_keypair = noise_keypair_ref(keypair);
2071744bfb21SJohn Baldwin 		if (wg_queue_both(&sc->sc_encrypt_parallel, &peer->p_encrypt_serial, pkt) != 0)
2072744bfb21SJohn Baldwin 			if_inc_counter(sc->sc_ifp, IFCOUNTER_OQDROPS, 1);
2073744bfb21SJohn Baldwin 	}
2074744bfb21SJohn Baldwin 	wg_encrypt_dispatch(sc);
2075744bfb21SJohn Baldwin 	noise_keypair_put(keypair);
2076744bfb21SJohn Baldwin 	return;
2077744bfb21SJohn Baldwin 
2078744bfb21SJohn Baldwin error_keypair:
2079744bfb21SJohn Baldwin 	noise_keypair_put(keypair);
2080744bfb21SJohn Baldwin error:
2081744bfb21SJohn Baldwin 	wg_queue_enlist_staged(&peer->p_stage_queue, &list);
2082744bfb21SJohn Baldwin 	wg_timers_event_want_initiation(peer);
2083744bfb21SJohn Baldwin }
2084744bfb21SJohn Baldwin 
2085744bfb21SJohn Baldwin static inline void
xmit_err(if_t ifp,struct mbuf * m,struct wg_packet * pkt,sa_family_t af)208687e72834SJustin Hibbits xmit_err(if_t ifp, struct mbuf *m, struct wg_packet *pkt, sa_family_t af)
2087744bfb21SJohn Baldwin {
2088744bfb21SJohn Baldwin 	if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
2089744bfb21SJohn Baldwin 	switch (af) {
2090744bfb21SJohn Baldwin #ifdef INET
2091744bfb21SJohn Baldwin 	case AF_INET:
2092744bfb21SJohn Baldwin 		icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0);
2093744bfb21SJohn Baldwin 		if (pkt)
2094744bfb21SJohn Baldwin 			pkt->p_mbuf = NULL;
2095744bfb21SJohn Baldwin 		m = NULL;
2096744bfb21SJohn Baldwin 		break;
2097744bfb21SJohn Baldwin #endif
2098744bfb21SJohn Baldwin #ifdef INET6
2099744bfb21SJohn Baldwin 	case AF_INET6:
2100744bfb21SJohn Baldwin 		icmp6_error(m, ICMP6_DST_UNREACH, 0, 0);
2101744bfb21SJohn Baldwin 		if (pkt)
2102744bfb21SJohn Baldwin 			pkt->p_mbuf = NULL;
2103744bfb21SJohn Baldwin 		m = NULL;
2104744bfb21SJohn Baldwin 		break;
2105744bfb21SJohn Baldwin #endif
2106744bfb21SJohn Baldwin 	}
2107744bfb21SJohn Baldwin 	if (pkt)
2108744bfb21SJohn Baldwin 		wg_packet_free(pkt);
2109744bfb21SJohn Baldwin 	else if (m)
2110744bfb21SJohn Baldwin 		m_freem(m);
2111744bfb21SJohn Baldwin }
2112744bfb21SJohn Baldwin 
2113744bfb21SJohn Baldwin static int
wg_xmit(if_t ifp,struct mbuf * m,sa_family_t af,uint32_t mtu)211487e72834SJustin Hibbits wg_xmit(if_t ifp, struct mbuf *m, sa_family_t af, uint32_t mtu)
2115744bfb21SJohn Baldwin {
2116744bfb21SJohn Baldwin 	struct wg_packet	*pkt = NULL;
211787e72834SJustin Hibbits 	struct wg_softc		*sc = if_getsoftc(ifp);
2118744bfb21SJohn Baldwin 	struct wg_peer		*peer;
2119744bfb21SJohn Baldwin 	int			 rc = 0;
2120744bfb21SJohn Baldwin 	sa_family_t		 peer_af;
2121744bfb21SJohn Baldwin 
2122744bfb21SJohn Baldwin 	/* Work around lifetime issue in the ipv6 mld code. */
212387e72834SJustin Hibbits 	if (__predict_false((if_getflags(ifp) & IFF_DYING) || !sc)) {
2124744bfb21SJohn Baldwin 		rc = ENXIO;
2125744bfb21SJohn Baldwin 		goto err_xmit;
2126744bfb21SJohn Baldwin 	}
2127744bfb21SJohn Baldwin 
2128744bfb21SJohn Baldwin 	if ((pkt = wg_packet_alloc(m)) == NULL) {
2129744bfb21SJohn Baldwin 		rc = ENOBUFS;
2130744bfb21SJohn Baldwin 		goto err_xmit;
2131744bfb21SJohn Baldwin 	}
2132744bfb21SJohn Baldwin 	pkt->p_mtu = mtu;
2133744bfb21SJohn Baldwin 	pkt->p_af = af;
2134744bfb21SJohn Baldwin 
2135744bfb21SJohn Baldwin 	if (af == AF_INET) {
2136744bfb21SJohn Baldwin 		peer = wg_aip_lookup(sc, AF_INET, &mtod(m, struct ip *)->ip_dst);
2137744bfb21SJohn Baldwin 	} else if (af == AF_INET6) {
2138744bfb21SJohn Baldwin 		peer = wg_aip_lookup(sc, AF_INET6, &mtod(m, struct ip6_hdr *)->ip6_dst);
2139744bfb21SJohn Baldwin 	} else {
2140744bfb21SJohn Baldwin 		rc = EAFNOSUPPORT;
2141744bfb21SJohn Baldwin 		goto err_xmit;
2142744bfb21SJohn Baldwin 	}
2143744bfb21SJohn Baldwin 
2144744bfb21SJohn Baldwin 	BPF_MTAP2_AF(ifp, m, pkt->p_af);
2145744bfb21SJohn Baldwin 
2146744bfb21SJohn Baldwin 	if (__predict_false(peer == NULL)) {
214763613e3bSMark Johnston 		rc = ENETUNREACH;
2148744bfb21SJohn Baldwin 		goto err_xmit;
2149744bfb21SJohn Baldwin 	}
2150744bfb21SJohn Baldwin 
2151744bfb21SJohn Baldwin 	if (__predict_false(if_tunnel_check_nesting(ifp, m, MTAG_WGLOOP, MAX_LOOPS))) {
2152744bfb21SJohn Baldwin 		DPRINTF(sc, "Packet looped");
2153744bfb21SJohn Baldwin 		rc = ELOOP;
2154744bfb21SJohn Baldwin 		goto err_peer;
2155744bfb21SJohn Baldwin 	}
2156744bfb21SJohn Baldwin 
2157744bfb21SJohn Baldwin 	peer_af = peer->p_endpoint.e_remote.r_sa.sa_family;
2158744bfb21SJohn Baldwin 	if (__predict_false(peer_af != AF_INET && peer_af != AF_INET6)) {
2159744bfb21SJohn Baldwin 		DPRINTF(sc, "No valid endpoint has been configured or "
2160744bfb21SJohn Baldwin 			    "discovered for peer %" PRIu64 "\n", peer->p_id);
2161744bfb21SJohn Baldwin 		rc = EHOSTUNREACH;
2162744bfb21SJohn Baldwin 		goto err_peer;
2163744bfb21SJohn Baldwin 	}
2164744bfb21SJohn Baldwin 
2165744bfb21SJohn Baldwin 	wg_queue_push_staged(&peer->p_stage_queue, pkt);
2166744bfb21SJohn Baldwin 	wg_peer_send_staged(peer);
2167744bfb21SJohn Baldwin 	noise_remote_put(peer->p_remote);
2168744bfb21SJohn Baldwin 	return (0);
2169744bfb21SJohn Baldwin 
2170744bfb21SJohn Baldwin err_peer:
2171744bfb21SJohn Baldwin 	noise_remote_put(peer->p_remote);
2172744bfb21SJohn Baldwin err_xmit:
2173744bfb21SJohn Baldwin 	xmit_err(ifp, m, pkt, af);
2174744bfb21SJohn Baldwin 	return (rc);
2175744bfb21SJohn Baldwin }
2176744bfb21SJohn Baldwin 
2177744bfb21SJohn Baldwin static inline int
determine_af_and_pullup(struct mbuf ** m,sa_family_t * af)2178744bfb21SJohn Baldwin determine_af_and_pullup(struct mbuf **m, sa_family_t *af)
2179744bfb21SJohn Baldwin {
2180744bfb21SJohn Baldwin 	u_char ipv;
2181744bfb21SJohn Baldwin 	if ((*m)->m_pkthdr.len >= sizeof(struct ip6_hdr))
2182744bfb21SJohn Baldwin 		*m = m_pullup(*m, sizeof(struct ip6_hdr));
2183744bfb21SJohn Baldwin 	else if ((*m)->m_pkthdr.len >= sizeof(struct ip))
2184744bfb21SJohn Baldwin 		*m = m_pullup(*m, sizeof(struct ip));
2185744bfb21SJohn Baldwin 	else
2186744bfb21SJohn Baldwin 		return (EAFNOSUPPORT);
2187744bfb21SJohn Baldwin 	if (*m == NULL)
2188744bfb21SJohn Baldwin 		return (ENOBUFS);
2189744bfb21SJohn Baldwin 	ipv = mtod(*m, struct ip *)->ip_v;
2190744bfb21SJohn Baldwin 	if (ipv == 4)
2191744bfb21SJohn Baldwin 		*af = AF_INET;
2192744bfb21SJohn Baldwin 	else if (ipv == 6 && (*m)->m_pkthdr.len >= sizeof(struct ip6_hdr))
2193744bfb21SJohn Baldwin 		*af = AF_INET6;
2194744bfb21SJohn Baldwin 	else
2195744bfb21SJohn Baldwin 		return (EAFNOSUPPORT);
2196744bfb21SJohn Baldwin 	return (0);
2197744bfb21SJohn Baldwin }
2198744bfb21SJohn Baldwin 
2199bf454ca8SMark Johnston static int
determine_ethertype_and_pullup(struct mbuf ** m,int * etp)2200bf454ca8SMark Johnston determine_ethertype_and_pullup(struct mbuf **m, int *etp)
2201bf454ca8SMark Johnston {
2202bf454ca8SMark Johnston 	struct ether_header *eh;
2203bf454ca8SMark Johnston 
2204bf454ca8SMark Johnston 	*m = m_pullup(*m, sizeof(struct ether_header));
2205bf454ca8SMark Johnston 	if (__predict_false(*m == NULL))
2206bf454ca8SMark Johnston 		return (ENOBUFS);
2207bf454ca8SMark Johnston 	eh = mtod(*m, struct ether_header *);
2208bf454ca8SMark Johnston 	*etp = ntohs(eh->ether_type);
2209bf454ca8SMark Johnston 	if (*etp != ETHERTYPE_IP && *etp != ETHERTYPE_IPV6)
2210bf454ca8SMark Johnston 		return (EAFNOSUPPORT);
2211bf454ca8SMark Johnston 	return (0);
2212bf454ca8SMark Johnston }
2213bf454ca8SMark Johnston 
2214bf454ca8SMark Johnston /*
2215bf454ca8SMark Johnston  * This should only be invoked by netmap, via nm_os_generic_xmit_frame(), to
2216bf454ca8SMark Johnston  * transmit packets from the netmap TX ring.
2217bf454ca8SMark Johnston  */
2218744bfb21SJohn Baldwin static int
wg_transmit(if_t ifp,struct mbuf * m)221987e72834SJustin Hibbits wg_transmit(if_t ifp, struct mbuf *m)
2220744bfb21SJohn Baldwin {
2221744bfb21SJohn Baldwin 	sa_family_t af;
2222bf454ca8SMark Johnston 	int et, ret;
2223744bfb21SJohn Baldwin 	struct mbuf *defragged;
2224744bfb21SJohn Baldwin 
2225bf454ca8SMark Johnston 	KASSERT((if_getcapenable(ifp) & IFCAP_NETMAP) != 0,
2226bf454ca8SMark Johnston 	    ("%s: ifp %p is not in netmap mode", __func__, ifp));
2227bf454ca8SMark Johnston 
2228744bfb21SJohn Baldwin 	defragged = m_defrag(m, M_NOWAIT);
2229744bfb21SJohn Baldwin 	if (defragged)
2230744bfb21SJohn Baldwin 		m = defragged;
2231744bfb21SJohn Baldwin 	m = m_unshare(m, M_NOWAIT);
2232744bfb21SJohn Baldwin 	if (!m) {
2233744bfb21SJohn Baldwin 		xmit_err(ifp, m, NULL, AF_UNSPEC);
2234744bfb21SJohn Baldwin 		return (ENOBUFS);
2235744bfb21SJohn Baldwin 	}
2236744bfb21SJohn Baldwin 
2237bf454ca8SMark Johnston 	ret = determine_ethertype_and_pullup(&m, &et);
2238bf454ca8SMark Johnston 	if (ret) {
2239bf454ca8SMark Johnston 		xmit_err(ifp, m, NULL, AF_UNSPEC);
2240bf454ca8SMark Johnston 		return (ret);
2241bf454ca8SMark Johnston 	}
2242bf454ca8SMark Johnston 	m_adj(m, sizeof(struct ether_header));
2243bf454ca8SMark Johnston 
2244744bfb21SJohn Baldwin 	ret = determine_af_and_pullup(&m, &af);
2245744bfb21SJohn Baldwin 	if (ret) {
2246744bfb21SJohn Baldwin 		xmit_err(ifp, m, NULL, AF_UNSPEC);
2247744bfb21SJohn Baldwin 		return (ret);
2248744bfb21SJohn Baldwin 	}
2249bf454ca8SMark Johnston 
2250bf454ca8SMark Johnston 	/*
2251bf454ca8SMark Johnston 	 * netmap only gets to see transient errors, since it handles errors by
2252bf454ca8SMark Johnston 	 * refusing to advance the transmit ring and retrying later.
2253bf454ca8SMark Johnston 	 */
2254bf454ca8SMark Johnston 	ret = wg_xmit(ifp, m, af, if_getmtu(ifp));
2255bf454ca8SMark Johnston 	if (ret == ENOBUFS)
2256bf454ca8SMark Johnston 		return (ret);
2257bf454ca8SMark Johnston 	return (0);
2258744bfb21SJohn Baldwin }
2259744bfb21SJohn Baldwin 
22605515e887SMark Johnston #ifdef DEV_NETMAP
2261bf454ca8SMark Johnston /*
2262bf454ca8SMark Johnston  * This should only be invoked by netmap, via nm_os_send_up(), to process
2263bf454ca8SMark Johnston  * packets from the host TX ring.
2264bf454ca8SMark Johnston  */
2265bf454ca8SMark Johnston static void
wg_if_input(if_t ifp,struct mbuf * m)2266bf454ca8SMark Johnston wg_if_input(if_t ifp, struct mbuf *m)
2267bf454ca8SMark Johnston {
2268bf454ca8SMark Johnston 	int et;
2269bf454ca8SMark Johnston 
2270bf454ca8SMark Johnston 	KASSERT((if_getcapenable(ifp) & IFCAP_NETMAP) != 0,
2271bf454ca8SMark Johnston 	    ("%s: ifp %p is not in netmap mode", __func__, ifp));
2272bf454ca8SMark Johnston 
2273bf454ca8SMark Johnston 	if (determine_ethertype_and_pullup(&m, &et) != 0) {
2274bf454ca8SMark Johnston 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2275bf454ca8SMark Johnston 		m_freem(m);
2276bf454ca8SMark Johnston 		return;
2277bf454ca8SMark Johnston 	}
2278bf454ca8SMark Johnston 	CURVNET_SET(if_getvnet(ifp));
2279bf454ca8SMark Johnston 	switch (et) {
2280bf454ca8SMark Johnston 	case ETHERTYPE_IP:
2281bf454ca8SMark Johnston 		m_adj(m, sizeof(struct ether_header));
2282bf454ca8SMark Johnston 		netisr_dispatch(NETISR_IP, m);
2283bf454ca8SMark Johnston 		break;
2284bf454ca8SMark Johnston 	case ETHERTYPE_IPV6:
2285bf454ca8SMark Johnston 		m_adj(m, sizeof(struct ether_header));
2286bf454ca8SMark Johnston 		netisr_dispatch(NETISR_IPV6, m);
2287bf454ca8SMark Johnston 		break;
2288bf454ca8SMark Johnston 	default:
2289bf454ca8SMark Johnston 		__assert_unreachable();
2290bf454ca8SMark Johnston 	}
2291bf454ca8SMark Johnston 	CURVNET_RESTORE();
2292bf454ca8SMark Johnston }
2293bf454ca8SMark Johnston 
2294bf454ca8SMark Johnston /*
2295bf454ca8SMark Johnston  * Deliver a packet to the host RX ring.  Because the interface is in netmap
2296bf454ca8SMark Johnston  * mode, the if_transmit() call should pass the packet to netmap_transmit().
2297bf454ca8SMark Johnston  */
2298bf454ca8SMark Johnston static int
wg_xmit_netmap(if_t ifp,struct mbuf * m,int af)2299bf454ca8SMark Johnston wg_xmit_netmap(if_t ifp, struct mbuf *m, int af)
2300bf454ca8SMark Johnston {
2301bf454ca8SMark Johnston 	struct ether_header *eh;
2302bf454ca8SMark Johnston 
2303bf454ca8SMark Johnston 	if (__predict_false(if_tunnel_check_nesting(ifp, m, MTAG_WGLOOP,
2304bf454ca8SMark Johnston 	    MAX_LOOPS))) {
2305bf454ca8SMark Johnston 		printf("%s:%d\n", __func__, __LINE__);
2306bf454ca8SMark Johnston 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2307bf454ca8SMark Johnston 		m_freem(m);
2308bf454ca8SMark Johnston 		return (ELOOP);
2309bf454ca8SMark Johnston 	}
2310bf454ca8SMark Johnston 
2311bf454ca8SMark Johnston 	M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT);
2312bf454ca8SMark Johnston 	if (__predict_false(m == NULL)) {
2313bf454ca8SMark Johnston 		if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
2314bf454ca8SMark Johnston 		return (ENOBUFS);
2315bf454ca8SMark Johnston 	}
2316bf454ca8SMark Johnston 
2317bf454ca8SMark Johnston 	eh = mtod(m, struct ether_header *);
2318bf454ca8SMark Johnston 	eh->ether_type = af == AF_INET ?
2319bf454ca8SMark Johnston 	    htons(ETHERTYPE_IP) : htons(ETHERTYPE_IPV6);
2320bf454ca8SMark Johnston 	memcpy(eh->ether_shost, "\x06\x06\x06\x06\x06\x06", ETHER_ADDR_LEN);
2321bf454ca8SMark Johnston 	memcpy(eh->ether_dhost, "\xff\xff\xff\xff\xff\xff", ETHER_ADDR_LEN);
2322bf454ca8SMark Johnston 	return (if_transmit(ifp, m));
2323bf454ca8SMark Johnston }
2324bf454ca8SMark Johnston #endif /* DEV_NETMAP */
2325bf454ca8SMark Johnston 
2326744bfb21SJohn Baldwin static int
wg_output(if_t ifp,struct mbuf * m,const struct sockaddr * dst,struct route * ro)232787e72834SJustin Hibbits wg_output(if_t ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro)
2328744bfb21SJohn Baldwin {
2329744bfb21SJohn Baldwin 	sa_family_t parsed_af;
2330744bfb21SJohn Baldwin 	uint32_t af, mtu;
2331744bfb21SJohn Baldwin 	int ret;
2332744bfb21SJohn Baldwin 	struct mbuf *defragged;
2333744bfb21SJohn Baldwin 
23342cb0fce2SSeth Hoffert 	/* BPF writes need to be handled specially. */
23352cb0fce2SSeth Hoffert 	if (dst->sa_family == AF_UNSPEC || dst->sa_family == pseudo_AF_HDRCMPLT)
2336744bfb21SJohn Baldwin 		memcpy(&af, dst->sa_data, sizeof(af));
2337744bfb21SJohn Baldwin 	else
2338*2bef0d54SKyle Evans 		af = RO_GET_FAMILY(ro, dst);
2339744bfb21SJohn Baldwin 	if (af == AF_UNSPEC) {
2340744bfb21SJohn Baldwin 		xmit_err(ifp, m, NULL, af);
2341744bfb21SJohn Baldwin 		return (EAFNOSUPPORT);
2342744bfb21SJohn Baldwin 	}
2343744bfb21SJohn Baldwin 
2344bf454ca8SMark Johnston #ifdef DEV_NETMAP
2345bf454ca8SMark Johnston 	if ((if_getcapenable(ifp) & IFCAP_NETMAP) != 0)
2346bf454ca8SMark Johnston 		return (wg_xmit_netmap(ifp, m, af));
2347bf454ca8SMark Johnston #endif
2348bf454ca8SMark Johnston 
2349744bfb21SJohn Baldwin 	defragged = m_defrag(m, M_NOWAIT);
2350744bfb21SJohn Baldwin 	if (defragged)
2351744bfb21SJohn Baldwin 		m = defragged;
2352744bfb21SJohn Baldwin 	m = m_unshare(m, M_NOWAIT);
2353744bfb21SJohn Baldwin 	if (!m) {
2354744bfb21SJohn Baldwin 		xmit_err(ifp, m, NULL, AF_UNSPEC);
2355744bfb21SJohn Baldwin 		return (ENOBUFS);
2356744bfb21SJohn Baldwin 	}
2357744bfb21SJohn Baldwin 
2358744bfb21SJohn Baldwin 	ret = determine_af_and_pullup(&m, &parsed_af);
2359744bfb21SJohn Baldwin 	if (ret) {
2360744bfb21SJohn Baldwin 		xmit_err(ifp, m, NULL, AF_UNSPEC);
2361744bfb21SJohn Baldwin 		return (ret);
2362744bfb21SJohn Baldwin 	}
2363*2bef0d54SKyle Evans 
2364*2bef0d54SKyle Evans 	MPASS(parsed_af == af);
236587e72834SJustin Hibbits 	mtu = (ro != NULL && ro->ro_mtu > 0) ? ro->ro_mtu : if_getmtu(ifp);
2366744bfb21SJohn Baldwin 	return (wg_xmit(ifp, m, parsed_af, mtu));
2367744bfb21SJohn Baldwin }
2368744bfb21SJohn Baldwin 
2369744bfb21SJohn Baldwin static int
wg_peer_add(struct wg_softc * sc,const nvlist_t * nvl)2370744bfb21SJohn Baldwin wg_peer_add(struct wg_softc *sc, const nvlist_t *nvl)
2371744bfb21SJohn Baldwin {
2372744bfb21SJohn Baldwin 	uint8_t			 public[WG_KEY_SIZE];
2373744bfb21SJohn Baldwin 	const void *pub_key, *preshared_key = NULL;
2374744bfb21SJohn Baldwin 	const struct sockaddr *endpoint;
2375744bfb21SJohn Baldwin 	int err;
2376744bfb21SJohn Baldwin 	size_t size;
2377744bfb21SJohn Baldwin 	struct noise_remote *remote;
2378744bfb21SJohn Baldwin 	struct wg_peer *peer = NULL;
2379744bfb21SJohn Baldwin 	bool need_insert = false;
2380744bfb21SJohn Baldwin 
2381744bfb21SJohn Baldwin 	sx_assert(&sc->sc_lock, SX_XLOCKED);
2382744bfb21SJohn Baldwin 
2383744bfb21SJohn Baldwin 	if (!nvlist_exists_binary(nvl, "public-key")) {
2384744bfb21SJohn Baldwin 		return (EINVAL);
2385744bfb21SJohn Baldwin 	}
2386744bfb21SJohn Baldwin 	pub_key = nvlist_get_binary(nvl, "public-key", &size);
2387744bfb21SJohn Baldwin 	if (size != WG_KEY_SIZE) {
2388744bfb21SJohn Baldwin 		return (EINVAL);
2389744bfb21SJohn Baldwin 	}
2390744bfb21SJohn Baldwin 	if (noise_local_keys(sc->sc_local, public, NULL) == 0 &&
2391744bfb21SJohn Baldwin 	    bcmp(public, pub_key, WG_KEY_SIZE) == 0) {
2392744bfb21SJohn Baldwin 		return (0); // Silently ignored; not actually a failure.
2393744bfb21SJohn Baldwin 	}
2394744bfb21SJohn Baldwin 	if ((remote = noise_remote_lookup(sc->sc_local, pub_key)) != NULL)
2395744bfb21SJohn Baldwin 		peer = noise_remote_arg(remote);
2396744bfb21SJohn Baldwin 	if (nvlist_exists_bool(nvl, "remove") &&
2397744bfb21SJohn Baldwin 		nvlist_get_bool(nvl, "remove")) {
2398744bfb21SJohn Baldwin 		if (remote != NULL) {
2399744bfb21SJohn Baldwin 			wg_peer_destroy(peer);
2400744bfb21SJohn Baldwin 			noise_remote_put(remote);
2401744bfb21SJohn Baldwin 		}
2402744bfb21SJohn Baldwin 		return (0);
2403744bfb21SJohn Baldwin 	}
2404744bfb21SJohn Baldwin 	if (nvlist_exists_bool(nvl, "replace-allowedips") &&
2405744bfb21SJohn Baldwin 		nvlist_get_bool(nvl, "replace-allowedips") &&
2406744bfb21SJohn Baldwin 	    peer != NULL) {
2407744bfb21SJohn Baldwin 
2408744bfb21SJohn Baldwin 		wg_aip_remove_all(sc, peer);
2409744bfb21SJohn Baldwin 	}
2410744bfb21SJohn Baldwin 	if (peer == NULL) {
2411744bfb21SJohn Baldwin 		peer = wg_peer_alloc(sc, pub_key);
2412744bfb21SJohn Baldwin 		need_insert = true;
2413744bfb21SJohn Baldwin 	}
2414744bfb21SJohn Baldwin 	if (nvlist_exists_binary(nvl, "endpoint")) {
2415744bfb21SJohn Baldwin 		endpoint = nvlist_get_binary(nvl, "endpoint", &size);
2416744bfb21SJohn Baldwin 		if (size > sizeof(peer->p_endpoint.e_remote)) {
2417744bfb21SJohn Baldwin 			err = EINVAL;
2418744bfb21SJohn Baldwin 			goto out;
2419744bfb21SJohn Baldwin 		}
2420744bfb21SJohn Baldwin 		memcpy(&peer->p_endpoint.e_remote, endpoint, size);
2421744bfb21SJohn Baldwin 	}
2422744bfb21SJohn Baldwin 	if (nvlist_exists_binary(nvl, "preshared-key")) {
2423744bfb21SJohn Baldwin 		preshared_key = nvlist_get_binary(nvl, "preshared-key", &size);
2424744bfb21SJohn Baldwin 		if (size != WG_KEY_SIZE) {
2425744bfb21SJohn Baldwin 			err = EINVAL;
2426744bfb21SJohn Baldwin 			goto out;
2427744bfb21SJohn Baldwin 		}
2428744bfb21SJohn Baldwin 		noise_remote_set_psk(peer->p_remote, preshared_key);
2429744bfb21SJohn Baldwin 	}
2430744bfb21SJohn Baldwin 	if (nvlist_exists_number(nvl, "persistent-keepalive-interval")) {
2431744bfb21SJohn Baldwin 		uint64_t pki = nvlist_get_number(nvl, "persistent-keepalive-interval");
2432744bfb21SJohn Baldwin 		if (pki > UINT16_MAX) {
2433744bfb21SJohn Baldwin 			err = EINVAL;
2434744bfb21SJohn Baldwin 			goto out;
2435744bfb21SJohn Baldwin 		}
2436744bfb21SJohn Baldwin 		wg_timers_set_persistent_keepalive(peer, pki);
2437744bfb21SJohn Baldwin 	}
2438744bfb21SJohn Baldwin 	if (nvlist_exists_nvlist_array(nvl, "allowed-ips")) {
2439744bfb21SJohn Baldwin 		const void *addr;
2440744bfb21SJohn Baldwin 		uint64_t cidr;
2441744bfb21SJohn Baldwin 		const nvlist_t * const * aipl;
2442744bfb21SJohn Baldwin 		size_t allowedip_count;
2443744bfb21SJohn Baldwin 
2444744bfb21SJohn Baldwin 		aipl = nvlist_get_nvlist_array(nvl, "allowed-ips", &allowedip_count);
2445744bfb21SJohn Baldwin 		for (size_t idx = 0; idx < allowedip_count; idx++) {
2446744bfb21SJohn Baldwin 			if (!nvlist_exists_number(aipl[idx], "cidr"))
2447744bfb21SJohn Baldwin 				continue;
2448744bfb21SJohn Baldwin 			cidr = nvlist_get_number(aipl[idx], "cidr");
2449744bfb21SJohn Baldwin 			if (nvlist_exists_binary(aipl[idx], "ipv4")) {
2450744bfb21SJohn Baldwin 				addr = nvlist_get_binary(aipl[idx], "ipv4", &size);
2451744bfb21SJohn Baldwin 				if (addr == NULL || cidr > 32 || size != sizeof(struct in_addr)) {
2452744bfb21SJohn Baldwin 					err = EINVAL;
2453744bfb21SJohn Baldwin 					goto out;
2454744bfb21SJohn Baldwin 				}
2455744bfb21SJohn Baldwin 				if ((err = wg_aip_add(sc, peer, AF_INET, addr, cidr)) != 0)
2456744bfb21SJohn Baldwin 					goto out;
2457744bfb21SJohn Baldwin 			} else if (nvlist_exists_binary(aipl[idx], "ipv6")) {
2458744bfb21SJohn Baldwin 				addr = nvlist_get_binary(aipl[idx], "ipv6", &size);
2459744bfb21SJohn Baldwin 				if (addr == NULL || cidr > 128 || size != sizeof(struct in6_addr)) {
2460744bfb21SJohn Baldwin 					err = EINVAL;
2461744bfb21SJohn Baldwin 					goto out;
2462744bfb21SJohn Baldwin 				}
2463744bfb21SJohn Baldwin 				if ((err = wg_aip_add(sc, peer, AF_INET6, addr, cidr)) != 0)
2464744bfb21SJohn Baldwin 					goto out;
2465744bfb21SJohn Baldwin 			} else {
2466744bfb21SJohn Baldwin 				continue;
2467744bfb21SJohn Baldwin 			}
2468744bfb21SJohn Baldwin 		}
2469744bfb21SJohn Baldwin 	}
2470744bfb21SJohn Baldwin 	if (need_insert) {
2471744bfb21SJohn Baldwin 		if ((err = noise_remote_enable(peer->p_remote)) != 0)
2472744bfb21SJohn Baldwin 			goto out;
2473744bfb21SJohn Baldwin 		TAILQ_INSERT_TAIL(&sc->sc_peers, peer, p_entry);
2474744bfb21SJohn Baldwin 		sc->sc_peers_num++;
247587e72834SJustin Hibbits 		if (if_getlinkstate(sc->sc_ifp) == LINK_STATE_UP)
2476744bfb21SJohn Baldwin 			wg_timers_enable(peer);
2477744bfb21SJohn Baldwin 	}
2478744bfb21SJohn Baldwin 	if (remote != NULL)
2479744bfb21SJohn Baldwin 		noise_remote_put(remote);
2480744bfb21SJohn Baldwin 	return (0);
2481744bfb21SJohn Baldwin out:
2482744bfb21SJohn Baldwin 	if (need_insert) /* If we fail, only destroy if it was new. */
2483744bfb21SJohn Baldwin 		wg_peer_destroy(peer);
2484744bfb21SJohn Baldwin 	if (remote != NULL)
2485744bfb21SJohn Baldwin 		noise_remote_put(remote);
2486744bfb21SJohn Baldwin 	return (err);
2487744bfb21SJohn Baldwin }
2488744bfb21SJohn Baldwin 
2489744bfb21SJohn Baldwin static int
wgc_set(struct wg_softc * sc,struct wg_data_io * wgd)2490744bfb21SJohn Baldwin wgc_set(struct wg_softc *sc, struct wg_data_io *wgd)
2491744bfb21SJohn Baldwin {
2492744bfb21SJohn Baldwin 	uint8_t public[WG_KEY_SIZE], private[WG_KEY_SIZE];
249387e72834SJustin Hibbits 	if_t ifp;
2494744bfb21SJohn Baldwin 	void *nvlpacked;
2495744bfb21SJohn Baldwin 	nvlist_t *nvl;
2496744bfb21SJohn Baldwin 	ssize_t size;
2497744bfb21SJohn Baldwin 	int err;
2498744bfb21SJohn Baldwin 
2499744bfb21SJohn Baldwin 	ifp = sc->sc_ifp;
2500744bfb21SJohn Baldwin 	if (wgd->wgd_size == 0 || wgd->wgd_data == NULL)
2501744bfb21SJohn Baldwin 		return (EFAULT);
2502744bfb21SJohn Baldwin 
2503744bfb21SJohn Baldwin 	/* Can nvlists be streamed in? It's not nice to impose arbitrary limits like that but
2504744bfb21SJohn Baldwin 	 * there needs to be _some_ limitation. */
2505744bfb21SJohn Baldwin 	if (wgd->wgd_size >= UINT32_MAX / 2)
2506744bfb21SJohn Baldwin 		return (E2BIG);
2507744bfb21SJohn Baldwin 
2508744bfb21SJohn Baldwin 	nvlpacked = malloc(wgd->wgd_size, M_TEMP, M_WAITOK | M_ZERO);
2509744bfb21SJohn Baldwin 
2510744bfb21SJohn Baldwin 	err = copyin(wgd->wgd_data, nvlpacked, wgd->wgd_size);
2511744bfb21SJohn Baldwin 	if (err)
2512744bfb21SJohn Baldwin 		goto out;
2513744bfb21SJohn Baldwin 	nvl = nvlist_unpack(nvlpacked, wgd->wgd_size, 0);
2514744bfb21SJohn Baldwin 	if (nvl == NULL) {
2515744bfb21SJohn Baldwin 		err = EBADMSG;
2516744bfb21SJohn Baldwin 		goto out;
2517744bfb21SJohn Baldwin 	}
2518744bfb21SJohn Baldwin 	sx_xlock(&sc->sc_lock);
2519744bfb21SJohn Baldwin 	if (nvlist_exists_bool(nvl, "replace-peers") &&
2520744bfb21SJohn Baldwin 		nvlist_get_bool(nvl, "replace-peers"))
2521744bfb21SJohn Baldwin 		wg_peer_destroy_all(sc);
2522744bfb21SJohn Baldwin 	if (nvlist_exists_number(nvl, "listen-port")) {
2523744bfb21SJohn Baldwin 		uint64_t new_port = nvlist_get_number(nvl, "listen-port");
2524744bfb21SJohn Baldwin 		if (new_port > UINT16_MAX) {
2525744bfb21SJohn Baldwin 			err = EINVAL;
2526744bfb21SJohn Baldwin 			goto out_locked;
2527744bfb21SJohn Baldwin 		}
2528744bfb21SJohn Baldwin 		if (new_port != sc->sc_socket.so_port) {
252987e72834SJustin Hibbits 			if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) {
2530744bfb21SJohn Baldwin 				if ((err = wg_socket_init(sc, new_port)) != 0)
2531744bfb21SJohn Baldwin 					goto out_locked;
2532744bfb21SJohn Baldwin 			} else
2533744bfb21SJohn Baldwin 				sc->sc_socket.so_port = new_port;
2534744bfb21SJohn Baldwin 		}
2535744bfb21SJohn Baldwin 	}
2536744bfb21SJohn Baldwin 	if (nvlist_exists_binary(nvl, "private-key")) {
2537744bfb21SJohn Baldwin 		const void *key = nvlist_get_binary(nvl, "private-key", &size);
2538744bfb21SJohn Baldwin 		if (size != WG_KEY_SIZE) {
2539744bfb21SJohn Baldwin 			err = EINVAL;
2540744bfb21SJohn Baldwin 			goto out_locked;
2541744bfb21SJohn Baldwin 		}
2542744bfb21SJohn Baldwin 
2543744bfb21SJohn Baldwin 		if (noise_local_keys(sc->sc_local, NULL, private) != 0 ||
2544744bfb21SJohn Baldwin 		    timingsafe_bcmp(private, key, WG_KEY_SIZE) != 0) {
2545744bfb21SJohn Baldwin 			struct wg_peer *peer;
2546744bfb21SJohn Baldwin 
2547744bfb21SJohn Baldwin 			if (curve25519_generate_public(public, key)) {
2548744bfb21SJohn Baldwin 				/* Peer conflict: remove conflicting peer. */
2549744bfb21SJohn Baldwin 				struct noise_remote *remote;
2550744bfb21SJohn Baldwin 				if ((remote = noise_remote_lookup(sc->sc_local,
2551744bfb21SJohn Baldwin 				    public)) != NULL) {
2552744bfb21SJohn Baldwin 					peer = noise_remote_arg(remote);
2553744bfb21SJohn Baldwin 					wg_peer_destroy(peer);
2554744bfb21SJohn Baldwin 					noise_remote_put(remote);
2555744bfb21SJohn Baldwin 				}
2556744bfb21SJohn Baldwin 			}
2557744bfb21SJohn Baldwin 
2558744bfb21SJohn Baldwin 			/*
2559744bfb21SJohn Baldwin 			 * Set the private key and invalidate all existing
2560744bfb21SJohn Baldwin 			 * handshakes.
2561744bfb21SJohn Baldwin 			 */
2562744bfb21SJohn Baldwin 			/* Note: we might be removing the private key. */
2563744bfb21SJohn Baldwin 			noise_local_private(sc->sc_local, key);
2564744bfb21SJohn Baldwin 			if (noise_local_keys(sc->sc_local, NULL, NULL) == 0)
2565744bfb21SJohn Baldwin 				cookie_checker_update(&sc->sc_cookie, public);
2566744bfb21SJohn Baldwin 			else
2567744bfb21SJohn Baldwin 				cookie_checker_update(&sc->sc_cookie, NULL);
2568744bfb21SJohn Baldwin 		}
2569744bfb21SJohn Baldwin 	}
2570744bfb21SJohn Baldwin 	if (nvlist_exists_number(nvl, "user-cookie")) {
2571744bfb21SJohn Baldwin 		uint64_t user_cookie = nvlist_get_number(nvl, "user-cookie");
2572744bfb21SJohn Baldwin 		if (user_cookie > UINT32_MAX) {
2573744bfb21SJohn Baldwin 			err = EINVAL;
2574744bfb21SJohn Baldwin 			goto out_locked;
2575744bfb21SJohn Baldwin 		}
2576744bfb21SJohn Baldwin 		err = wg_socket_set_cookie(sc, user_cookie);
2577744bfb21SJohn Baldwin 		if (err)
2578744bfb21SJohn Baldwin 			goto out_locked;
2579744bfb21SJohn Baldwin 	}
2580744bfb21SJohn Baldwin 	if (nvlist_exists_nvlist_array(nvl, "peers")) {
2581744bfb21SJohn Baldwin 		size_t peercount;
2582744bfb21SJohn Baldwin 		const nvlist_t * const*nvl_peers;
2583744bfb21SJohn Baldwin 
2584744bfb21SJohn Baldwin 		nvl_peers = nvlist_get_nvlist_array(nvl, "peers", &peercount);
2585744bfb21SJohn Baldwin 		for (int i = 0; i < peercount; i++) {
2586744bfb21SJohn Baldwin 			err = wg_peer_add(sc, nvl_peers[i]);
2587744bfb21SJohn Baldwin 			if (err != 0)
2588744bfb21SJohn Baldwin 				goto out_locked;
2589744bfb21SJohn Baldwin 		}
2590744bfb21SJohn Baldwin 	}
2591744bfb21SJohn Baldwin 
2592744bfb21SJohn Baldwin out_locked:
2593744bfb21SJohn Baldwin 	sx_xunlock(&sc->sc_lock);
2594744bfb21SJohn Baldwin 	nvlist_destroy(nvl);
2595744bfb21SJohn Baldwin out:
2596dcf581bbSJohn Baldwin 	zfree(nvlpacked, M_TEMP);
2597744bfb21SJohn Baldwin 	return (err);
2598744bfb21SJohn Baldwin }
2599744bfb21SJohn Baldwin 
2600744bfb21SJohn Baldwin static int
wgc_get(struct wg_softc * sc,struct wg_data_io * wgd)2601744bfb21SJohn Baldwin wgc_get(struct wg_softc *sc, struct wg_data_io *wgd)
2602744bfb21SJohn Baldwin {
2603744bfb21SJohn Baldwin 	uint8_t public_key[WG_KEY_SIZE] = { 0 };
2604744bfb21SJohn Baldwin 	uint8_t private_key[WG_KEY_SIZE] = { 0 };
2605744bfb21SJohn Baldwin 	uint8_t preshared_key[NOISE_SYMMETRIC_KEY_LEN] = { 0 };
2606744bfb21SJohn Baldwin 	nvlist_t *nvl, *nvl_peer, *nvl_aip, **nvl_peers, **nvl_aips;
2607744bfb21SJohn Baldwin 	size_t size, peer_count, aip_count, i, j;
2608744bfb21SJohn Baldwin 	struct wg_timespec64 ts64;
2609744bfb21SJohn Baldwin 	struct wg_peer *peer;
2610744bfb21SJohn Baldwin 	struct wg_aip *aip;
2611744bfb21SJohn Baldwin 	void *packed;
2612744bfb21SJohn Baldwin 	int err = 0;
2613744bfb21SJohn Baldwin 
2614744bfb21SJohn Baldwin 	nvl = nvlist_create(0);
2615744bfb21SJohn Baldwin 	if (!nvl)
2616744bfb21SJohn Baldwin 		return (ENOMEM);
2617744bfb21SJohn Baldwin 
2618744bfb21SJohn Baldwin 	sx_slock(&sc->sc_lock);
2619744bfb21SJohn Baldwin 
2620744bfb21SJohn Baldwin 	if (sc->sc_socket.so_port != 0)
2621744bfb21SJohn Baldwin 		nvlist_add_number(nvl, "listen-port", sc->sc_socket.so_port);
2622744bfb21SJohn Baldwin 	if (sc->sc_socket.so_user_cookie != 0)
2623744bfb21SJohn Baldwin 		nvlist_add_number(nvl, "user-cookie", sc->sc_socket.so_user_cookie);
2624744bfb21SJohn Baldwin 	if (noise_local_keys(sc->sc_local, public_key, private_key) == 0) {
2625744bfb21SJohn Baldwin 		nvlist_add_binary(nvl, "public-key", public_key, WG_KEY_SIZE);
2626744bfb21SJohn Baldwin 		if (wgc_privileged(sc))
2627744bfb21SJohn Baldwin 			nvlist_add_binary(nvl, "private-key", private_key, WG_KEY_SIZE);
2628744bfb21SJohn Baldwin 		explicit_bzero(private_key, sizeof(private_key));
2629744bfb21SJohn Baldwin 	}
2630744bfb21SJohn Baldwin 	peer_count = sc->sc_peers_num;
2631744bfb21SJohn Baldwin 	if (peer_count) {
2632744bfb21SJohn Baldwin 		nvl_peers = mallocarray(peer_count, sizeof(void *), M_NVLIST, M_WAITOK | M_ZERO);
2633744bfb21SJohn Baldwin 		i = 0;
2634744bfb21SJohn Baldwin 		TAILQ_FOREACH(peer, &sc->sc_peers, p_entry) {
2635744bfb21SJohn Baldwin 			if (i >= peer_count)
2636744bfb21SJohn Baldwin 				panic("peers changed from under us");
2637744bfb21SJohn Baldwin 
2638744bfb21SJohn Baldwin 			nvl_peers[i++] = nvl_peer = nvlist_create(0);
2639744bfb21SJohn Baldwin 			if (!nvl_peer) {
2640744bfb21SJohn Baldwin 				err = ENOMEM;
2641744bfb21SJohn Baldwin 				goto err_peer;
2642744bfb21SJohn Baldwin 			}
2643744bfb21SJohn Baldwin 
2644744bfb21SJohn Baldwin 			(void)noise_remote_keys(peer->p_remote, public_key, preshared_key);
2645744bfb21SJohn Baldwin 			nvlist_add_binary(nvl_peer, "public-key", public_key, sizeof(public_key));
2646744bfb21SJohn Baldwin 			if (wgc_privileged(sc))
2647744bfb21SJohn Baldwin 				nvlist_add_binary(nvl_peer, "preshared-key", preshared_key, sizeof(preshared_key));
2648744bfb21SJohn Baldwin 			explicit_bzero(preshared_key, sizeof(preshared_key));
2649744bfb21SJohn Baldwin 			if (peer->p_endpoint.e_remote.r_sa.sa_family == AF_INET)
2650744bfb21SJohn Baldwin 				nvlist_add_binary(nvl_peer, "endpoint", &peer->p_endpoint.e_remote, sizeof(struct sockaddr_in));
2651744bfb21SJohn Baldwin 			else if (peer->p_endpoint.e_remote.r_sa.sa_family == AF_INET6)
2652744bfb21SJohn Baldwin 				nvlist_add_binary(nvl_peer, "endpoint", &peer->p_endpoint.e_remote, sizeof(struct sockaddr_in6));
2653744bfb21SJohn Baldwin 			wg_timers_get_last_handshake(peer, &ts64);
2654744bfb21SJohn Baldwin 			nvlist_add_binary(nvl_peer, "last-handshake-time", &ts64, sizeof(ts64));
2655744bfb21SJohn Baldwin 			nvlist_add_number(nvl_peer, "persistent-keepalive-interval", peer->p_persistent_keepalive_interval);
2656744bfb21SJohn Baldwin 			nvlist_add_number(nvl_peer, "rx-bytes", counter_u64_fetch(peer->p_rx_bytes));
2657744bfb21SJohn Baldwin 			nvlist_add_number(nvl_peer, "tx-bytes", counter_u64_fetch(peer->p_tx_bytes));
2658744bfb21SJohn Baldwin 
2659744bfb21SJohn Baldwin 			aip_count = peer->p_aips_num;
2660744bfb21SJohn Baldwin 			if (aip_count) {
2661744bfb21SJohn Baldwin 				nvl_aips = mallocarray(aip_count, sizeof(void *), M_NVLIST, M_WAITOK | M_ZERO);
2662744bfb21SJohn Baldwin 				j = 0;
2663744bfb21SJohn Baldwin 				LIST_FOREACH(aip, &peer->p_aips, a_entry) {
2664744bfb21SJohn Baldwin 					if (j >= aip_count)
2665744bfb21SJohn Baldwin 						panic("aips changed from under us");
2666744bfb21SJohn Baldwin 
2667744bfb21SJohn Baldwin 					nvl_aips[j++] = nvl_aip = nvlist_create(0);
2668744bfb21SJohn Baldwin 					if (!nvl_aip) {
2669744bfb21SJohn Baldwin 						err = ENOMEM;
2670744bfb21SJohn Baldwin 						goto err_aip;
2671744bfb21SJohn Baldwin 					}
2672744bfb21SJohn Baldwin 					if (aip->a_af == AF_INET) {
2673744bfb21SJohn Baldwin 						nvlist_add_binary(nvl_aip, "ipv4", &aip->a_addr.in, sizeof(aip->a_addr.in));
2674744bfb21SJohn Baldwin 						nvlist_add_number(nvl_aip, "cidr", bitcount32(aip->a_mask.ip));
2675744bfb21SJohn Baldwin 					}
2676744bfb21SJohn Baldwin #ifdef INET6
2677744bfb21SJohn Baldwin 					else if (aip->a_af == AF_INET6) {
2678744bfb21SJohn Baldwin 						nvlist_add_binary(nvl_aip, "ipv6", &aip->a_addr.in6, sizeof(aip->a_addr.in6));
2679744bfb21SJohn Baldwin 						nvlist_add_number(nvl_aip, "cidr", in6_mask2len(&aip->a_mask.in6, NULL));
2680744bfb21SJohn Baldwin 					}
2681744bfb21SJohn Baldwin #endif
2682744bfb21SJohn Baldwin 				}
2683744bfb21SJohn Baldwin 				nvlist_add_nvlist_array(nvl_peer, "allowed-ips", (const nvlist_t *const *)nvl_aips, aip_count);
2684744bfb21SJohn Baldwin 			err_aip:
2685744bfb21SJohn Baldwin 				for (j = 0; j < aip_count; ++j)
2686744bfb21SJohn Baldwin 					nvlist_destroy(nvl_aips[j]);
2687744bfb21SJohn Baldwin 				free(nvl_aips, M_NVLIST);
2688744bfb21SJohn Baldwin 				if (err)
2689744bfb21SJohn Baldwin 					goto err_peer;
2690744bfb21SJohn Baldwin 			}
2691744bfb21SJohn Baldwin 		}
2692744bfb21SJohn Baldwin 		nvlist_add_nvlist_array(nvl, "peers", (const nvlist_t * const *)nvl_peers, peer_count);
2693744bfb21SJohn Baldwin 	err_peer:
2694744bfb21SJohn Baldwin 		for (i = 0; i < peer_count; ++i)
2695744bfb21SJohn Baldwin 			nvlist_destroy(nvl_peers[i]);
2696744bfb21SJohn Baldwin 		free(nvl_peers, M_NVLIST);
2697744bfb21SJohn Baldwin 		if (err) {
2698744bfb21SJohn Baldwin 			sx_sunlock(&sc->sc_lock);
2699744bfb21SJohn Baldwin 			goto err;
2700744bfb21SJohn Baldwin 		}
2701744bfb21SJohn Baldwin 	}
2702744bfb21SJohn Baldwin 	sx_sunlock(&sc->sc_lock);
2703744bfb21SJohn Baldwin 	packed = nvlist_pack(nvl, &size);
2704744bfb21SJohn Baldwin 	if (!packed) {
2705744bfb21SJohn Baldwin 		err = ENOMEM;
2706744bfb21SJohn Baldwin 		goto err;
2707744bfb21SJohn Baldwin 	}
2708744bfb21SJohn Baldwin 	if (!wgd->wgd_size) {
2709744bfb21SJohn Baldwin 		wgd->wgd_size = size;
2710744bfb21SJohn Baldwin 		goto out;
2711744bfb21SJohn Baldwin 	}
2712744bfb21SJohn Baldwin 	if (wgd->wgd_size < size) {
2713744bfb21SJohn Baldwin 		err = ENOSPC;
2714744bfb21SJohn Baldwin 		goto out;
2715744bfb21SJohn Baldwin 	}
2716744bfb21SJohn Baldwin 	err = copyout(packed, wgd->wgd_data, size);
2717744bfb21SJohn Baldwin 	wgd->wgd_size = size;
2718744bfb21SJohn Baldwin 
2719744bfb21SJohn Baldwin out:
2720dcf581bbSJohn Baldwin 	zfree(packed, M_NVLIST);
2721744bfb21SJohn Baldwin err:
2722744bfb21SJohn Baldwin 	nvlist_destroy(nvl);
2723744bfb21SJohn Baldwin 	return (err);
2724744bfb21SJohn Baldwin }
2725744bfb21SJohn Baldwin 
2726744bfb21SJohn Baldwin static int
wg_ioctl(if_t ifp,u_long cmd,caddr_t data)272787e72834SJustin Hibbits wg_ioctl(if_t ifp, u_long cmd, caddr_t data)
2728744bfb21SJohn Baldwin {
2729744bfb21SJohn Baldwin 	struct wg_data_io *wgd = (struct wg_data_io *)data;
2730744bfb21SJohn Baldwin 	struct ifreq *ifr = (struct ifreq *)data;
2731744bfb21SJohn Baldwin 	struct wg_softc *sc;
2732744bfb21SJohn Baldwin 	int ret = 0;
2733744bfb21SJohn Baldwin 
2734744bfb21SJohn Baldwin 	sx_slock(&wg_sx);
273587e72834SJustin Hibbits 	sc = if_getsoftc(ifp);
2736744bfb21SJohn Baldwin 	if (!sc) {
2737744bfb21SJohn Baldwin 		ret = ENXIO;
2738744bfb21SJohn Baldwin 		goto out;
2739744bfb21SJohn Baldwin 	}
2740744bfb21SJohn Baldwin 
2741744bfb21SJohn Baldwin 	switch (cmd) {
2742744bfb21SJohn Baldwin 	case SIOCSWG:
2743744bfb21SJohn Baldwin 		ret = priv_check(curthread, PRIV_NET_WG);
2744744bfb21SJohn Baldwin 		if (ret == 0)
2745744bfb21SJohn Baldwin 			ret = wgc_set(sc, wgd);
2746744bfb21SJohn Baldwin 		break;
2747744bfb21SJohn Baldwin 	case SIOCGWG:
2748744bfb21SJohn Baldwin 		ret = wgc_get(sc, wgd);
2749744bfb21SJohn Baldwin 		break;
2750744bfb21SJohn Baldwin 	/* Interface IOCTLs */
2751744bfb21SJohn Baldwin 	case SIOCSIFADDR:
2752744bfb21SJohn Baldwin 		/*
2753744bfb21SJohn Baldwin 		 * This differs from *BSD norms, but is more uniform with how
2754744bfb21SJohn Baldwin 		 * WireGuard behaves elsewhere.
2755744bfb21SJohn Baldwin 		 */
2756744bfb21SJohn Baldwin 		break;
2757744bfb21SJohn Baldwin 	case SIOCSIFFLAGS:
275887e72834SJustin Hibbits 		if (if_getflags(ifp) & IFF_UP)
2759744bfb21SJohn Baldwin 			ret = wg_up(sc);
2760744bfb21SJohn Baldwin 		else
2761744bfb21SJohn Baldwin 			wg_down(sc);
2762744bfb21SJohn Baldwin 		break;
2763744bfb21SJohn Baldwin 	case SIOCSIFMTU:
2764744bfb21SJohn Baldwin 		if (ifr->ifr_mtu <= 0 || ifr->ifr_mtu > MAX_MTU)
2765744bfb21SJohn Baldwin 			ret = EINVAL;
2766744bfb21SJohn Baldwin 		else
276787e72834SJustin Hibbits 			if_setmtu(ifp, ifr->ifr_mtu);
2768744bfb21SJohn Baldwin 		break;
2769744bfb21SJohn Baldwin 	case SIOCADDMULTI:
2770744bfb21SJohn Baldwin 	case SIOCDELMULTI:
2771744bfb21SJohn Baldwin 		break;
2772744bfb21SJohn Baldwin 	case SIOCGTUNFIB:
2773744bfb21SJohn Baldwin 		ifr->ifr_fib = sc->sc_socket.so_fibnum;
2774744bfb21SJohn Baldwin 		break;
2775744bfb21SJohn Baldwin 	case SIOCSTUNFIB:
2776744bfb21SJohn Baldwin 		ret = priv_check(curthread, PRIV_NET_WG);
2777744bfb21SJohn Baldwin 		if (ret)
2778744bfb21SJohn Baldwin 			break;
2779744bfb21SJohn Baldwin 		ret = priv_check(curthread, PRIV_NET_SETIFFIB);
2780744bfb21SJohn Baldwin 		if (ret)
2781744bfb21SJohn Baldwin 			break;
2782744bfb21SJohn Baldwin 		sx_xlock(&sc->sc_lock);
2783744bfb21SJohn Baldwin 		ret = wg_socket_set_fibnum(sc, ifr->ifr_fib);
2784744bfb21SJohn Baldwin 		sx_xunlock(&sc->sc_lock);
2785744bfb21SJohn Baldwin 		break;
2786744bfb21SJohn Baldwin 	default:
2787744bfb21SJohn Baldwin 		ret = ENOTTY;
2788744bfb21SJohn Baldwin 	}
2789744bfb21SJohn Baldwin 
2790744bfb21SJohn Baldwin out:
2791744bfb21SJohn Baldwin 	sx_sunlock(&wg_sx);
2792744bfb21SJohn Baldwin 	return (ret);
2793744bfb21SJohn Baldwin }
2794744bfb21SJohn Baldwin 
2795744bfb21SJohn Baldwin static int
wg_up(struct wg_softc * sc)2796744bfb21SJohn Baldwin wg_up(struct wg_softc *sc)
2797744bfb21SJohn Baldwin {
279887e72834SJustin Hibbits 	if_t ifp = sc->sc_ifp;
2799744bfb21SJohn Baldwin 	struct wg_peer *peer;
2800744bfb21SJohn Baldwin 	int rc = EBUSY;
2801744bfb21SJohn Baldwin 
2802744bfb21SJohn Baldwin 	sx_xlock(&sc->sc_lock);
2803744bfb21SJohn Baldwin 	/* Jail's being removed, no more wg_up(). */
2804744bfb21SJohn Baldwin 	if ((sc->sc_flags & WGF_DYING) != 0)
2805744bfb21SJohn Baldwin 		goto out;
2806744bfb21SJohn Baldwin 
2807744bfb21SJohn Baldwin 	/* Silent success if we're already running. */
2808744bfb21SJohn Baldwin 	rc = 0;
280987e72834SJustin Hibbits 	if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
2810744bfb21SJohn Baldwin 		goto out;
281187e72834SJustin Hibbits 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
2812744bfb21SJohn Baldwin 
2813744bfb21SJohn Baldwin 	rc = wg_socket_init(sc, sc->sc_socket.so_port);
2814744bfb21SJohn Baldwin 	if (rc == 0) {
2815744bfb21SJohn Baldwin 		TAILQ_FOREACH(peer, &sc->sc_peers, p_entry)
2816744bfb21SJohn Baldwin 			wg_timers_enable(peer);
2817744bfb21SJohn Baldwin 		if_link_state_change(sc->sc_ifp, LINK_STATE_UP);
2818744bfb21SJohn Baldwin 	} else {
281987e72834SJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
2820744bfb21SJohn Baldwin 		DPRINTF(sc, "Unable to initialize sockets: %d\n", rc);
2821744bfb21SJohn Baldwin 	}
2822744bfb21SJohn Baldwin out:
2823744bfb21SJohn Baldwin 	sx_xunlock(&sc->sc_lock);
2824744bfb21SJohn Baldwin 	return (rc);
2825744bfb21SJohn Baldwin }
2826744bfb21SJohn Baldwin 
2827744bfb21SJohn Baldwin static void
wg_down(struct wg_softc * sc)2828744bfb21SJohn Baldwin wg_down(struct wg_softc *sc)
2829744bfb21SJohn Baldwin {
283087e72834SJustin Hibbits 	if_t ifp = sc->sc_ifp;
2831744bfb21SJohn Baldwin 	struct wg_peer *peer;
2832744bfb21SJohn Baldwin 
2833744bfb21SJohn Baldwin 	sx_xlock(&sc->sc_lock);
283487e72834SJustin Hibbits 	if (!(if_getdrvflags(ifp) & IFF_DRV_RUNNING)) {
2835744bfb21SJohn Baldwin 		sx_xunlock(&sc->sc_lock);
2836744bfb21SJohn Baldwin 		return;
2837744bfb21SJohn Baldwin 	}
283887e72834SJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
2839744bfb21SJohn Baldwin 
2840744bfb21SJohn Baldwin 	TAILQ_FOREACH(peer, &sc->sc_peers, p_entry) {
2841744bfb21SJohn Baldwin 		wg_queue_purge(&peer->p_stage_queue);
2842744bfb21SJohn Baldwin 		wg_timers_disable(peer);
2843744bfb21SJohn Baldwin 	}
2844744bfb21SJohn Baldwin 
2845744bfb21SJohn Baldwin 	wg_queue_purge(&sc->sc_handshake_queue);
2846744bfb21SJohn Baldwin 
2847744bfb21SJohn Baldwin 	TAILQ_FOREACH(peer, &sc->sc_peers, p_entry) {
2848744bfb21SJohn Baldwin 		noise_remote_handshake_clear(peer->p_remote);
2849744bfb21SJohn Baldwin 		noise_remote_keypairs_clear(peer->p_remote);
2850744bfb21SJohn Baldwin 	}
2851744bfb21SJohn Baldwin 
2852744bfb21SJohn Baldwin 	if_link_state_change(sc->sc_ifp, LINK_STATE_DOWN);
2853744bfb21SJohn Baldwin 	wg_socket_uninit(sc);
2854744bfb21SJohn Baldwin 
2855744bfb21SJohn Baldwin 	sx_xunlock(&sc->sc_lock);
2856744bfb21SJohn Baldwin }
2857744bfb21SJohn Baldwin 
2858744bfb21SJohn Baldwin static int
wg_clone_create(struct if_clone * ifc,char * name,size_t len,struct ifc_data * ifd,struct ifnet ** ifpp)2859eb3f9a7aSAlan Somers wg_clone_create(struct if_clone *ifc, char *name, size_t len,
2860eb3f9a7aSAlan Somers     struct ifc_data *ifd, struct ifnet **ifpp)
2861744bfb21SJohn Baldwin {
2862744bfb21SJohn Baldwin 	struct wg_softc *sc;
286387e72834SJustin Hibbits 	if_t ifp;
2864744bfb21SJohn Baldwin 
2865744bfb21SJohn Baldwin 	sc = malloc(sizeof(*sc), M_WG, M_WAITOK | M_ZERO);
2866744bfb21SJohn Baldwin 
2867744bfb21SJohn Baldwin 	sc->sc_local = noise_local_alloc(sc);
2868744bfb21SJohn Baldwin 
2869744bfb21SJohn Baldwin 	sc->sc_encrypt = mallocarray(sizeof(struct grouptask), mp_ncpus, M_WG, M_WAITOK | M_ZERO);
2870744bfb21SJohn Baldwin 
2871744bfb21SJohn Baldwin 	sc->sc_decrypt = mallocarray(sizeof(struct grouptask), mp_ncpus, M_WG, M_WAITOK | M_ZERO);
2872744bfb21SJohn Baldwin 
2873744bfb21SJohn Baldwin 	if (!rn_inithead((void **)&sc->sc_aip4, offsetof(struct aip_addr, in) * NBBY))
2874744bfb21SJohn Baldwin 		goto free_decrypt;
2875744bfb21SJohn Baldwin 
2876744bfb21SJohn Baldwin 	if (!rn_inithead((void **)&sc->sc_aip6, offsetof(struct aip_addr, in6) * NBBY))
2877744bfb21SJohn Baldwin 		goto free_aip4;
2878744bfb21SJohn Baldwin 
2879744bfb21SJohn Baldwin 	atomic_add_int(&clone_count, 1);
2880744bfb21SJohn Baldwin 	ifp = sc->sc_ifp = if_alloc(IFT_WIREGUARD);
2881744bfb21SJohn Baldwin 
2882744bfb21SJohn Baldwin 	sc->sc_ucred = crhold(curthread->td_ucred);
2883744bfb21SJohn Baldwin 	sc->sc_socket.so_fibnum = curthread->td_proc->p_fibnum;
2884744bfb21SJohn Baldwin 	sc->sc_socket.so_port = 0;
2885744bfb21SJohn Baldwin 
2886744bfb21SJohn Baldwin 	TAILQ_INIT(&sc->sc_peers);
2887744bfb21SJohn Baldwin 	sc->sc_peers_num = 0;
2888744bfb21SJohn Baldwin 
2889744bfb21SJohn Baldwin 	cookie_checker_init(&sc->sc_cookie);
2890744bfb21SJohn Baldwin 
2891744bfb21SJohn Baldwin 	RADIX_NODE_HEAD_LOCK_INIT(sc->sc_aip4);
2892744bfb21SJohn Baldwin 	RADIX_NODE_HEAD_LOCK_INIT(sc->sc_aip6);
2893744bfb21SJohn Baldwin 
2894744bfb21SJohn Baldwin 	GROUPTASK_INIT(&sc->sc_handshake, 0, (gtask_fn_t *)wg_softc_handshake_receive, sc);
2895744bfb21SJohn Baldwin 	taskqgroup_attach(qgroup_wg_tqg, &sc->sc_handshake, sc, NULL, NULL, "wg tx initiation");
2896744bfb21SJohn Baldwin 	wg_queue_init(&sc->sc_handshake_queue, "hsq");
2897744bfb21SJohn Baldwin 
2898744bfb21SJohn Baldwin 	for (int i = 0; i < mp_ncpus; i++) {
2899744bfb21SJohn Baldwin 		GROUPTASK_INIT(&sc->sc_encrypt[i], 0,
2900744bfb21SJohn Baldwin 		     (gtask_fn_t *)wg_softc_encrypt, sc);
2901744bfb21SJohn Baldwin 		taskqgroup_attach_cpu(qgroup_wg_tqg, &sc->sc_encrypt[i], sc, i, NULL, NULL, "wg encrypt");
2902744bfb21SJohn Baldwin 		GROUPTASK_INIT(&sc->sc_decrypt[i], 0,
2903744bfb21SJohn Baldwin 		    (gtask_fn_t *)wg_softc_decrypt, sc);
2904744bfb21SJohn Baldwin 		taskqgroup_attach_cpu(qgroup_wg_tqg, &sc->sc_decrypt[i], sc, i, NULL, NULL, "wg decrypt");
2905744bfb21SJohn Baldwin 	}
2906744bfb21SJohn Baldwin 
2907744bfb21SJohn Baldwin 	wg_queue_init(&sc->sc_encrypt_parallel, "encp");
2908744bfb21SJohn Baldwin 	wg_queue_init(&sc->sc_decrypt_parallel, "decp");
2909744bfb21SJohn Baldwin 
2910744bfb21SJohn Baldwin 	sx_init(&sc->sc_lock, "wg softc lock");
2911744bfb21SJohn Baldwin 
291287e72834SJustin Hibbits 	if_setsoftc(ifp, sc);
291387e72834SJustin Hibbits 	if_setcapabilities(ifp, WG_CAPS);
291487e72834SJustin Hibbits 	if_setcapenable(ifp, WG_CAPS);
2915eb3f9a7aSAlan Somers 	if_initname(ifp, wgname, ifd->unit);
2916744bfb21SJohn Baldwin 
2917744bfb21SJohn Baldwin 	if_setmtu(ifp, DEFAULT_MTU);
291887e72834SJustin Hibbits 	if_setflags(ifp, IFF_NOARP | IFF_MULTICAST);
291987e72834SJustin Hibbits 	if_setinitfn(ifp, wg_init);
292087e72834SJustin Hibbits 	if_setreassignfn(ifp, wg_reassign);
292187e72834SJustin Hibbits 	if_setqflushfn(ifp, wg_qflush);
292287e72834SJustin Hibbits 	if_settransmitfn(ifp, wg_transmit);
29235515e887SMark Johnston #ifdef DEV_NETMAP
2924bf454ca8SMark Johnston 	if_setinputfn(ifp, wg_if_input);
2925bf454ca8SMark Johnston #endif
292687e72834SJustin Hibbits 	if_setoutputfn(ifp, wg_output);
292787e72834SJustin Hibbits 	if_setioctlfn(ifp, wg_ioctl);
2928744bfb21SJohn Baldwin 	if_attach(ifp);
2929744bfb21SJohn Baldwin 	bpfattach(ifp, DLT_NULL, sizeof(uint32_t));
2930744bfb21SJohn Baldwin #ifdef INET6
2931744bfb21SJohn Baldwin 	ND_IFINFO(ifp)->flags &= ~ND6_IFF_AUTO_LINKLOCAL;
2932744bfb21SJohn Baldwin 	ND_IFINFO(ifp)->flags |= ND6_IFF_NO_DAD;
2933744bfb21SJohn Baldwin #endif
2934744bfb21SJohn Baldwin 	sx_xlock(&wg_sx);
2935744bfb21SJohn Baldwin 	LIST_INSERT_HEAD(&wg_list, sc, sc_entry);
2936744bfb21SJohn Baldwin 	sx_xunlock(&wg_sx);
2937eb3f9a7aSAlan Somers 	*ifpp = ifp;
2938744bfb21SJohn Baldwin 	return (0);
2939744bfb21SJohn Baldwin free_aip4:
2940744bfb21SJohn Baldwin 	RADIX_NODE_HEAD_DESTROY(sc->sc_aip4);
2941744bfb21SJohn Baldwin 	free(sc->sc_aip4, M_RTABLE);
2942744bfb21SJohn Baldwin free_decrypt:
2943744bfb21SJohn Baldwin 	free(sc->sc_decrypt, M_WG);
2944744bfb21SJohn Baldwin 	free(sc->sc_encrypt, M_WG);
2945744bfb21SJohn Baldwin 	noise_local_free(sc->sc_local, NULL);
2946744bfb21SJohn Baldwin 	free(sc, M_WG);
2947744bfb21SJohn Baldwin 	return (ENOMEM);
2948744bfb21SJohn Baldwin }
2949744bfb21SJohn Baldwin 
2950744bfb21SJohn Baldwin static void
wg_clone_deferred_free(struct noise_local * l)2951744bfb21SJohn Baldwin wg_clone_deferred_free(struct noise_local *l)
2952744bfb21SJohn Baldwin {
2953744bfb21SJohn Baldwin 	struct wg_softc *sc = noise_local_arg(l);
2954744bfb21SJohn Baldwin 
2955744bfb21SJohn Baldwin 	free(sc, M_WG);
2956744bfb21SJohn Baldwin 	atomic_add_int(&clone_count, -1);
2957744bfb21SJohn Baldwin }
2958744bfb21SJohn Baldwin 
2959eb3f9a7aSAlan Somers static int
wg_clone_destroy(struct if_clone * ifc,if_t ifp,uint32_t flags)296087e72834SJustin Hibbits wg_clone_destroy(struct if_clone *ifc, if_t ifp, uint32_t flags)
2961744bfb21SJohn Baldwin {
296287e72834SJustin Hibbits 	struct wg_softc *sc = if_getsoftc(ifp);
2963744bfb21SJohn Baldwin 	struct ucred *cred;
2964744bfb21SJohn Baldwin 
2965744bfb21SJohn Baldwin 	sx_xlock(&wg_sx);
296687e72834SJustin Hibbits 	if_setsoftc(ifp, NULL);
2967744bfb21SJohn Baldwin 	sx_xlock(&sc->sc_lock);
2968744bfb21SJohn Baldwin 	sc->sc_flags |= WGF_DYING;
2969744bfb21SJohn Baldwin 	cred = sc->sc_ucred;
2970744bfb21SJohn Baldwin 	sc->sc_ucred = NULL;
2971744bfb21SJohn Baldwin 	sx_xunlock(&sc->sc_lock);
2972744bfb21SJohn Baldwin 	LIST_REMOVE(sc, sc_entry);
2973744bfb21SJohn Baldwin 	sx_xunlock(&wg_sx);
2974744bfb21SJohn Baldwin 
2975744bfb21SJohn Baldwin 	if_link_state_change(sc->sc_ifp, LINK_STATE_DOWN);
297687e72834SJustin Hibbits 	CURVNET_SET(if_getvnet(sc->sc_ifp));
2977744bfb21SJohn Baldwin 	if_purgeaddrs(sc->sc_ifp);
2978744bfb21SJohn Baldwin 	CURVNET_RESTORE();
2979744bfb21SJohn Baldwin 
2980744bfb21SJohn Baldwin 	sx_xlock(&sc->sc_lock);
2981744bfb21SJohn Baldwin 	wg_socket_uninit(sc);
2982744bfb21SJohn Baldwin 	sx_xunlock(&sc->sc_lock);
2983744bfb21SJohn Baldwin 
2984744bfb21SJohn Baldwin 	/*
2985744bfb21SJohn Baldwin 	 * No guarantees that all traffic have passed until the epoch has
2986744bfb21SJohn Baldwin 	 * elapsed with the socket closed.
2987744bfb21SJohn Baldwin 	 */
2988744bfb21SJohn Baldwin 	NET_EPOCH_WAIT();
2989744bfb21SJohn Baldwin 
2990744bfb21SJohn Baldwin 	taskqgroup_drain_all(qgroup_wg_tqg);
2991744bfb21SJohn Baldwin 	sx_xlock(&sc->sc_lock);
2992744bfb21SJohn Baldwin 	wg_peer_destroy_all(sc);
2993f948cb71SZhenlei Huang 	NET_EPOCH_DRAIN_CALLBACKS();
2994744bfb21SJohn Baldwin 	sx_xunlock(&sc->sc_lock);
2995744bfb21SJohn Baldwin 	sx_destroy(&sc->sc_lock);
2996744bfb21SJohn Baldwin 	taskqgroup_detach(qgroup_wg_tqg, &sc->sc_handshake);
2997744bfb21SJohn Baldwin 	for (int i = 0; i < mp_ncpus; i++) {
2998744bfb21SJohn Baldwin 		taskqgroup_detach(qgroup_wg_tqg, &sc->sc_encrypt[i]);
2999744bfb21SJohn Baldwin 		taskqgroup_detach(qgroup_wg_tqg, &sc->sc_decrypt[i]);
3000744bfb21SJohn Baldwin 	}
3001744bfb21SJohn Baldwin 	free(sc->sc_encrypt, M_WG);
3002744bfb21SJohn Baldwin 	free(sc->sc_decrypt, M_WG);
3003744bfb21SJohn Baldwin 	wg_queue_deinit(&sc->sc_handshake_queue);
3004744bfb21SJohn Baldwin 	wg_queue_deinit(&sc->sc_encrypt_parallel);
3005744bfb21SJohn Baldwin 	wg_queue_deinit(&sc->sc_decrypt_parallel);
3006744bfb21SJohn Baldwin 
3007744bfb21SJohn Baldwin 	RADIX_NODE_HEAD_DESTROY(sc->sc_aip4);
3008744bfb21SJohn Baldwin 	RADIX_NODE_HEAD_DESTROY(sc->sc_aip6);
3009744bfb21SJohn Baldwin 	rn_detachhead((void **)&sc->sc_aip4);
3010744bfb21SJohn Baldwin 	rn_detachhead((void **)&sc->sc_aip6);
3011744bfb21SJohn Baldwin 
3012744bfb21SJohn Baldwin 	cookie_checker_free(&sc->sc_cookie);
3013744bfb21SJohn Baldwin 
3014744bfb21SJohn Baldwin 	if (cred != NULL)
3015744bfb21SJohn Baldwin 		crfree(cred);
301643be2d7aSAaron LI 	bpfdetach(sc->sc_ifp);
3017744bfb21SJohn Baldwin 	if_detach(sc->sc_ifp);
3018744bfb21SJohn Baldwin 	if_free(sc->sc_ifp);
3019744bfb21SJohn Baldwin 
3020744bfb21SJohn Baldwin 	noise_local_free(sc->sc_local, wg_clone_deferred_free);
3021eb3f9a7aSAlan Somers 
3022eb3f9a7aSAlan Somers 	return (0);
3023744bfb21SJohn Baldwin }
3024744bfb21SJohn Baldwin 
3025744bfb21SJohn Baldwin static void
wg_qflush(if_t ifp __unused)302687e72834SJustin Hibbits wg_qflush(if_t ifp __unused)
3027744bfb21SJohn Baldwin {
3028744bfb21SJohn Baldwin }
3029744bfb21SJohn Baldwin 
3030744bfb21SJohn Baldwin /*
3031744bfb21SJohn Baldwin  * Privileged information (private-key, preshared-key) are only exported for
3032744bfb21SJohn Baldwin  * root and jailed root by default.
3033744bfb21SJohn Baldwin  */
3034744bfb21SJohn Baldwin static bool
wgc_privileged(struct wg_softc * sc)3035744bfb21SJohn Baldwin wgc_privileged(struct wg_softc *sc)
3036744bfb21SJohn Baldwin {
3037744bfb21SJohn Baldwin 	struct thread *td;
3038744bfb21SJohn Baldwin 
3039744bfb21SJohn Baldwin 	td = curthread;
3040744bfb21SJohn Baldwin 	return (priv_check(td, PRIV_NET_WG) == 0);
3041744bfb21SJohn Baldwin }
3042744bfb21SJohn Baldwin 
3043744bfb21SJohn Baldwin static void
wg_reassign(if_t ifp,struct vnet * new_vnet __unused,char * unused __unused)304487e72834SJustin Hibbits wg_reassign(if_t ifp, struct vnet *new_vnet __unused,
3045744bfb21SJohn Baldwin     char *unused __unused)
3046744bfb21SJohn Baldwin {
3047744bfb21SJohn Baldwin 	struct wg_softc *sc;
3048744bfb21SJohn Baldwin 
304987e72834SJustin Hibbits 	sc = if_getsoftc(ifp);
3050744bfb21SJohn Baldwin 	wg_down(sc);
3051744bfb21SJohn Baldwin }
3052744bfb21SJohn Baldwin 
3053744bfb21SJohn Baldwin static void
wg_init(void * xsc)3054744bfb21SJohn Baldwin wg_init(void *xsc)
3055744bfb21SJohn Baldwin {
3056744bfb21SJohn Baldwin 	struct wg_softc *sc;
3057744bfb21SJohn Baldwin 
3058744bfb21SJohn Baldwin 	sc = xsc;
3059744bfb21SJohn Baldwin 	wg_up(sc);
3060744bfb21SJohn Baldwin }
3061744bfb21SJohn Baldwin 
3062744bfb21SJohn Baldwin static void
vnet_wg_init(const void * unused __unused)3063744bfb21SJohn Baldwin vnet_wg_init(const void *unused __unused)
3064744bfb21SJohn Baldwin {
3065eb3f9a7aSAlan Somers 	struct if_clone_addreq req = {
3066eb3f9a7aSAlan Somers 		.create_f = wg_clone_create,
3067eb3f9a7aSAlan Somers 		.destroy_f = wg_clone_destroy,
3068eb3f9a7aSAlan Somers 		.flags = IFC_F_AUTOUNIT,
3069eb3f9a7aSAlan Somers 	};
3070eb3f9a7aSAlan Somers 	V_wg_cloner = ifc_attach_cloner(wgname, &req);
3071744bfb21SJohn Baldwin }
3072744bfb21SJohn Baldwin VNET_SYSINIT(vnet_wg_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
3073744bfb21SJohn Baldwin 	     vnet_wg_init, NULL);
3074744bfb21SJohn Baldwin 
3075744bfb21SJohn Baldwin static void
vnet_wg_uninit(const void * unused __unused)3076744bfb21SJohn Baldwin vnet_wg_uninit(const void *unused __unused)
3077744bfb21SJohn Baldwin {
3078744bfb21SJohn Baldwin 	if (V_wg_cloner)
3079eb3f9a7aSAlan Somers 		ifc_detach_cloner(V_wg_cloner);
3080744bfb21SJohn Baldwin }
3081744bfb21SJohn Baldwin VNET_SYSUNINIT(vnet_wg_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
3082744bfb21SJohn Baldwin 	       vnet_wg_uninit, NULL);
3083744bfb21SJohn Baldwin 
3084744bfb21SJohn Baldwin static int
wg_prison_remove(void * obj,void * data __unused)3085744bfb21SJohn Baldwin wg_prison_remove(void *obj, void *data __unused)
3086744bfb21SJohn Baldwin {
3087744bfb21SJohn Baldwin 	const struct prison *pr = obj;
3088744bfb21SJohn Baldwin 	struct wg_softc *sc;
3089744bfb21SJohn Baldwin 
3090744bfb21SJohn Baldwin 	/*
3091744bfb21SJohn Baldwin 	 * Do a pass through all if_wg interfaces and release creds on any from
3092744bfb21SJohn Baldwin 	 * the jail that are supposed to be going away.  This will, in turn, let
3093744bfb21SJohn Baldwin 	 * the jail die so that we don't end up with Schrödinger's jail.
3094744bfb21SJohn Baldwin 	 */
3095744bfb21SJohn Baldwin 	sx_slock(&wg_sx);
3096744bfb21SJohn Baldwin 	LIST_FOREACH(sc, &wg_list, sc_entry) {
3097744bfb21SJohn Baldwin 		sx_xlock(&sc->sc_lock);
3098744bfb21SJohn Baldwin 		if (!(sc->sc_flags & WGF_DYING) && sc->sc_ucred && sc->sc_ucred->cr_prison == pr) {
3099744bfb21SJohn Baldwin 			struct ucred *cred = sc->sc_ucred;
3100744bfb21SJohn Baldwin 			DPRINTF(sc, "Creating jail exiting\n");
3101744bfb21SJohn Baldwin 			if_link_state_change(sc->sc_ifp, LINK_STATE_DOWN);
3102744bfb21SJohn Baldwin 			wg_socket_uninit(sc);
3103744bfb21SJohn Baldwin 			sc->sc_ucred = NULL;
3104744bfb21SJohn Baldwin 			crfree(cred);
3105744bfb21SJohn Baldwin 			sc->sc_flags |= WGF_DYING;
3106744bfb21SJohn Baldwin 		}
3107744bfb21SJohn Baldwin 		sx_xunlock(&sc->sc_lock);
3108744bfb21SJohn Baldwin 	}
3109744bfb21SJohn Baldwin 	sx_sunlock(&wg_sx);
3110744bfb21SJohn Baldwin 
3111744bfb21SJohn Baldwin 	return (0);
3112744bfb21SJohn Baldwin }
3113744bfb21SJohn Baldwin 
3114744bfb21SJohn Baldwin #ifdef SELFTESTS
3115744bfb21SJohn Baldwin #include "selftest/allowedips.c"
wg_run_selftests(void)3116744bfb21SJohn Baldwin static bool wg_run_selftests(void)
3117744bfb21SJohn Baldwin {
3118744bfb21SJohn Baldwin 	bool ret = true;
3119744bfb21SJohn Baldwin 	ret &= wg_allowedips_selftest();
3120744bfb21SJohn Baldwin 	ret &= noise_counter_selftest();
3121744bfb21SJohn Baldwin 	ret &= cookie_selftest();
3122744bfb21SJohn Baldwin 	return ret;
3123744bfb21SJohn Baldwin }
3124744bfb21SJohn Baldwin #else
wg_run_selftests(void)3125744bfb21SJohn Baldwin static inline bool wg_run_selftests(void) { return true; }
3126744bfb21SJohn Baldwin #endif
3127744bfb21SJohn Baldwin 
3128744bfb21SJohn Baldwin static int
wg_module_init(void)3129744bfb21SJohn Baldwin wg_module_init(void)
3130744bfb21SJohn Baldwin {
3131b08ee10cSKyle Evans 	int ret;
3132744bfb21SJohn Baldwin 	osd_method_t methods[PR_MAXMETHOD] = {
3133744bfb21SJohn Baldwin 		[PR_METHOD_REMOVE] = wg_prison_remove,
3134744bfb21SJohn Baldwin 	};
3135744bfb21SJohn Baldwin 
3136b6a0ed7cSMark Johnston 	wg_packet_zone = uma_zcreate("wg packet", sizeof(struct wg_packet),
3137b6a0ed7cSMark Johnston 	     NULL, NULL, NULL, NULL, 0, 0);
3138b6a0ed7cSMark Johnston 
3139744bfb21SJohn Baldwin 	ret = crypto_init();
3140744bfb21SJohn Baldwin 	if (ret != 0)
3141b08ee10cSKyle Evans 		return (ret);
3142ad9f4e63SKyle Evans 	ret = cookie_init();
3143ad9f4e63SKyle Evans 	if (ret != 0)
3144b08ee10cSKyle Evans 		return (ret);
3145744bfb21SJohn Baldwin 
3146744bfb21SJohn Baldwin 	wg_osd_jail_slot = osd_jail_register(NULL, methods);
3147744bfb21SJohn Baldwin 
3148744bfb21SJohn Baldwin 	if (!wg_run_selftests())
3149b08ee10cSKyle Evans 		return (ENOTRECOVERABLE);
3150744bfb21SJohn Baldwin 
3151744bfb21SJohn Baldwin 	return (0);
3152744bfb21SJohn Baldwin }
3153744bfb21SJohn Baldwin 
3154744bfb21SJohn Baldwin static void
wg_module_deinit(void)3155744bfb21SJohn Baldwin wg_module_deinit(void)
3156744bfb21SJohn Baldwin {
3157744bfb21SJohn Baldwin 	VNET_ITERATOR_DECL(vnet_iter);
3158744bfb21SJohn Baldwin 	VNET_LIST_RLOCK();
3159744bfb21SJohn Baldwin 	VNET_FOREACH(vnet_iter) {
3160744bfb21SJohn Baldwin 		struct if_clone *clone = VNET_VNET(vnet_iter, wg_cloner);
3161744bfb21SJohn Baldwin 		if (clone) {
3162eb3f9a7aSAlan Somers 			ifc_detach_cloner(clone);
3163744bfb21SJohn Baldwin 			VNET_VNET(vnet_iter, wg_cloner) = NULL;
3164744bfb21SJohn Baldwin 		}
3165744bfb21SJohn Baldwin 	}
3166744bfb21SJohn Baldwin 	VNET_LIST_RUNLOCK();
3167744bfb21SJohn Baldwin 	NET_EPOCH_WAIT();
3168744bfb21SJohn Baldwin 	MPASS(LIST_EMPTY(&wg_list));
3169b08ee10cSKyle Evans 	if (wg_osd_jail_slot != 0)
3170744bfb21SJohn Baldwin 		osd_jail_deregister(wg_osd_jail_slot);
3171744bfb21SJohn Baldwin 	cookie_deinit();
3172744bfb21SJohn Baldwin 	crypto_deinit();
3173b08ee10cSKyle Evans 	if (wg_packet_zone != NULL)
3174744bfb21SJohn Baldwin 		uma_zdestroy(wg_packet_zone);
3175744bfb21SJohn Baldwin }
3176744bfb21SJohn Baldwin 
3177744bfb21SJohn Baldwin static int
wg_module_event_handler(module_t mod,int what,void * arg)3178744bfb21SJohn Baldwin wg_module_event_handler(module_t mod, int what, void *arg)
3179744bfb21SJohn Baldwin {
3180744bfb21SJohn Baldwin 	switch (what) {
3181744bfb21SJohn Baldwin 		case MOD_LOAD:
3182744bfb21SJohn Baldwin 			return wg_module_init();
3183744bfb21SJohn Baldwin 		case MOD_UNLOAD:
3184744bfb21SJohn Baldwin 			wg_module_deinit();
3185744bfb21SJohn Baldwin 			break;
3186744bfb21SJohn Baldwin 		default:
3187744bfb21SJohn Baldwin 			return (EOPNOTSUPP);
3188744bfb21SJohn Baldwin 	}
3189744bfb21SJohn Baldwin 	return (0);
3190744bfb21SJohn Baldwin }
3191744bfb21SJohn Baldwin 
3192744bfb21SJohn Baldwin static moduledata_t wg_moduledata = {
319361b95bcbSKristof Provost 	"if_wg",
3194744bfb21SJohn Baldwin 	wg_module_event_handler,
3195744bfb21SJohn Baldwin 	NULL
3196744bfb21SJohn Baldwin };
3197744bfb21SJohn Baldwin 
319861b95bcbSKristof Provost DECLARE_MODULE(if_wg, wg_moduledata, SI_SUB_PSEUDO, SI_ORDER_ANY);
319961b95bcbSKristof Provost MODULE_VERSION(if_wg, WIREGUARD_VERSION);
320061b95bcbSKristof Provost MODULE_DEPEND(if_wg, crypto, 1, 1, 1);
3201