2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2020 Rubicon Communications, LLC (Netgate)
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/ioctl.h>
34 #include <sys/socket.h>
35 #include <sys/sysctl.h>
39 #include <net/ethernet.h>
41 #include <net/if_dl.h>
42 #include <net/if_types.h>
43 #include <net/if_media.h>
44 #include <net/route.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
61 #include <stddef.h> /* NB: for offsetof */
73 static nvlist_t *nvl_params;
75 static int allowed_ips_count;
76 static int allowed_ips_max;
78 struct sockaddr_storage a_addr;
79 struct sockaddr_storage a_mask;
81 struct allowedip *allowed_ips;
83 #define ALLOWEDIPS_START 16
85 #define WG_KEY_LEN_BASE64 ((((WG_KEY_LEN) + 2) / 3) * 4 + 1)
86 #define WG_KEY_LEN_HEX (WG_KEY_LEN * 2 + 1)
87 #define WG_MAX_STRLEN 64
90 key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64)
93 if (strlen(base64) != WG_KEY_LEN_BASE64 - 1) {
94 warnx("bad key len - need %d got %zu\n", WG_KEY_LEN_BASE64 - 1, strlen(base64));
97 if (base64[WG_KEY_LEN_BASE64 - 2] != '=') {
98 warnx("bad key terminator, expected '=' got '%c'", base64[WG_KEY_LEN_BASE64 - 2]);
101 return (b64_pton(base64, key, WG_KEY_LEN));
105 parse_endpoint(const char *endpoint_)
108 char *base, *endpoint, *port, *colon, *tmp;
109 struct addrinfo hints, *res;
111 endpoint = base = strdup(endpoint_);
112 colon = rindex(endpoint, ':');
114 errx(1, "bad endpoint format %s - no port delimiter found", endpoint);
119 if (endpoint[0] == '[') {
121 tmp = index(endpoint, ']');
123 errx(1, "bad endpoint format %s - '[' found with no matching ']'", endpoint);
126 bzero(&hints, sizeof(hints));
127 hints.ai_family = AF_UNSPEC;
128 err = getaddrinfo(endpoint, port, &hints, &res);
130 errx(1, "%s", gai_strerror(err));
131 nvlist_add_binary(nvl_params, "endpoint", res->ai_addr, res->ai_addrlen);
137 in_len2mask(struct in_addr *mask, u_int len)
143 memset(mask, 0, sizeof(*mask));
144 for (i = 0; i < len / NBBY; i++)
147 p[i] = (0xff00 >> (len % NBBY)) & 0xff;
151 in_mask2len(struct in_addr *mask)
157 for (x = 0; x < sizeof(*mask); x++) {
162 if (x < sizeof(*mask)) {
163 for (y = 0; y < NBBY; y++) {
164 if ((p[x] & (0x80 >> y)) == 0)
172 in6_prefixlen2mask(struct in6_addr *maskp, int len)
174 static const u_char maskarray[NBBY] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};
175 int bytelen, bitlen, i;
178 if (len < 0 || len > 128) {
179 errx(1, "in6_prefixlen2mask: invalid prefix length(%d)\n",
184 memset(maskp, 0, sizeof(*maskp));
185 bytelen = len / NBBY;
187 for (i = 0; i < bytelen; i++)
188 maskp->s6_addr[i] = 0xff;
190 maskp->s6_addr[bytelen] = maskarray[bitlen - 1];
194 in6_mask2len(struct in6_addr *mask, u_char *lim0)
197 u_char *lim = lim0, *p;
199 /* ignore the scope_id part */
200 if (lim0 == NULL || lim0 - (u_char *)mask > sizeof(*mask))
201 lim = (u_char *)mask + sizeof(*mask);
202 for (p = (u_char *)mask; p < lim; x++, p++) {
208 for (y = 0; y < NBBY; y++) {
209 if ((*p & (0x80 >> y)) == 0)
215 * when the limit pointer is given, do a stricter check on the
219 if (y != 0 && (*p & (0x00ff >> y)) != 0)
221 for (p = p + 1; p < lim; p++)
230 parse_ip(struct allowedip *aip, const char *value)
232 struct addrinfo hints, *res;
235 bzero(&aip->a_addr, sizeof(aip->a_addr));
236 bzero(&hints, sizeof(hints));
237 hints.ai_family = AF_UNSPEC;
238 hints.ai_flags = AI_NUMERICHOST;
239 err = getaddrinfo(value, NULL, &hints, &res);
241 errx(1, "%s", gai_strerror(err));
243 memcpy(&aip->a_addr, res->ai_addr, res->ai_addrlen);
250 sa_ntop(const struct sockaddr *sa, char *buf, int *port)
252 const struct sockaddr_in *sin;
253 const struct sockaddr_in6 *sin6;
256 err = getnameinfo(sa, sa->sa_len, buf, INET6_ADDRSTRLEN, NULL,
259 if (sa->sa_family == AF_INET) {
260 sin = (const struct sockaddr_in *)sa;
262 *port = sin->sin_port;
263 } else if (sa->sa_family == AF_INET6) {
264 sin6 = (const struct sockaddr_in6 *)sa;
266 *port = sin6->sin6_port;
270 errx(1, "%s", gai_strerror(err));
274 dump_peer(const nvlist_t *nvl_peer)
277 const struct allowedip *aips;
278 const struct sockaddr *endpoint;
279 char outbuf[WG_MAX_STRLEN];
280 char addr_buf[INET6_ADDRSTRLEN];
285 if (nvlist_exists_binary(nvl_peer, "public-key")) {
286 key = nvlist_get_binary(nvl_peer, "public-key", &size);
287 b64_ntop((const uint8_t *)key, size, outbuf, WG_MAX_STRLEN);
288 printf("PublicKey = %s\n", outbuf);
290 if (nvlist_exists_binary(nvl_peer, "endpoint")) {
291 endpoint = nvlist_get_binary(nvl_peer, "endpoint", &size);
292 sa_ntop(endpoint, addr_buf, &port);
293 printf("Endpoint = %s:%d\n", addr_buf, ntohs(port));
296 if (!nvlist_exists_binary(nvl_peer, "allowed-ips"))
298 aips = nvlist_get_binary(nvl_peer, "allowed-ips", &size);
299 if (size == 0 || size % sizeof(struct allowedip) != 0) {
300 errx(1, "size %zu not integer multiple of allowedip", size);
302 printf("AllowedIPs = ");
303 count = size / sizeof(struct allowedip);
304 for (int i = 0; i < count; i++) {
310 sa = __DECONST(void *, &aips[i].a_addr);
311 bitmask = __DECONST(void *,
312 ((const struct sockaddr *)&aips->a_mask)->sa_data);
313 family = aips[i].a_addr.ss_family;
314 getnameinfo(sa, sa->sa_len, addr_buf, INET6_ADDRSTRLEN, NULL,
316 if (family == AF_INET)
317 mask = in_mask2len(bitmask);
318 else if (family == AF_INET6)
319 mask = in6_mask2len(bitmask, NULL);
321 errx(1, "bad family in peer %d\n", family);
322 printf("%s/%d", addr_buf, mask);
330 get_nvl_out_size(int sock, u_long op, size_t *size)
335 memset(&ifd, 0, sizeof(ifd));
337 strlcpy(ifd.ifd_name, name, sizeof(ifd.ifd_name));
342 err = ioctl(sock, SIOCGDRVSPEC, &ifd);
350 do_cmd(int sock, u_long op, void *arg, size_t argsize, int set)
354 memset(&ifd, 0, sizeof(ifd));
356 strlcpy(ifd.ifd_name, name, sizeof(ifd.ifd_name));
358 ifd.ifd_len = argsize;
361 return (ioctl(sock, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd));
365 DECL_CMD_FUNC(peerlist, val, d)
367 size_t size, peercount;
369 const nvlist_t *nvl, *nvl_peer;
370 const nvlist_t *const *nvl_peerlist;
372 if (get_nvl_out_size(s, WGC_GET, &size))
373 errx(1, "can't get peer list size");
374 if ((packed = malloc(size)) == NULL)
375 errx(1, "malloc failed for peer list");
376 if (do_cmd(s, WGC_GET, packed, size, 0))
377 errx(1, "failed to obtain peer list");
379 nvl = nvlist_unpack(packed, size, 0);
380 if (!nvlist_exists_nvlist_array(nvl, "peer-list"))
382 nvl_peerlist = nvlist_get_nvlist_array(nvl, "peer-list", &peercount);
384 for (int i = 0; i < peercount; i++, nvl_peerlist++) {
385 nvl_peer = *nvl_peerlist;
391 peerfinish(int s, void *arg)
393 nvlist_t *nvl, **nvl_array;
397 if ((nvl = nvlist_create(0)) == NULL)
398 errx(1, "failed to allocate nvlist");
399 if ((nvl_array = calloc(sizeof(void *), 1)) == NULL)
400 errx(1, "failed to allocate nvl_array");
401 if (!nvlist_exists_binary(nvl_params, "public-key"))
402 errx(1, "must specify a public-key for adding peer");
403 if (!nvlist_exists_binary(nvl_params, "endpoint"))
404 errx(1, "must specify an endpoint for adding peer");
405 if (allowed_ips_count == 0)
406 errx(1, "must specify at least one range of allowed-ips to add a peer");
408 nvl_array[0] = nvl_params;
409 nvlist_add_nvlist_array(nvl, "peer-list", (const nvlist_t * const *)nvl_array, 1);
410 packed = nvlist_pack(nvl, &size);
412 if (do_cmd(s, WGC_SET, packed, size, true))
413 errx(1, "failed to install peer");
417 DECL_CMD_FUNC(peerstart, val, d)
420 callback_register(peerfinish, NULL);
421 allowed_ips = malloc(ALLOWEDIPS_START * sizeof(struct allowedip));
422 allowed_ips_max = ALLOWEDIPS_START;
423 if (allowed_ips == NULL)
424 errx(1, "failed to allocate array for allowedips");
428 DECL_CMD_FUNC(setwglistenport, val, d)
430 struct addrinfo hints, *res;
431 const struct sockaddr_in *sin;
432 const struct sockaddr_in6 *sin6;
437 bzero(&hints, sizeof(hints));
438 hints.ai_family = AF_UNSPEC;
439 hints.ai_flags = AI_NUMERICHOST;
440 err = getaddrinfo(NULL, val, &hints, &res);
442 errx(1, "%s", gai_strerror(err));
444 if (res->ai_family == AF_INET) {
445 sin = (struct sockaddr_in *)res->ai_addr;
447 } else if (res->ai_family == AF_INET6) {
448 sin6 = (struct sockaddr_in6 *)res->ai_addr;
449 ul = sin6->sin6_port;
451 errx(1, "unknown family");
453 ul = ntohs((u_short)ul);
454 nvlist_add_number(nvl_params, "listen-port", ul);
458 DECL_CMD_FUNC(setwgprivkey, val, d)
460 uint8_t key[WG_KEY_LEN];
462 if (!key_from_base64(key, val))
463 errx(1, "invalid key %s", val);
464 nvlist_add_binary(nvl_params, "private-key", key, WG_KEY_LEN);
468 DECL_CMD_FUNC(setwgpubkey, val, d)
470 uint8_t key[WG_KEY_LEN];
473 errx(1, "setting public key only valid when adding peer");
475 if (!key_from_base64(key, val))
476 errx(1, "invalid key %s", val);
477 nvlist_add_binary(nvl_params, "public-key", key, WG_KEY_LEN);
481 DECL_CMD_FUNC(setallowedips, val, d)
483 char *base, *allowedip, *mask;
486 struct allowedip *aip;
489 errx(1, "setting allowed ip only valid when adding peer");
490 if (allowed_ips_count == allowed_ips_max) {
493 aip = &allowed_ips[allowed_ips_count];
494 base = allowedip = strdup(val);
495 mask = index(allowedip, '/');
497 errx(1, "mask separator not found in allowedip %s", val);
500 parse_ip(aip, allowedip);
501 ul = strtoul(mask, &endp, 0);
503 errx(1, "invalid value for allowedip mask");
504 bzero(&aip->a_mask, sizeof(aip->a_mask));
505 if (aip->a_addr.ss_family == AF_INET)
506 in_len2mask((struct in_addr *)&((struct sockaddr *)&aip->a_mask)->sa_data, ul);
507 else if (aip->a_addr.ss_family == AF_INET6)
508 in6_prefixlen2mask((struct in6_addr *)&((struct sockaddr *)&aip->a_mask)->sa_data, ul);
510 errx(1, "invalid address family %d\n", aip->a_addr.ss_family);
512 if (allowed_ips_count > 1)
513 nvlist_free_binary(nvl_params, "allowed-ips");
514 nvlist_add_binary(nvl_params, "allowed-ips", allowed_ips,
515 allowed_ips_count*sizeof(*aip));
517 dump_peer(nvl_params);
522 DECL_CMD_FUNC(setendpoint, val, d)
525 errx(1, "setting endpoint only valid when adding peer");
530 wireguard_status(int s)
535 char buf[WG_KEY_LEN_BASE64];
537 uint16_t listen_port;
539 if (get_nvl_out_size(s, WGC_GET, &size))
541 if ((packed = malloc(size)) == NULL)
543 if (do_cmd(s, WGC_GET, packed, size, 0))
545 nvl = nvlist_unpack(packed, size, 0);
546 if (nvlist_exists_number(nvl, "listen-port")) {
547 listen_port = nvlist_get_number(nvl, "listen-port");
548 printf("\tlisten-port: %d\n", listen_port);
550 if (nvlist_exists_binary(nvl, "private-key")) {
551 key = nvlist_get_binary(nvl, "private-key", &size);
552 b64_ntop((const uint8_t *)key, size, buf, WG_MAX_STRLEN);
553 printf("\tprivate-key: %s\n", buf);
555 if (nvlist_exists_binary(nvl, "public-key")) {
556 key = nvlist_get_binary(nvl, "public-key", &size);
557 b64_ntop((const uint8_t *)key, size, buf, WG_MAX_STRLEN);
558 printf("\tpublic-key: %s\n", buf);
562 static struct cmd wireguard_cmds[] = {
563 DEF_CLONE_CMD_ARG("listen-port", setwglistenport),
564 DEF_CLONE_CMD_ARG("private-key", setwgprivkey),
565 DEF_CMD("peer-list", 0, peerlist),
566 DEF_CMD("peer", 0, peerstart),
567 DEF_CMD_ARG("public-key", setwgpubkey),
568 DEF_CMD_ARG("allowed-ips", setallowedips),
569 DEF_CMD_ARG("endpoint", setendpoint),
572 static struct afswtch af_wireguard = {
573 .af_name = "af_wireguard",
575 .af_other_status = wireguard_status,
579 wg_create(int s, struct ifreq *ifr)
585 setproctitle("ifconfig %s create ...\n", name);
586 if (!nvlist_exists_number(nvl_params, "listen-port"))
588 if (!nvlist_exists_binary(nvl_params, "private-key"))
591 packed = nvlist_pack(nvl_params, &size);
593 errx(1, "failed to setup create request");
595 iov.iov_base = packed;
596 ifr->ifr_data = (caddr_t)&iov;
597 if (ioctl(s, SIOCIFCREATE2, ifr) < 0)
598 err(1, "SIOCIFCREATE2");
601 ifr->ifr_data == NULL;
602 if (ioctl(s, SIOCIFCREATE, ifr) < 0)
603 err(1, "SIOCIFCREATE");
606 static __constructor void
611 nvl_params = nvlist_create(0);
612 for (i = 0; i < nitems(wireguard_cmds); i++)
613 cmd_register(&wireguard_cmds[i]);
614 af_register(&af_wireguard);
615 clone_setdefcallback_prefix("wg", wg_create);