xref: /dflybsd-src/sbin/ifconfig/ifwg.c (revision 11bfc5a143f2724aee12854f3d6413ae89332dc5)
1ead657e8SAaron LI /*-
2ead657e8SAaron LI  * SPDX-License-Identifier: ISC
3ead657e8SAaron LI  *
4ead657e8SAaron LI  * Copyright (C) 2019-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
5ead657e8SAaron LI  * Copyright (C) 2019-2020 Matt Dunwoodie <ncon@noconroy.net>
6d4d5b6b1SAaron LI  * Copyright (c) 2023-2024 Aaron LI <aly@aaronly.me>
7ead657e8SAaron LI  *
8ead657e8SAaron LI  * Permission to use, copy, modify, and distribute this software for any
9ead657e8SAaron LI  * purpose with or without fee is hereby granted, provided that the above
10ead657e8SAaron LI  * copyright notice and this permission notice appear in all copies.
11ead657e8SAaron LI  *
12ead657e8SAaron LI  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13ead657e8SAaron LI  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14ead657e8SAaron LI  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15ead657e8SAaron LI  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16ead657e8SAaron LI  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17ead657e8SAaron LI  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18ead657e8SAaron LI  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19ead657e8SAaron LI  */
20ead657e8SAaron LI 
21ead657e8SAaron LI #include <sys/param.h>
22ead657e8SAaron LI #include <sys/ioctl.h>
23ead657e8SAaron LI #include <sys/socket.h>
24ead657e8SAaron LI 
25ead657e8SAaron LI #include <arpa/inet.h>
26ead657e8SAaron LI #include <net/wg/if_wg.h>
27ead657e8SAaron LI 
28ead657e8SAaron LI #include <err.h>
29ead657e8SAaron LI #include <errno.h>
30ead657e8SAaron LI #include <inttypes.h>
31ead657e8SAaron LI #include <limits.h>
32ead657e8SAaron LI #include <netdb.h> /* getaddrinfo(), getnameinfo() */
33ead657e8SAaron LI #include <resolv.h> /* b64_pton(), b64_ntop() */
34ead657e8SAaron LI #include <stdbool.h>
35ead657e8SAaron LI #include <stddef.h> /* ptrdiff_t */
36ead657e8SAaron LI #include <stdint.h>
37ead657e8SAaron LI #include <stdio.h>
38ead657e8SAaron LI #include <stdlib.h>
39ead657e8SAaron LI #include <string.h>
40ead657e8SAaron LI #include <time.h> /* timespec_get() */
41ead657e8SAaron LI 
42ead657e8SAaron LI #include "ifconfig.h"
43ead657e8SAaron LI 
44ead657e8SAaron LI /*
45ead657e8SAaron LI  * WG_BASE64_KEY_LEN is the size of a base64 encoded WireGuard key.
46ead657e8SAaron LI  * For every 4 input (base64) bytes, 3 output bytes wil be produced.
47ead657e8SAaron LI  * The output will be padded with 0 bits, therefore we need more than
48ead657e8SAaron LI  * the regular 32 bytes of space.
49ead657e8SAaron LI  */
50ead657e8SAaron LI #define WG_BASE64_KEY_LEN	(4 * ((WG_KEY_SIZE + 2) / 3))
51ead657e8SAaron LI 
52ead657e8SAaron LI static struct wg_data_io wg_data;
53ead657e8SAaron LI static struct wg_interface_io *wg_interface;
54ead657e8SAaron LI static struct wg_peer_io *wg_peer;
55ead657e8SAaron LI static struct wg_aip_io *wg_aip;
56ead657e8SAaron LI 
57ead657e8SAaron LI 
58ead657e8SAaron LI static void
wg_data_init(void)59ead657e8SAaron LI wg_data_init(void)
60ead657e8SAaron LI {
61ead657e8SAaron LI 	if (wg_interface != NULL)
62ead657e8SAaron LI 		return;
63ead657e8SAaron LI 
64ead657e8SAaron LI 	strlcpy(wg_data.wgd_name, IfName, sizeof(wg_data.wgd_name));
65ead657e8SAaron LI 	wg_data.wgd_size = sizeof(*wg_interface);
66ead657e8SAaron LI 	wg_data.wgd_interface = wg_interface = calloc(1, wg_data.wgd_size);
67ead657e8SAaron LI 	if (wg_interface == NULL)
68ead657e8SAaron LI 		err(1, "calloc");
69ead657e8SAaron LI }
70ead657e8SAaron LI 
71ead657e8SAaron LI static void
wg_data_grow(size_t by)72ead657e8SAaron LI wg_data_grow(size_t by)
73ead657e8SAaron LI {
74ead657e8SAaron LI 	ptrdiff_t peer_offset, aip_offset;
75ead657e8SAaron LI 
76ead657e8SAaron LI 	wg_data_init();
77ead657e8SAaron LI 
78ead657e8SAaron LI 	peer_offset = (char *)wg_peer - (char *)wg_interface;
79ead657e8SAaron LI 	aip_offset = (char *)wg_aip - (char *)wg_interface;
80ead657e8SAaron LI 
81ead657e8SAaron LI 	wg_data.wgd_size += by;
82ead657e8SAaron LI 	wg_data.wgd_interface = realloc(wg_interface, wg_data.wgd_size);
83ead657e8SAaron LI 	if (wg_data.wgd_interface == NULL)
84ead657e8SAaron LI 		err(1, "realloc");
85ead657e8SAaron LI 
86ead657e8SAaron LI 	wg_interface = wg_data.wgd_interface;
87ead657e8SAaron LI 	memset((char *)wg_interface + wg_data.wgd_size - by, 0, by);
88ead657e8SAaron LI 
89ead657e8SAaron LI 	if (wg_peer != NULL)
90ead657e8SAaron LI 		wg_peer = (void *)((char *)wg_interface + peer_offset);
91ead657e8SAaron LI 	if (wg_aip != NULL)
92ead657e8SAaron LI 		wg_aip = (void *)((char *)wg_interface + aip_offset);
93ead657e8SAaron LI }
94ead657e8SAaron LI 
95ead657e8SAaron LI 
96ead657e8SAaron LI static void
wg_callback(int s,void * arg __unused)97ead657e8SAaron LI wg_callback(int s, void *arg __unused)
98ead657e8SAaron LI {
99ead657e8SAaron LI 	if (ioctl(s, SIOCSWG, &wg_data) == -1)
100ead657e8SAaron LI 		err(1, "%s: SIOCSWG", wg_data.wgd_name);
101ead657e8SAaron LI }
102ead657e8SAaron LI 
103ead657e8SAaron LI static bool wg_cb_registered;
104ead657e8SAaron LI 
105ead657e8SAaron LI #define WG_REGISTER_CALLBACK()					\
106ead657e8SAaron LI 	if (!wg_cb_registered) {				\
107ead657e8SAaron LI 		callback_register(wg_callback, NULL);		\
108ead657e8SAaron LI 		wg_cb_registered = true;			\
109ead657e8SAaron LI 	}
110ead657e8SAaron LI 
111ead657e8SAaron LI 
112ead657e8SAaron LI static void
wg_setkey(const char * privkey,int arg __unused,int s __unused,const struct afswtch * afp __unused)113ead657e8SAaron LI wg_setkey(const char *privkey, int arg __unused, int s __unused,
114ead657e8SAaron LI 	  const struct afswtch *afp __unused)
115ead657e8SAaron LI {
116ead657e8SAaron LI 	wg_data_init();
117ead657e8SAaron LI 
118ead657e8SAaron LI 	if (b64_pton(privkey, wg_interface->i_private, WG_KEY_SIZE)
119ead657e8SAaron LI 	    != WG_KEY_SIZE)
120ead657e8SAaron LI 		errx(1, "wgkey: invalid private key: %s", privkey);
121ead657e8SAaron LI 	wg_interface->i_flags |= WG_INTERFACE_HAS_PRIVATE;
122ead657e8SAaron LI 
123ead657e8SAaron LI 	WG_REGISTER_CALLBACK();
124ead657e8SAaron LI }
125ead657e8SAaron LI 
126ead657e8SAaron LI static void
wg_setport(const char * port,int arg __unused,int s __unused,const struct afswtch * afp __unused)127ead657e8SAaron LI wg_setport(const char *port, int arg __unused, int s __unused,
128ead657e8SAaron LI 	   const struct afswtch *afp __unused)
129ead657e8SAaron LI {
130ead657e8SAaron LI 	const char *errmsg = NULL;
131ead657e8SAaron LI 
132ead657e8SAaron LI 	wg_data_init();
133ead657e8SAaron LI 
134ead657e8SAaron LI 	wg_interface->i_port = (in_port_t)strtonum(port, 0, 65535, &errmsg);
135ead657e8SAaron LI 	if (errmsg != NULL)
136ead657e8SAaron LI 		errx(1, "wgport: invalid port %s: %s", port, errmsg);
137ead657e8SAaron LI 	wg_interface->i_flags |= WG_INTERFACE_HAS_PORT;
138ead657e8SAaron LI 
139ead657e8SAaron LI 	WG_REGISTER_CALLBACK();
140ead657e8SAaron LI }
141ead657e8SAaron LI 
142ead657e8SAaron LI static void
wg_setcookie(const char * cookie,int arg __unused,int s __unused,const struct afswtch * afp __unused)143ead657e8SAaron LI wg_setcookie(const char *cookie, int arg __unused, int s __unused,
144ead657e8SAaron LI 	     const struct afswtch *afp __unused)
145ead657e8SAaron LI {
146ead657e8SAaron LI 	const char *errmsg = NULL;
147ead657e8SAaron LI 
148ead657e8SAaron LI 	wg_data_init();
149ead657e8SAaron LI 
150ead657e8SAaron LI 	wg_interface->i_cookie =
151ead657e8SAaron LI 		(uint32_t)strtonum(cookie, 0, UINT32_MAX, &errmsg);
152ead657e8SAaron LI 	if (errmsg != NULL)
153ead657e8SAaron LI 		errx(1, "wgcookie: invalid cookie %s: %s", cookie, errmsg);
154ead657e8SAaron LI 	wg_interface->i_flags |= WG_INTERFACE_HAS_COOKIE;
155ead657e8SAaron LI 
156ead657e8SAaron LI 	WG_REGISTER_CALLBACK();
157ead657e8SAaron LI }
158ead657e8SAaron LI 
159ead657e8SAaron LI static void
wg_unsetcookie(const char * x __unused,int arg __unused,int s __unused,const struct afswtch * afp __unused)160ead657e8SAaron LI wg_unsetcookie(const char *x __unused, int arg __unused, int s __unused,
161ead657e8SAaron LI 	       const struct afswtch *afp __unused)
162ead657e8SAaron LI {
163ead657e8SAaron LI 	wg_data_init();
164ead657e8SAaron LI 
165ead657e8SAaron LI 	/* Unset cookie by setting it to value 0. */
166ead657e8SAaron LI 	wg_interface->i_cookie = 0;
167ead657e8SAaron LI 	wg_interface->i_flags |= WG_INTERFACE_HAS_COOKIE;
168ead657e8SAaron LI 
169ead657e8SAaron LI 	WG_REGISTER_CALLBACK();
170ead657e8SAaron LI }
171ead657e8SAaron LI 
172ead657e8SAaron LI 
173ead657e8SAaron LI static void
wg_setpeer(const char * peerkey,int arg __unused,int s __unused,const struct afswtch * afp __unused)174ead657e8SAaron LI wg_setpeer(const char *peerkey, int arg __unused, int s __unused,
175ead657e8SAaron LI 	   const struct afswtch *afp __unused)
176ead657e8SAaron LI {
177ead657e8SAaron LI 	wg_data_grow(sizeof(*wg_peer));
178ead657e8SAaron LI 
179ead657e8SAaron LI 	if (wg_aip == NULL)
18082082fe8SAaron LI 		wg_peer = &wg_interface->i_peers[0]; /* first peer */
181ead657e8SAaron LI 	else
18282082fe8SAaron LI 		wg_peer = (struct wg_peer_io *)wg_aip; /* end of last peer */
183ead657e8SAaron LI 	wg_aip = &wg_peer->p_aips[0];
184ead657e8SAaron LI 
185ead657e8SAaron LI 	if (b64_pton(peerkey, wg_peer->p_public, WG_KEY_SIZE) != WG_KEY_SIZE)
186ead657e8SAaron LI 		errx(1, "wgpeer: invalid peer key: %s", peerkey);
187ead657e8SAaron LI 	wg_peer->p_flags |= WG_PEER_HAS_PUBLIC;
188ead657e8SAaron LI 	wg_interface->i_peers_count++;
189ead657e8SAaron LI 
190ead657e8SAaron LI 	WG_REGISTER_CALLBACK();
191ead657e8SAaron LI }
192ead657e8SAaron LI 
193ead657e8SAaron LI static void
wg_unsetpeer(const char * peerkey,int arg,int s,const struct afswtch * afp)194ead657e8SAaron LI wg_unsetpeer(const char *peerkey, int arg, int s, const struct afswtch *afp)
195ead657e8SAaron LI {
196ead657e8SAaron LI 	wg_setpeer(peerkey, arg, s, afp);
197ead657e8SAaron LI 	wg_peer->p_flags |= WG_PEER_REMOVE;
198ead657e8SAaron LI }
199ead657e8SAaron LI 
200ead657e8SAaron LI static void
wg_unsetpeerall(const char * x __unused,int arg __unused,int s __unused,const struct afswtch * afp __unused)201ead657e8SAaron LI wg_unsetpeerall(const char *x __unused, int arg __unused, int s __unused,
202ead657e8SAaron LI 		const struct afswtch *afp __unused)
203ead657e8SAaron LI {
204ead657e8SAaron LI 	wg_data_init();
205ead657e8SAaron LI 
206ead657e8SAaron LI 	wg_interface->i_flags |= WG_INTERFACE_REPLACE_PEERS;
207ead657e8SAaron LI 
208ead657e8SAaron LI 	WG_REGISTER_CALLBACK();
209ead657e8SAaron LI }
210ead657e8SAaron LI 
211d4d5b6b1SAaron LI /*
212d4d5b6b1SAaron LI  * Manually parse the CIDR instead of using inet_net_pton() because:
213d4d5b6b1SAaron LI  * 1. it uses a legacy IPv6 CIDR format (e.g., 1:2:3:4/64) and fails to parse
214d4d5b6b1SAaron LI  *    some now valid IPv6 CIDRs (e.g., 1:2:3:4::/64);
215d4d5b6b1SAaron LI  * 2. it's not standard and behaves differently across BSDs and Linux.
216d4d5b6b1SAaron LI  */
217d4d5b6b1SAaron LI static int
wg_aip_parse(const char * aip,struct wg_aip_io * waip)218d4d5b6b1SAaron LI wg_aip_parse(const char *aip, struct wg_aip_io *waip)
219d4d5b6b1SAaron LI {
220*11bfc5a1SAaron LI 	const char *errmsg = NULL;
221d4d5b6b1SAaron LI 	char *p, buf[INET6_ADDRSTRLEN + sizeof("/128")];
222d4d5b6b1SAaron LI 	int plen;
223d4d5b6b1SAaron LI 
224d4d5b6b1SAaron LI 	if (snprintf(buf, sizeof(buf), "%s", aip) >= (int)sizeof(buf))
225d4d5b6b1SAaron LI 		return (-1);
226d4d5b6b1SAaron LI 
227d4d5b6b1SAaron LI 	plen = 128;
228d4d5b6b1SAaron LI 	if ((p = strchr(buf, '/')) != NULL) {
229*11bfc5a1SAaron LI 		*p = '\0';
230*11bfc5a1SAaron LI 		plen = (int)strtonum(p + 1, 0, 128, &errmsg);
231*11bfc5a1SAaron LI 		if (errmsg != NULL)
232*11bfc5a1SAaron LI 			return (-1);
233d4d5b6b1SAaron LI 	}
234d4d5b6b1SAaron LI 
235d4d5b6b1SAaron LI 	if (inet_pton(AF_INET6, buf, &waip->a_ipv6) == 1) {
236d4d5b6b1SAaron LI 		if (plen < 0 || plen > 128)
237d4d5b6b1SAaron LI 			return (-1);
238d4d5b6b1SAaron LI 		waip->a_cidr = plen;
239d4d5b6b1SAaron LI 		waip->a_af = AF_INET6;
240d4d5b6b1SAaron LI 		return (0);
241d4d5b6b1SAaron LI 	}
242d4d5b6b1SAaron LI 
243d4d5b6b1SAaron LI 	if (inet_pton(AF_INET, buf, &waip->a_ipv4) == 1) {
244d4d5b6b1SAaron LI 		if (plen == 128)
245d4d5b6b1SAaron LI 			plen = 32;
246d4d5b6b1SAaron LI 		if (plen < 0 || plen > 32)
247d4d5b6b1SAaron LI 			return (-1);
248d4d5b6b1SAaron LI 		waip->a_cidr = plen;
249d4d5b6b1SAaron LI 		waip->a_af = AF_INET;
250d4d5b6b1SAaron LI 		return (0);
251d4d5b6b1SAaron LI 	}
252d4d5b6b1SAaron LI 
253d4d5b6b1SAaron LI 	return (-1);
254d4d5b6b1SAaron LI }
255d4d5b6b1SAaron LI 
256ead657e8SAaron LI static void
wg_setpeeraip(const char * aip,int arg __unused,int s __unused,const struct afswtch * afp __unused)257ead657e8SAaron LI wg_setpeeraip(const char *aip, int arg __unused, int s __unused,
258ead657e8SAaron LI 	      const struct afswtch *afp __unused)
259ead657e8SAaron LI {
260ead657e8SAaron LI 	if (wg_peer == NULL)
261ead657e8SAaron LI 		errx(1, "wgaip: wgpeer not set");
262ead657e8SAaron LI 
263ead657e8SAaron LI 	wg_data_grow(sizeof(*wg_aip));
264ead657e8SAaron LI 
265d4d5b6b1SAaron LI 	if (wg_aip_parse(aip, wg_aip) == -1)
266ead657e8SAaron LI 		errx(1, "wgaip: bad address: %s", aip);
267ead657e8SAaron LI 
268ead657e8SAaron LI 	wg_peer->p_flags |= WG_PEER_REPLACE_AIPS;
269ead657e8SAaron LI 	wg_peer->p_aips_count++;
270ead657e8SAaron LI 
271ead657e8SAaron LI 	wg_aip++;
272ead657e8SAaron LI 
273ead657e8SAaron LI 	WG_REGISTER_CALLBACK();
274ead657e8SAaron LI }
275ead657e8SAaron LI 
276ead657e8SAaron LI static void
wg_setpeerpsk(const char * psk,int arg __unused,int s __unused,const struct afswtch * afp __unused)277ead657e8SAaron LI wg_setpeerpsk(const char *psk, int arg __unused, int s __unused,
278ead657e8SAaron LI 	      const struct afswtch *afp __unused)
279ead657e8SAaron LI {
280ead657e8SAaron LI 	if (wg_peer == NULL)
281ead657e8SAaron LI 		errx(1, "wgpsk: wgpeer not set");
282ead657e8SAaron LI 
283ead657e8SAaron LI 	if (b64_pton(psk, wg_peer->p_psk, WG_KEY_SIZE) != WG_KEY_SIZE)
284ead657e8SAaron LI 		errx(1, "wgpsk: invalid key: %s", psk);
285ead657e8SAaron LI 	wg_peer->p_flags |= WG_PEER_HAS_PSK;
286ead657e8SAaron LI 
287ead657e8SAaron LI 	WG_REGISTER_CALLBACK();
288ead657e8SAaron LI }
289ead657e8SAaron LI 
290ead657e8SAaron LI static void
wg_unsetpeerpsk(const char * x __unused,int arg __unused,int s __unused,const struct afswtch * afp __unused)291ead657e8SAaron LI wg_unsetpeerpsk(const char *x __unused, int arg __unused, int s __unused,
292ead657e8SAaron LI 		const struct afswtch *afp __unused)
293ead657e8SAaron LI {
294ead657e8SAaron LI 	if (wg_peer == NULL)
295ead657e8SAaron LI 		errx(1, "-wgpsk: wgpeer not set");
296ead657e8SAaron LI 
297ead657e8SAaron LI 	/* Unset PSK by setting it to empty. */
298ead657e8SAaron LI 	memset(wg_peer->p_psk, 0, sizeof(wg_peer->p_psk));
299ead657e8SAaron LI 	wg_peer->p_flags |= WG_PEER_HAS_PSK;
300ead657e8SAaron LI 
301ead657e8SAaron LI 	WG_REGISTER_CALLBACK();
302ead657e8SAaron LI }
303ead657e8SAaron LI 
304ead657e8SAaron LI static void
wg_setpeerpka(const char * pka,int arg __unused,int s __unused,const struct afswtch * afp __unused)305ead657e8SAaron LI wg_setpeerpka(const char *pka, int arg __unused, int s __unused,
306ead657e8SAaron LI 	      const struct afswtch *afp __unused)
307ead657e8SAaron LI {
308ead657e8SAaron LI 	const char *errmsg = NULL;
309ead657e8SAaron LI 
310ead657e8SAaron LI 	if (wg_peer == NULL)
311ead657e8SAaron LI 		errx(1, "wgpka: wgpeer not set");
312ead657e8SAaron LI 
313ead657e8SAaron LI 	/* 43200 seconds == 12h, reasonable for a uint16_t value */
314ead657e8SAaron LI 	wg_peer->p_pka = (uint16_t)strtonum(pka, 0, 43200, &errmsg);
315ead657e8SAaron LI 	if (errmsg != NULL)
316ead657e8SAaron LI 		errx(1, "wgpka: invalid pka %s: %s", pka, errmsg);
317ead657e8SAaron LI 	wg_peer->p_flags |= WG_PEER_HAS_PKA;
318ead657e8SAaron LI 
319ead657e8SAaron LI 	WG_REGISTER_CALLBACK();
320ead657e8SAaron LI }
321ead657e8SAaron LI 
322ead657e8SAaron LI static void
wg_unsetpeerpka(const char * x __unused,int arg __unused,int s __unused,const struct afswtch * afp __unused)323ead657e8SAaron LI wg_unsetpeerpka(const char *x __unused, int arg __unused, int s __unused,
324ead657e8SAaron LI 		const struct afswtch *afp __unused)
325ead657e8SAaron LI {
326ead657e8SAaron LI 	if (wg_peer == NULL)
32782082fe8SAaron LI 		errx(1, "-wgpka: wgpeer not set");
328ead657e8SAaron LI 
329ead657e8SAaron LI 	wg_peer->p_pka = 0;
330ead657e8SAaron LI 	wg_peer->p_flags |= WG_PEER_HAS_PKA;
331ead657e8SAaron LI 
332ead657e8SAaron LI 	WG_REGISTER_CALLBACK();
333ead657e8SAaron LI }
334ead657e8SAaron LI 
335ead657e8SAaron LI static void
wg_setpeerendpoint(const char * host,const char * service,int s __unused,const struct afswtch * afp __unused)33682082fe8SAaron LI wg_setpeerendpoint(const char *host, const char *service, int s __unused,
337ead657e8SAaron LI 		   const struct afswtch *afp __unused)
338ead657e8SAaron LI {
339ead657e8SAaron LI 	struct addrinfo *ai;
340ead657e8SAaron LI 	int error;
341ead657e8SAaron LI 
342ead657e8SAaron LI 	if (wg_peer == NULL)
343ead657e8SAaron LI 		errx(1, "wgendpoint: wgpeer not set");
344ead657e8SAaron LI 
345ead657e8SAaron LI 	if ((error = getaddrinfo(host, service, NULL, &ai)) != 0)
346ead657e8SAaron LI 		errx(1, "%s", gai_strerror(error));
347ead657e8SAaron LI 
348ead657e8SAaron LI 	memcpy(&wg_peer->p_sa, ai->ai_addr, ai->ai_addrlen);
349ead657e8SAaron LI 	wg_peer->p_flags |= WG_PEER_HAS_ENDPOINT;
350ead657e8SAaron LI 
351ead657e8SAaron LI 	freeaddrinfo(ai);
352ead657e8SAaron LI 	WG_REGISTER_CALLBACK();
353ead657e8SAaron LI }
354ead657e8SAaron LI 
355ead657e8SAaron LI static void
wg_setpeerdesc(const char * desc,int arg __unused,int s __unused,const struct afswtch * afp __unused)356ead657e8SAaron LI wg_setpeerdesc(const char *desc, int arg __unused, int s __unused,
357ead657e8SAaron LI 	       const struct afswtch *afp __unused)
358ead657e8SAaron LI {
359ead657e8SAaron LI 	if (wg_peer == NULL)
360ead657e8SAaron LI 		errx(1, "wgdescr: wgpeer not set");
361ead657e8SAaron LI 
362ead657e8SAaron LI 	strlcpy(wg_peer->p_description, desc, sizeof(wg_peer->p_description));
363ead657e8SAaron LI 	wg_peer->p_flags |= WG_PEER_SET_DESCRIPTION;
364ead657e8SAaron LI 
365ead657e8SAaron LI 	WG_REGISTER_CALLBACK();
366ead657e8SAaron LI }
367ead657e8SAaron LI 
368ead657e8SAaron LI static void
wg_unsetpeerdesc(const char * x __unused,int arg __unused,int s __unused,const struct afswtch * afp __unused)369ead657e8SAaron LI wg_unsetpeerdesc(const char *x __unused, int arg __unused, int s __unused,
370ead657e8SAaron LI 		 const struct afswtch *afp __unused)
371ead657e8SAaron LI {
372ead657e8SAaron LI 	if (wg_peer == NULL)
373ead657e8SAaron LI 		errx(1, "-wgpsk: wgpeer not set");
374ead657e8SAaron LI 
375ead657e8SAaron LI 	memset(wg_peer->p_description, 0, sizeof(wg_peer->p_description));
376ead657e8SAaron LI 	wg_peer->p_flags |= WG_PEER_SET_DESCRIPTION;
377ead657e8SAaron LI 
378ead657e8SAaron LI 	WG_REGISTER_CALLBACK();
379ead657e8SAaron LI }
380ead657e8SAaron LI 
381ead657e8SAaron LI 
382ead657e8SAaron LI static void
wg_status(int s)383ead657e8SAaron LI wg_status(int s)
384ead657e8SAaron LI {
385ead657e8SAaron LI 	struct timespec now;
386ead657e8SAaron LI 	size_t i, j, last_size;
387ead657e8SAaron LI 	char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
388ead657e8SAaron LI 	char key[WG_BASE64_KEY_LEN + 1];
389ead657e8SAaron LI 
390ead657e8SAaron LI 	memset(&wg_data, 0, sizeof(wg_data));
391ead657e8SAaron LI 	strlcpy(wg_data.wgd_name, IfName, sizeof(wg_data.wgd_name));
392ead657e8SAaron LI 
393ead657e8SAaron LI 	for (;;) {
394ead657e8SAaron LI 		last_size = wg_data.wgd_size;
395ead657e8SAaron LI 
396ead657e8SAaron LI 		if (ioctl(s, SIOCGWG, &wg_data) == -1) {
397ead657e8SAaron LI 			if (errno == EINVAL || errno == ENOTTY)
398ead657e8SAaron LI 				goto out;
399ead657e8SAaron LI 			err(1, "%s: SIOCGWG", wg_data.wgd_name);
400ead657e8SAaron LI 		}
401ead657e8SAaron LI 
402ead657e8SAaron LI 		if (last_size >= wg_data.wgd_size)
403ead657e8SAaron LI 			break;
404ead657e8SAaron LI 
405ead657e8SAaron LI 		wg_interface = calloc(1, wg_data.wgd_size);
406ead657e8SAaron LI 		if (wg_interface == NULL)
407ead657e8SAaron LI 			err(1, "calloc");
408ead657e8SAaron LI 		free(wg_data.wgd_interface);
409ead657e8SAaron LI 		wg_data.wgd_interface = wg_interface;
410ead657e8SAaron LI 	}
411ead657e8SAaron LI 
412ead657e8SAaron LI 	wg_interface = wg_data.wgd_interface;
413ead657e8SAaron LI 
414ead657e8SAaron LI 	if (wg_interface->i_flags & WG_INTERFACE_HAS_PORT)
415ead657e8SAaron LI 		printf("\twgport: %hu\n", wg_interface->i_port);
416ead657e8SAaron LI 	if (wg_interface->i_flags & WG_INTERFACE_HAS_COOKIE)
417ead657e8SAaron LI 		printf("\twgcookie: %u\n", wg_interface->i_cookie);
418b03c8f13SAaron LI 	if (wg_interface->i_flags & WG_INTERFACE_HAS_PRIVATE && printkeys) {
419b03c8f13SAaron LI 		b64_ntop(wg_interface->i_private, WG_KEY_SIZE,
420b03c8f13SAaron LI 			 key, sizeof(key));
421b03c8f13SAaron LI 		printf("\twgkey: %s\n", key);
422b03c8f13SAaron LI 	}
423ead657e8SAaron LI 	if (wg_interface->i_flags & WG_INTERFACE_HAS_PUBLIC) {
424ead657e8SAaron LI 		b64_ntop(wg_interface->i_public, WG_KEY_SIZE,
425ead657e8SAaron LI 			 key, sizeof(key));
426ead657e8SAaron LI 		printf("\twgpubkey: %s\n", key);
427ead657e8SAaron LI 	}
428ead657e8SAaron LI 
429ead657e8SAaron LI 	wg_peer = &wg_interface->i_peers[0];
430ead657e8SAaron LI 	for (i = 0; i < wg_interface->i_peers_count; i++) {
431ead657e8SAaron LI 		b64_ntop(wg_peer->p_public, WG_KEY_SIZE,
432ead657e8SAaron LI 			 key, sizeof(key));
433ead657e8SAaron LI 		printf("\twgpeer: %s\n", key);
434ead657e8SAaron LI 
4351671e443SAaron LI 		printf("\t\tid: %" PRIu64 "\n", wg_peer->p_id);
436ead657e8SAaron LI 		if (wg_peer->p_description[0] != '\0')
437ead657e8SAaron LI 			printf("\t\twgdescr: %s\n", wg_peer->p_description);
438b03c8f13SAaron LI 		if (wg_peer->p_flags & WG_PEER_HAS_PSK) {
439b03c8f13SAaron LI 			if (printkeys) {
440b03c8f13SAaron LI 				b64_ntop(wg_peer->p_psk, WG_KEY_SIZE,
441b03c8f13SAaron LI 					 key, sizeof(key));
442b03c8f13SAaron LI 				printf("\t\twgpsk: %s\n", key);
443b03c8f13SAaron LI 			} else {
444ead657e8SAaron LI 				printf("\t\twgpsk: (present)\n");
445b03c8f13SAaron LI 			}
446b03c8f13SAaron LI 		}
447ead657e8SAaron LI 		if ((wg_peer->p_flags & WG_PEER_HAS_PKA) && wg_peer->p_pka > 0)
448ead657e8SAaron LI 			printf("\t\twgpka: %u (seconds)\n", wg_peer->p_pka);
449ead657e8SAaron LI 		if (wg_peer->p_flags & WG_PEER_HAS_ENDPOINT) {
450ead657e8SAaron LI 			if (getnameinfo(&wg_peer->p_sa, wg_peer->p_sa.sa_len,
451ead657e8SAaron LI 					hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
452ead657e8SAaron LI 					NI_NUMERICHOST | NI_NUMERICSERV) == 0)
453ead657e8SAaron LI 				printf("\t\twgendpoint: %s %s\n", hbuf, sbuf);
454ead657e8SAaron LI 			else
455ead657e8SAaron LI 				printf("\t\twgendpoint: (unable to print)\n");
456ead657e8SAaron LI 		}
457ead657e8SAaron LI 
458ead657e8SAaron LI 		printf("\t\ttx: %" PRIu64 " (bytes), rx: %" PRIu64 " (bytes)\n",
459ead657e8SAaron LI 		       wg_peer->p_txbytes, wg_peer->p_rxbytes);
460ead657e8SAaron LI 
461ead657e8SAaron LI 		if (wg_peer->p_last_handshake.tv_sec != 0) {
462ead657e8SAaron LI 			timespec_get(&now, TIME_UTC);
463ead657e8SAaron LI 			printf("\t\tlast handshake: %ld seconds ago\n",
464ead657e8SAaron LI 			       now.tv_sec - wg_peer->p_last_handshake.tv_sec);
465ead657e8SAaron LI 		}
466ead657e8SAaron LI 
467ead657e8SAaron LI 		for (j = 0; j < wg_peer->p_aips_count; j++) {
468ead657e8SAaron LI 			wg_aip = &wg_peer->p_aips[j];
469ead657e8SAaron LI 			inet_ntop(wg_aip->a_af, &wg_aip->a_addr,
470ead657e8SAaron LI 				  hbuf, sizeof(hbuf));
471ead657e8SAaron LI 			printf("\t\twgaip: %s/%d\n", hbuf, wg_aip->a_cidr);
472ead657e8SAaron LI 		}
473ead657e8SAaron LI 
474ead657e8SAaron LI 		wg_aip = &wg_peer->p_aips[wg_peer->p_aips_count];
475ead657e8SAaron LI 		wg_peer = (struct wg_peer_io *)wg_aip;
476ead657e8SAaron LI 	}
477ead657e8SAaron LI 
478ead657e8SAaron LI out:
479ead657e8SAaron LI 	free(wg_data.wgd_interface);
480ead657e8SAaron LI }
481ead657e8SAaron LI 
482ead657e8SAaron LI 
483ead657e8SAaron LI static struct cmd wg_cmds[] = {
484ead657e8SAaron LI 	DEF_CMD_ARG("wgkey",			wg_setkey),
485ead657e8SAaron LI 	DEF_CMD_ARG("wgport",			wg_setport),
486ead657e8SAaron LI 	DEF_CMD_ARG("wgcookie",			wg_setcookie),
487ead657e8SAaron LI 	DEF_CMD("-wgcookie",		0,	wg_unsetcookie),
488ead657e8SAaron LI 	DEF_CMD_ARG("wgpeer",			wg_setpeer),
489ead657e8SAaron LI 	DEF_CMD_ARG("-wgpeer",			wg_unsetpeer),
490ead657e8SAaron LI 	DEF_CMD("-wgpeerall",		0,	wg_unsetpeerall),
491ead657e8SAaron LI 	DEF_CMD_ARG("wgaip",			wg_setpeeraip),
492ead657e8SAaron LI 	DEF_CMD_ARG("wgpsk",			wg_setpeerpsk),
493ead657e8SAaron LI 	DEF_CMD("-wgpsk",		0,	wg_unsetpeerpsk),
494ead657e8SAaron LI 	DEF_CMD_ARG("wgpka",			wg_setpeerpka),
495ead657e8SAaron LI 	DEF_CMD("-wgpka",		0,	wg_unsetpeerpka),
49682082fe8SAaron LI 	DEF_CMD_ARG2("wgendpoint",		wg_setpeerendpoint),
497ead657e8SAaron LI 	DEF_CMD_ARG("wgdescr",			wg_setpeerdesc),
498ead657e8SAaron LI 	DEF_CMD_ARG("wgdescription",		wg_setpeerdesc),
499ead657e8SAaron LI 	DEF_CMD("-wgdescr",		0,	wg_unsetpeerdesc),
500ead657e8SAaron LI 	DEF_CMD("-wgdescription",	0,	wg_unsetpeerdesc),
501ead657e8SAaron LI };
502ead657e8SAaron LI 
503ead657e8SAaron LI static struct afswtch af_wg = {
504ead657e8SAaron LI 	.af_name		= "af_wg", /* dummy */
505ead657e8SAaron LI 	.af_af			= AF_UNSPEC,
506ead657e8SAaron LI 	.af_other_status	= wg_status,
507ead657e8SAaron LI };
508ead657e8SAaron LI 
509ead657e8SAaron LI __constructor(143)
510ead657e8SAaron LI static void
wg_ctor(void)511ead657e8SAaron LI wg_ctor(void)
512ead657e8SAaron LI {
513ead657e8SAaron LI 	size_t i;
514ead657e8SAaron LI 
515ead657e8SAaron LI 	for (i = 0; i < nitems(wg_cmds); i++)
516ead657e8SAaron LI 		cmd_register(&wg_cmds[i]);
517ead657e8SAaron LI 
518ead657e8SAaron LI 	af_register(&af_wg);
519ead657e8SAaron LI }
520