2 * SPDX-License-Identifier: ISC
4 * Copyright (C) 2019-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
5 * Copyright (C) 2019-2020 Matt Dunwoodie <ncon@noconroy.net>
6 * Copyright (c) 2023 Aaron LI <aly@aaronly.me>
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 #include <sys/param.h>
22 #include <sys/ioctl.h>
23 #include <sys/socket.h>
25 #include <arpa/inet.h>
26 #include <net/wg/if_wg.h>
32 #include <netdb.h> /* getaddrinfo(), getnameinfo() */
33 #include <resolv.h> /* b64_pton(), b64_ntop() */
35 #include <stddef.h> /* ptrdiff_t */
40 #include <time.h> /* timespec_get() */
45 * WG_BASE64_KEY_LEN is the size of a base64 encoded WireGuard key.
46 * For every 4 input (base64) bytes, 3 output bytes wil be produced.
47 * The output will be padded with 0 bits, therefore we need more than
48 * the regular 32 bytes of space.
50 #define WG_BASE64_KEY_LEN (4 * ((WG_KEY_SIZE + 2) / 3))
52 static struct wg_data_io wg_data;
53 static struct wg_interface_io *wg_interface;
54 static struct wg_peer_io *wg_peer;
55 static struct wg_aip_io *wg_aip;
61 if (wg_interface != NULL)
64 strlcpy(wg_data.wgd_name, IfName, sizeof(wg_data.wgd_name));
65 wg_data.wgd_size = sizeof(*wg_interface);
66 wg_data.wgd_interface = wg_interface = calloc(1, wg_data.wgd_size);
67 if (wg_interface == NULL)
72 wg_data_grow(size_t by)
74 ptrdiff_t peer_offset, aip_offset;
78 peer_offset = (char *)wg_peer - (char *)wg_interface;
79 aip_offset = (char *)wg_aip - (char *)wg_interface;
81 wg_data.wgd_size += by;
82 wg_data.wgd_interface = realloc(wg_interface, wg_data.wgd_size);
83 if (wg_data.wgd_interface == NULL)
86 wg_interface = wg_data.wgd_interface;
87 memset((char *)wg_interface + wg_data.wgd_size - by, 0, by);
90 wg_peer = (void *)((char *)wg_interface + peer_offset);
92 wg_aip = (void *)((char *)wg_interface + aip_offset);
97 wg_callback(int s, void *arg __unused)
99 if (ioctl(s, SIOCSWG, &wg_data) == -1)
100 err(1, "%s: SIOCSWG", wg_data.wgd_name);
103 static bool wg_cb_registered;
105 #define WG_REGISTER_CALLBACK() \
106 if (!wg_cb_registered) { \
107 callback_register(wg_callback, NULL); \
108 wg_cb_registered = true; \
113 wg_setkey(const char *privkey, int arg __unused, int s __unused,
114 const struct afswtch *afp __unused)
118 if (b64_pton(privkey, wg_interface->i_private, WG_KEY_SIZE)
120 errx(1, "wgkey: invalid private key: %s", privkey);
121 wg_interface->i_flags |= WG_INTERFACE_HAS_PRIVATE;
123 WG_REGISTER_CALLBACK();
127 wg_setport(const char *port, int arg __unused, int s __unused,
128 const struct afswtch *afp __unused)
130 const char *errmsg = NULL;
134 wg_interface->i_port = (in_port_t)strtonum(port, 0, 65535, &errmsg);
136 errx(1, "wgport: invalid port %s: %s", port, errmsg);
137 wg_interface->i_flags |= WG_INTERFACE_HAS_PORT;
139 WG_REGISTER_CALLBACK();
143 wg_setcookie(const char *cookie, int arg __unused, int s __unused,
144 const struct afswtch *afp __unused)
146 const char *errmsg = NULL;
150 wg_interface->i_cookie =
151 (uint32_t)strtonum(cookie, 0, UINT32_MAX, &errmsg);
153 errx(1, "wgcookie: invalid cookie %s: %s", cookie, errmsg);
154 wg_interface->i_flags |= WG_INTERFACE_HAS_COOKIE;
156 WG_REGISTER_CALLBACK();
160 wg_unsetcookie(const char *x __unused, int arg __unused, int s __unused,
161 const struct afswtch *afp __unused)
165 /* Unset cookie by setting it to value 0. */
166 wg_interface->i_cookie = 0;
167 wg_interface->i_flags |= WG_INTERFACE_HAS_COOKIE;
169 WG_REGISTER_CALLBACK();
174 wg_setpeer(const char *peerkey, int arg __unused, int s __unused,
175 const struct afswtch *afp __unused)
177 wg_data_grow(sizeof(*wg_peer));
180 wg_peer = &wg_interface->i_peers[0];
182 wg_peer = (struct wg_peer_io *)wg_aip;
183 wg_aip = &wg_peer->p_aips[0];
185 if (b64_pton(peerkey, wg_peer->p_public, WG_KEY_SIZE) != WG_KEY_SIZE)
186 errx(1, "wgpeer: invalid peer key: %s", peerkey);
187 wg_peer->p_flags |= WG_PEER_HAS_PUBLIC;
188 wg_interface->i_peers_count++;
190 WG_REGISTER_CALLBACK();
194 wg_unsetpeer(const char *peerkey, int arg, int s, const struct afswtch *afp)
196 wg_setpeer(peerkey, arg, s, afp);
197 wg_peer->p_flags |= WG_PEER_REMOVE;
201 wg_unsetpeerall(const char *x __unused, int arg __unused, int s __unused,
202 const struct afswtch *afp __unused)
206 wg_interface->i_flags |= WG_INTERFACE_REPLACE_PEERS;
208 WG_REGISTER_CALLBACK();
212 wg_setpeeraip(const char *aip, int arg __unused, int s __unused,
213 const struct afswtch *afp __unused)
218 errx(1, "wgaip: wgpeer not set");
220 wg_data_grow(sizeof(*wg_aip));
222 if ((res = inet_net_pton(AF_INET, aip, &wg_aip->a_ipv4,
223 sizeof(wg_aip->a_ipv4))) != -1) {
224 wg_aip->a_af = AF_INET;
225 } else if ((res = inet_net_pton(AF_INET6, aip, &wg_aip->a_ipv6,
226 sizeof(wg_aip->a_ipv6))) != -1) {
227 wg_aip->a_af = AF_INET6;
229 errx(1, "wgaip: bad address: %s", aip);
231 wg_aip->a_cidr = res;
233 wg_peer->p_flags |= WG_PEER_REPLACE_AIPS;
234 wg_peer->p_aips_count++;
238 WG_REGISTER_CALLBACK();
242 wg_setpeerpsk(const char *psk, int arg __unused, int s __unused,
243 const struct afswtch *afp __unused)
246 errx(1, "wgpsk: wgpeer not set");
248 if (b64_pton(psk, wg_peer->p_psk, WG_KEY_SIZE) != WG_KEY_SIZE)
249 errx(1, "wgpsk: invalid key: %s", psk);
250 wg_peer->p_flags |= WG_PEER_HAS_PSK;
252 WG_REGISTER_CALLBACK();
256 wg_unsetpeerpsk(const char *x __unused, int arg __unused, int s __unused,
257 const struct afswtch *afp __unused)
260 errx(1, "-wgpsk: wgpeer not set");
262 /* Unset PSK by setting it to empty. */
263 memset(wg_peer->p_psk, 0, sizeof(wg_peer->p_psk));
264 wg_peer->p_flags |= WG_PEER_HAS_PSK;
266 WG_REGISTER_CALLBACK();
270 wg_setpeerpka(const char *pka, int arg __unused, int s __unused,
271 const struct afswtch *afp __unused)
273 const char *errmsg = NULL;
276 errx(1, "wgpka: wgpeer not set");
278 /* 43200 seconds == 12h, reasonable for a uint16_t value */
279 wg_peer->p_pka = (uint16_t)strtonum(pka, 0, 43200, &errmsg);
281 errx(1, "wgpka: invalid pka %s: %s", pka, errmsg);
282 wg_peer->p_flags |= WG_PEER_HAS_PKA;
284 WG_REGISTER_CALLBACK();
288 wg_unsetpeerpka(const char *x __unused, int arg __unused, int s __unused,
289 const struct afswtch *afp __unused)
292 errx(1, "wgpka: wgpeer not set");
295 wg_peer->p_flags |= WG_PEER_HAS_PKA;
297 WG_REGISTER_CALLBACK();
301 wg_setpeerep(const char *host, const char *service, int s __unused,
302 const struct afswtch *afp __unused)
308 errx(1, "wgendpoint: wgpeer not set");
310 if ((error = getaddrinfo(host, service, NULL, &ai)) != 0)
311 errx(1, "%s", gai_strerror(error));
313 memcpy(&wg_peer->p_sa, ai->ai_addr, ai->ai_addrlen);
314 wg_peer->p_flags |= WG_PEER_HAS_ENDPOINT;
317 WG_REGISTER_CALLBACK();
321 wg_setpeerdesc(const char *desc, int arg __unused, int s __unused,
322 const struct afswtch *afp __unused)
325 errx(1, "wgdescr: wgpeer not set");
327 strlcpy(wg_peer->p_description, desc, sizeof(wg_peer->p_description));
328 wg_peer->p_flags |= WG_PEER_SET_DESCRIPTION;
330 WG_REGISTER_CALLBACK();
334 wg_unsetpeerdesc(const char *x __unused, int arg __unused, int s __unused,
335 const struct afswtch *afp __unused)
338 errx(1, "-wgpsk: wgpeer not set");
340 memset(wg_peer->p_description, 0, sizeof(wg_peer->p_description));
341 wg_peer->p_flags |= WG_PEER_SET_DESCRIPTION;
343 WG_REGISTER_CALLBACK();
351 size_t i, j, last_size;
352 char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
353 char key[WG_BASE64_KEY_LEN + 1];
355 memset(&wg_data, 0, sizeof(wg_data));
356 strlcpy(wg_data.wgd_name, IfName, sizeof(wg_data.wgd_name));
359 last_size = wg_data.wgd_size;
361 if (ioctl(s, SIOCGWG, &wg_data) == -1) {
362 if (errno == EINVAL || errno == ENOTTY)
364 err(1, "%s: SIOCGWG", wg_data.wgd_name);
367 if (last_size >= wg_data.wgd_size)
370 wg_interface = calloc(1, wg_data.wgd_size);
371 if (wg_interface == NULL)
373 free(wg_data.wgd_interface);
374 wg_data.wgd_interface = wg_interface;
377 wg_interface = wg_data.wgd_interface;
379 if (wg_interface->i_flags & WG_INTERFACE_HAS_PORT)
380 printf("\twgport: %hu\n", wg_interface->i_port);
381 if (wg_interface->i_flags & WG_INTERFACE_HAS_COOKIE)
382 printf("\twgcookie: %u\n", wg_interface->i_cookie);
383 if (wg_interface->i_flags & WG_INTERFACE_HAS_PRIVATE && printkeys) {
384 b64_ntop(wg_interface->i_private, WG_KEY_SIZE,
386 printf("\twgkey: %s\n", key);
388 if (wg_interface->i_flags & WG_INTERFACE_HAS_PUBLIC) {
389 b64_ntop(wg_interface->i_public, WG_KEY_SIZE,
391 printf("\twgpubkey: %s\n", key);
394 wg_peer = &wg_interface->i_peers[0];
395 for (i = 0; i < wg_interface->i_peers_count; i++) {
396 b64_ntop(wg_peer->p_public, WG_KEY_SIZE,
398 printf("\twgpeer: %s\n", key);
400 if (wg_peer->p_description[0] != '\0')
401 printf("\t\twgdescr: %s\n", wg_peer->p_description);
402 if (wg_peer->p_flags & WG_PEER_HAS_PSK) {
404 b64_ntop(wg_peer->p_psk, WG_KEY_SIZE,
406 printf("\t\twgpsk: %s\n", key);
408 printf("\t\twgpsk: (present)\n");
411 if ((wg_peer->p_flags & WG_PEER_HAS_PKA) && wg_peer->p_pka > 0)
412 printf("\t\twgpka: %u (seconds)\n", wg_peer->p_pka);
413 if (wg_peer->p_flags & WG_PEER_HAS_ENDPOINT) {
414 if (getnameinfo(&wg_peer->p_sa, wg_peer->p_sa.sa_len,
415 hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
416 NI_NUMERICHOST | NI_NUMERICSERV) == 0)
417 printf("\t\twgendpoint: %s %s\n", hbuf, sbuf);
419 printf("\t\twgendpoint: (unable to print)\n");
422 printf("\t\ttx: %" PRIu64 " (bytes), rx: %" PRIu64 " (bytes)\n",
423 wg_peer->p_txbytes, wg_peer->p_rxbytes);
425 if (wg_peer->p_last_handshake.tv_sec != 0) {
426 timespec_get(&now, TIME_UTC);
427 printf("\t\tlast handshake: %ld seconds ago\n",
428 now.tv_sec - wg_peer->p_last_handshake.tv_sec);
431 for (j = 0; j < wg_peer->p_aips_count; j++) {
432 wg_aip = &wg_peer->p_aips[j];
433 inet_ntop(wg_aip->a_af, &wg_aip->a_addr,
435 printf("\t\twgaip: %s/%d\n", hbuf, wg_aip->a_cidr);
438 wg_aip = &wg_peer->p_aips[wg_peer->p_aips_count];
439 wg_peer = (struct wg_peer_io *)wg_aip;
443 free(wg_data.wgd_interface);
447 static struct cmd wg_cmds[] = {
448 DEF_CMD_ARG("wgkey", wg_setkey),
449 DEF_CMD_ARG("wgport", wg_setport),
450 DEF_CMD_ARG("wgcookie", wg_setcookie),
451 DEF_CMD("-wgcookie", 0, wg_unsetcookie),
452 DEF_CMD_ARG("wgpeer", wg_setpeer),
453 DEF_CMD_ARG("-wgpeer", wg_unsetpeer),
454 DEF_CMD("-wgpeerall", 0, wg_unsetpeerall),
455 DEF_CMD_ARG("wgaip", wg_setpeeraip),
456 DEF_CMD_ARG("wgpsk", wg_setpeerpsk),
457 DEF_CMD("-wgpsk", 0, wg_unsetpeerpsk),
458 DEF_CMD_ARG("wgpka", wg_setpeerpka),
459 DEF_CMD("-wgpka", 0, wg_unsetpeerpka),
460 DEF_CMD_ARG2("wgendpoint", wg_setpeerep),
461 DEF_CMD_ARG("wgdescr", wg_setpeerdesc),
462 DEF_CMD_ARG("wgdescription", wg_setpeerdesc),
463 DEF_CMD("-wgdescr", 0, wg_unsetpeerdesc),
464 DEF_CMD("-wgdescription", 0, wg_unsetpeerdesc),
467 static struct afswtch af_wg = {
468 .af_name = "af_wg", /* dummy */
470 .af_other_status = wg_status,
479 for (i = 0; i < nitems(wg_cmds); i++)
480 cmd_register(&wg_cmds[i]);