2020-11-29 19:38:03 +00:00
|
|
|
/*-
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
*
|
|
|
|
* Copyright (c) 2020 Rubicon Communications, LLC (Netgate)
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
|
|
|
#ifndef RESCUE
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/nv.h>
|
|
|
|
|
|
|
|
#include <net/ethernet.h>
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <net/if_dl.h>
|
|
|
|
#include <net/if_types.h>
|
|
|
|
#include <net/if_media.h>
|
|
|
|
#include <net/route.h>
|
|
|
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <err.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stddef.h> /* NB: for offsetof */
|
|
|
|
#include <locale.h>
|
|
|
|
#include <langinfo.h>
|
|
|
|
#include <resolv.h>
|
|
|
|
|
|
|
|
#include "ifconfig.h"
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
WGC_GET = 0x5,
|
|
|
|
WGC_SET = 0x6,
|
|
|
|
} wg_cmd_t;
|
|
|
|
|
|
|
|
static nvlist_t *nvl_params;
|
|
|
|
static bool do_peer;
|
|
|
|
static int allowed_ips_count;
|
|
|
|
static int allowed_ips_max;
|
|
|
|
struct allowedip {
|
|
|
|
struct sockaddr_storage a_addr;
|
|
|
|
struct sockaddr_storage a_mask;
|
|
|
|
};
|
|
|
|
struct allowedip *allowed_ips;
|
|
|
|
|
|
|
|
#define ALLOWEDIPS_START 16
|
|
|
|
#define WG_KEY_LEN 32
|
|
|
|
#define WG_KEY_LEN_BASE64 ((((WG_KEY_LEN) + 2) / 3) * 4 + 1)
|
|
|
|
#define WG_KEY_LEN_HEX (WG_KEY_LEN * 2 + 1)
|
|
|
|
#define WG_MAX_STRLEN 64
|
|
|
|
|
|
|
|
static bool
|
|
|
|
key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (strlen(base64) != WG_KEY_LEN_BASE64 - 1) {
|
|
|
|
warnx("bad key len - need %d got %zu\n", WG_KEY_LEN_BASE64 - 1, strlen(base64));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (base64[WG_KEY_LEN_BASE64 - 2] != '=') {
|
|
|
|
warnx("bad key terminator, expected '=' got '%c'", base64[WG_KEY_LEN_BASE64 - 2]);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return (b64_pton(base64, key, WG_KEY_LEN));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
parse_endpoint(const char *endpoint_)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
char *base, *endpoint, *port, *colon, *tmp;
|
|
|
|
struct addrinfo hints, *res;
|
|
|
|
|
|
|
|
endpoint = base = strdup(endpoint_);
|
|
|
|
colon = rindex(endpoint, ':');
|
|
|
|
if (colon == NULL)
|
|
|
|
errx(1, "bad endpoint format %s - no port delimiter found", endpoint);
|
|
|
|
*colon = '\0';
|
|
|
|
port = colon + 1;
|
|
|
|
|
|
|
|
/* [::]:<> */
|
|
|
|
if (endpoint[0] == '[') {
|
|
|
|
endpoint++;
|
|
|
|
tmp = index(endpoint, ']');
|
|
|
|
if (tmp == NULL)
|
|
|
|
errx(1, "bad endpoint format %s - '[' found with no matching ']'", endpoint);
|
|
|
|
*tmp = '\0';
|
|
|
|
}
|
|
|
|
bzero(&hints, sizeof(hints));
|
|
|
|
hints.ai_family = AF_UNSPEC;
|
|
|
|
err = getaddrinfo(endpoint, port, &hints, &res);
|
|
|
|
if (err)
|
|
|
|
errx(1, "%s", gai_strerror(err));
|
|
|
|
nvlist_add_binary(nvl_params, "endpoint", res->ai_addr, res->ai_addrlen);
|
|
|
|
freeaddrinfo(res);
|
|
|
|
free(base);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
in_len2mask(struct in_addr *mask, u_int len)
|
|
|
|
{
|
|
|
|
u_int i;
|
|
|
|
u_char *p;
|
|
|
|
|
|
|
|
p = (u_char *)mask;
|
|
|
|
memset(mask, 0, sizeof(*mask));
|
|
|
|
for (i = 0; i < len / NBBY; i++)
|
|
|
|
p[i] = 0xff;
|
|
|
|
if (len % NBBY)
|
|
|
|
p[i] = (0xff00 >> (len % NBBY)) & 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u_int
|
|
|
|
in_mask2len(struct in_addr *mask)
|
|
|
|
{
|
|
|
|
u_int x, y;
|
|
|
|
u_char *p;
|
|
|
|
|
|
|
|
p = (u_char *)mask;
|
|
|
|
for (x = 0; x < sizeof(*mask); x++) {
|
|
|
|
if (p[x] != 0xff)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
y = 0;
|
|
|
|
if (x < sizeof(*mask)) {
|
|
|
|
for (y = 0; y < NBBY; y++) {
|
|
|
|
if ((p[x] & (0x80 >> y)) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return x * NBBY + y;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
in6_prefixlen2mask(struct in6_addr *maskp, int len)
|
|
|
|
{
|
|
|
|
static const u_char maskarray[NBBY] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};
|
|
|
|
int bytelen, bitlen, i;
|
|
|
|
|
|
|
|
/* sanity check */
|
|
|
|
if (len < 0 || len > 128) {
|
|
|
|
errx(1, "in6_prefixlen2mask: invalid prefix length(%d)\n",
|
|
|
|
len);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(maskp, 0, sizeof(*maskp));
|
|
|
|
bytelen = len / NBBY;
|
|
|
|
bitlen = len % NBBY;
|
|
|
|
for (i = 0; i < bytelen; i++)
|
|
|
|
maskp->s6_addr[i] = 0xff;
|
|
|
|
if (bitlen)
|
|
|
|
maskp->s6_addr[bytelen] = maskarray[bitlen - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
in6_mask2len(struct in6_addr *mask, u_char *lim0)
|
|
|
|
{
|
|
|
|
int x = 0, y;
|
|
|
|
u_char *lim = lim0, *p;
|
|
|
|
|
|
|
|
/* ignore the scope_id part */
|
|
|
|
if (lim0 == NULL || lim0 - (u_char *)mask > sizeof(*mask))
|
|
|
|
lim = (u_char *)mask + sizeof(*mask);
|
|
|
|
for (p = (u_char *)mask; p < lim; x++, p++) {
|
|
|
|
if (*p != 0xff)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
y = 0;
|
|
|
|
if (p < lim) {
|
|
|
|
for (y = 0; y < NBBY; y++) {
|
|
|
|
if ((*p & (0x80 >> y)) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* when the limit pointer is given, do a stricter check on the
|
|
|
|
* remaining bits.
|
|
|
|
*/
|
|
|
|
if (p < lim) {
|
|
|
|
if (y != 0 && (*p & (0x00ff >> y)) != 0)
|
|
|
|
return -1;
|
|
|
|
for (p = p + 1; p < lim; p++)
|
|
|
|
if (*p != 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return x * NBBY + y;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
parse_ip(struct allowedip *aip, const char *value)
|
|
|
|
{
|
|
|
|
struct addrinfo hints, *res;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
bzero(&aip->a_addr, sizeof(aip->a_addr));
|
|
|
|
bzero(&hints, sizeof(hints));
|
|
|
|
hints.ai_family = AF_UNSPEC;
|
|
|
|
hints.ai_flags = AI_NUMERICHOST;
|
|
|
|
err = getaddrinfo(value, NULL, &hints, &res);
|
|
|
|
if (err)
|
|
|
|
errx(1, "%s", gai_strerror(err));
|
|
|
|
|
|
|
|
memcpy(&aip->a_addr, res->ai_addr, res->ai_addrlen);
|
|
|
|
|
|
|
|
freeaddrinfo(res);
|
|
|
|
return (true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sa_ntop(const struct sockaddr *sa, char *buf, int *port)
|
|
|
|
{
|
|
|
|
const struct sockaddr_in *sin;
|
|
|
|
const struct sockaddr_in6 *sin6;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = getnameinfo(sa, sa->sa_len, buf, INET6_ADDRSTRLEN, NULL,
|
|
|
|
0, NI_NUMERICHOST);
|
|
|
|
|
|
|
|
if (sa->sa_family == AF_INET) {
|
|
|
|
sin = (const struct sockaddr_in *)sa;
|
|
|
|
if (port)
|
|
|
|
*port = sin->sin_port;
|
|
|
|
} else if (sa->sa_family == AF_INET6) {
|
|
|
|
sin6 = (const struct sockaddr_in6 *)sa;
|
|
|
|
if (port)
|
|
|
|
*port = sin6->sin6_port;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
errx(1, "%s", gai_strerror(err));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dump_peer(const nvlist_t *nvl_peer)
|
|
|
|
{
|
|
|
|
const void *key;
|
|
|
|
const struct allowedip *aips;
|
|
|
|
const struct sockaddr *endpoint;
|
|
|
|
char outbuf[WG_MAX_STRLEN];
|
|
|
|
char addr_buf[INET6_ADDRSTRLEN];
|
|
|
|
size_t size;
|
|
|
|
int count, port;
|
|
|
|
|
|
|
|
printf("[Peer]\n");
|
|
|
|
if (nvlist_exists_binary(nvl_peer, "public-key")) {
|
|
|
|
key = nvlist_get_binary(nvl_peer, "public-key", &size);
|
|
|
|
b64_ntop((const uint8_t *)key, size, outbuf, WG_MAX_STRLEN);
|
|
|
|
printf("PublicKey = %s\n", outbuf);
|
|
|
|
}
|
|
|
|
if (nvlist_exists_binary(nvl_peer, "endpoint")) {
|
|
|
|
endpoint = nvlist_get_binary(nvl_peer, "endpoint", &size);
|
|
|
|
sa_ntop(endpoint, addr_buf, &port);
|
|
|
|
printf("Endpoint = %s:%d\n", addr_buf, ntohs(port));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!nvlist_exists_binary(nvl_peer, "allowed-ips"))
|
|
|
|
return;
|
|
|
|
aips = nvlist_get_binary(nvl_peer, "allowed-ips", &size);
|
|
|
|
if (size == 0 || size % sizeof(struct allowedip) != 0) {
|
|
|
|
errx(1, "size %zu not integer multiple of allowedip", size);
|
|
|
|
}
|
|
|
|
printf("AllowedIPs = ");
|
|
|
|
count = size / sizeof(struct allowedip);
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
int mask;
|
|
|
|
sa_family_t family;
|
|
|
|
void *bitmask;
|
|
|
|
struct sockaddr *sa;
|
|
|
|
|
|
|
|
sa = __DECONST(void *, &aips[i].a_addr);
|
|
|
|
bitmask = __DECONST(void *,
|
2021-02-15 00:38:26 +00:00
|
|
|
((const struct sockaddr *)&(&aips[i])->a_mask)->sa_data);
|
2020-11-29 19:38:03 +00:00
|
|
|
family = aips[i].a_addr.ss_family;
|
|
|
|
getnameinfo(sa, sa->sa_len, addr_buf, INET6_ADDRSTRLEN, NULL,
|
|
|
|
0, NI_NUMERICHOST);
|
|
|
|
if (family == AF_INET)
|
|
|
|
mask = in_mask2len(bitmask);
|
|
|
|
else if (family == AF_INET6)
|
|
|
|
mask = in6_mask2len(bitmask, NULL);
|
|
|
|
else
|
|
|
|
errx(1, "bad family in peer %d\n", family);
|
|
|
|
printf("%s/%d", addr_buf, mask);
|
|
|
|
if (i < count -1)
|
|
|
|
printf(", ");
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
get_nvl_out_size(int sock, u_long op, size_t *size)
|
|
|
|
{
|
|
|
|
struct ifdrv ifd;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
memset(&ifd, 0, sizeof(ifd));
|
|
|
|
|
|
|
|
strlcpy(ifd.ifd_name, name, sizeof(ifd.ifd_name));
|
|
|
|
ifd.ifd_cmd = op;
|
|
|
|
ifd.ifd_len = 0;
|
|
|
|
ifd.ifd_data = NULL;
|
|
|
|
|
|
|
|
err = ioctl(sock, SIOCGDRVSPEC, &ifd);
|
|
|
|
if (err)
|
|
|
|
return (err);
|
|
|
|
*size = ifd.ifd_len;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
do_cmd(int sock, u_long op, void *arg, size_t argsize, int set)
|
|
|
|
{
|
|
|
|
struct ifdrv ifd;
|
|
|
|
|
|
|
|
memset(&ifd, 0, sizeof(ifd));
|
|
|
|
|
|
|
|
strlcpy(ifd.ifd_name, name, sizeof(ifd.ifd_name));
|
|
|
|
ifd.ifd_cmd = op;
|
|
|
|
ifd.ifd_len = argsize;
|
|
|
|
ifd.ifd_data = arg;
|
|
|
|
|
|
|
|
return (ioctl(sock, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd));
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
DECL_CMD_FUNC(peerlist, val, d)
|
|
|
|
{
|
|
|
|
size_t size, peercount;
|
|
|
|
void *packed;
|
|
|
|
const nvlist_t *nvl, *nvl_peer;
|
|
|
|
const nvlist_t *const *nvl_peerlist;
|
|
|
|
|
|
|
|
if (get_nvl_out_size(s, WGC_GET, &size))
|
|
|
|
errx(1, "can't get peer list size");
|
|
|
|
if ((packed = malloc(size)) == NULL)
|
|
|
|
errx(1, "malloc failed for peer list");
|
|
|
|
if (do_cmd(s, WGC_GET, packed, size, 0))
|
|
|
|
errx(1, "failed to obtain peer list");
|
|
|
|
|
|
|
|
nvl = nvlist_unpack(packed, size, 0);
|
|
|
|
if (!nvlist_exists_nvlist_array(nvl, "peer-list"))
|
|
|
|
return;
|
|
|
|
nvl_peerlist = nvlist_get_nvlist_array(nvl, "peer-list", &peercount);
|
|
|
|
|
|
|
|
for (int i = 0; i < peercount; i++, nvl_peerlist++) {
|
|
|
|
nvl_peer = *nvl_peerlist;
|
|
|
|
dump_peer(nvl_peer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
peerfinish(int s, void *arg)
|
|
|
|
{
|
|
|
|
nvlist_t *nvl, **nvl_array;
|
|
|
|
void *packed;
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
if ((nvl = nvlist_create(0)) == NULL)
|
|
|
|
errx(1, "failed to allocate nvlist");
|
|
|
|
if ((nvl_array = calloc(sizeof(void *), 1)) == NULL)
|
|
|
|
errx(1, "failed to allocate nvl_array");
|
|
|
|
if (!nvlist_exists_binary(nvl_params, "public-key"))
|
|
|
|
errx(1, "must specify a public-key for adding peer");
|
|
|
|
if (!nvlist_exists_binary(nvl_params, "endpoint"))
|
|
|
|
errx(1, "must specify an endpoint for adding peer");
|
|
|
|
if (allowed_ips_count == 0)
|
|
|
|
errx(1, "must specify at least one range of allowed-ips to add a peer");
|
|
|
|
|
|
|
|
nvl_array[0] = nvl_params;
|
|
|
|
nvlist_add_nvlist_array(nvl, "peer-list", (const nvlist_t * const *)nvl_array, 1);
|
|
|
|
packed = nvlist_pack(nvl, &size);
|
|
|
|
|
|
|
|
if (do_cmd(s, WGC_SET, packed, size, true))
|
|
|
|
errx(1, "failed to install peer");
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
DECL_CMD_FUNC(peerstart, val, d)
|
|
|
|
{
|
|
|
|
do_peer = true;
|
|
|
|
callback_register(peerfinish, NULL);
|
|
|
|
allowed_ips = malloc(ALLOWEDIPS_START * sizeof(struct allowedip));
|
|
|
|
allowed_ips_max = ALLOWEDIPS_START;
|
|
|
|
if (allowed_ips == NULL)
|
|
|
|
errx(1, "failed to allocate array for allowedips");
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
DECL_CMD_FUNC(setwglistenport, val, d)
|
|
|
|
{
|
|
|
|
struct addrinfo hints, *res;
|
|
|
|
const struct sockaddr_in *sin;
|
|
|
|
const struct sockaddr_in6 *sin6;
|
|
|
|
|
|
|
|
u_long ul;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
bzero(&hints, sizeof(hints));
|
|
|
|
hints.ai_family = AF_UNSPEC;
|
|
|
|
hints.ai_flags = AI_NUMERICHOST;
|
|
|
|
err = getaddrinfo(NULL, val, &hints, &res);
|
|
|
|
if (err)
|
|
|
|
errx(1, "%s", gai_strerror(err));
|
|
|
|
|
|
|
|
if (res->ai_family == AF_INET) {
|
|
|
|
sin = (struct sockaddr_in *)res->ai_addr;
|
|
|
|
ul = sin->sin_port;
|
|
|
|
} else if (res->ai_family == AF_INET6) {
|
|
|
|
sin6 = (struct sockaddr_in6 *)res->ai_addr;
|
|
|
|
ul = sin6->sin6_port;
|
|
|
|
} else {
|
|
|
|
errx(1, "unknown family");
|
|
|
|
}
|
|
|
|
ul = ntohs((u_short)ul);
|
|
|
|
nvlist_add_number(nvl_params, "listen-port", ul);
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
DECL_CMD_FUNC(setwgprivkey, val, d)
|
|
|
|
{
|
|
|
|
uint8_t key[WG_KEY_LEN];
|
|
|
|
|
|
|
|
if (!key_from_base64(key, val))
|
|
|
|
errx(1, "invalid key %s", val);
|
|
|
|
nvlist_add_binary(nvl_params, "private-key", key, WG_KEY_LEN);
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
DECL_CMD_FUNC(setwgpubkey, val, d)
|
|
|
|
{
|
|
|
|
uint8_t key[WG_KEY_LEN];
|
|
|
|
|
|
|
|
if (!do_peer)
|
|
|
|
errx(1, "setting public key only valid when adding peer");
|
|
|
|
|
|
|
|
if (!key_from_base64(key, val))
|
|
|
|
errx(1, "invalid key %s", val);
|
|
|
|
nvlist_add_binary(nvl_params, "public-key", key, WG_KEY_LEN);
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
DECL_CMD_FUNC(setallowedips, val, d)
|
|
|
|
{
|
|
|
|
char *base, *allowedip, *mask;
|
|
|
|
u_long ul;
|
|
|
|
char *endp;
|
|
|
|
struct allowedip *aip;
|
|
|
|
|
|
|
|
if (!do_peer)
|
|
|
|
errx(1, "setting allowed ip only valid when adding peer");
|
|
|
|
if (allowed_ips_count == allowed_ips_max) {
|
|
|
|
/* XXX grow array */
|
|
|
|
}
|
|
|
|
aip = &allowed_ips[allowed_ips_count];
|
|
|
|
base = allowedip = strdup(val);
|
|
|
|
mask = index(allowedip, '/');
|
|
|
|
if (mask == NULL)
|
|
|
|
errx(1, "mask separator not found in allowedip %s", val);
|
|
|
|
*mask = '\0';
|
|
|
|
mask++;
|
|
|
|
parse_ip(aip, allowedip);
|
|
|
|
ul = strtoul(mask, &endp, 0);
|
|
|
|
if (*endp != '\0')
|
|
|
|
errx(1, "invalid value for allowedip mask");
|
|
|
|
bzero(&aip->a_mask, sizeof(aip->a_mask));
|
|
|
|
if (aip->a_addr.ss_family == AF_INET)
|
|
|
|
in_len2mask((struct in_addr *)&((struct sockaddr *)&aip->a_mask)->sa_data, ul);
|
|
|
|
else if (aip->a_addr.ss_family == AF_INET6)
|
|
|
|
in6_prefixlen2mask((struct in6_addr *)&((struct sockaddr *)&aip->a_mask)->sa_data, ul);
|
|
|
|
else
|
|
|
|
errx(1, "invalid address family %d\n", aip->a_addr.ss_family);
|
|
|
|
allowed_ips_count++;
|
|
|
|
if (allowed_ips_count > 1)
|
|
|
|
nvlist_free_binary(nvl_params, "allowed-ips");
|
|
|
|
nvlist_add_binary(nvl_params, "allowed-ips", allowed_ips,
|
|
|
|
allowed_ips_count*sizeof(*aip));
|
|
|
|
|
|
|
|
dump_peer(nvl_params);
|
|
|
|
free(base);
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
DECL_CMD_FUNC(setendpoint, val, d)
|
|
|
|
{
|
|
|
|
if (!do_peer)
|
|
|
|
errx(1, "setting endpoint only valid when adding peer");
|
|
|
|
parse_endpoint(val);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
wireguard_status(int s)
|
|
|
|
{
|
|
|
|
size_t size;
|
|
|
|
void *packed;
|
|
|
|
nvlist_t *nvl;
|
|
|
|
char buf[WG_KEY_LEN_BASE64];
|
|
|
|
const void *key;
|
|
|
|
uint16_t listen_port;
|
|
|
|
|
|
|
|
if (get_nvl_out_size(s, WGC_GET, &size))
|
|
|
|
return;
|
|
|
|
if ((packed = malloc(size)) == NULL)
|
|
|
|
return;
|
|
|
|
if (do_cmd(s, WGC_GET, packed, size, 0))
|
|
|
|
return;
|
|
|
|
nvl = nvlist_unpack(packed, size, 0);
|
|
|
|
if (nvlist_exists_number(nvl, "listen-port")) {
|
|
|
|
listen_port = nvlist_get_number(nvl, "listen-port");
|
|
|
|
printf("\tlisten-port: %d\n", listen_port);
|
|
|
|
}
|
|
|
|
if (nvlist_exists_binary(nvl, "private-key")) {
|
|
|
|
key = nvlist_get_binary(nvl, "private-key", &size);
|
|
|
|
b64_ntop((const uint8_t *)key, size, buf, WG_MAX_STRLEN);
|
|
|
|
printf("\tprivate-key: %s\n", buf);
|
|
|
|
}
|
|
|
|
if (nvlist_exists_binary(nvl, "public-key")) {
|
|
|
|
key = nvlist_get_binary(nvl, "public-key", &size);
|
|
|
|
b64_ntop((const uint8_t *)key, size, buf, WG_MAX_STRLEN);
|
|
|
|
printf("\tpublic-key: %s\n", buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd wireguard_cmds[] = {
|
|
|
|
DEF_CLONE_CMD_ARG("listen-port", setwglistenport),
|
|
|
|
DEF_CLONE_CMD_ARG("private-key", setwgprivkey),
|
|
|
|
DEF_CMD("peer-list", 0, peerlist),
|
|
|
|
DEF_CMD("peer", 0, peerstart),
|
|
|
|
DEF_CMD_ARG("public-key", setwgpubkey),
|
|
|
|
DEF_CMD_ARG("allowed-ips", setallowedips),
|
|
|
|
DEF_CMD_ARG("endpoint", setendpoint),
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct afswtch af_wireguard = {
|
|
|
|
.af_name = "af_wireguard",
|
|
|
|
.af_af = AF_UNSPEC,
|
|
|
|
.af_other_status = wireguard_status,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
wg_create(int s, struct ifreq *ifr)
|
|
|
|
{
|
|
|
|
struct iovec iov;
|
|
|
|
void *packed;
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
setproctitle("ifconfig %s create ...\n", name);
|
|
|
|
if (!nvlist_exists_number(nvl_params, "listen-port"))
|
|
|
|
goto legacy;
|
|
|
|
if (!nvlist_exists_binary(nvl_params, "private-key"))
|
|
|
|
goto legacy;
|
|
|
|
|
|
|
|
packed = nvlist_pack(nvl_params, &size);
|
|
|
|
if (packed == NULL)
|
|
|
|
errx(1, "failed to setup create request");
|
|
|
|
iov.iov_len = size;
|
|
|
|
iov.iov_base = packed;
|
|
|
|
ifr->ifr_data = (caddr_t)&iov;
|
|
|
|
if (ioctl(s, SIOCIFCREATE2, ifr) < 0)
|
|
|
|
err(1, "SIOCIFCREATE2");
|
|
|
|
return;
|
|
|
|
legacy:
|
|
|
|
ifr->ifr_data == NULL;
|
|
|
|
if (ioctl(s, SIOCIFCREATE, ifr) < 0)
|
|
|
|
err(1, "SIOCIFCREATE");
|
|
|
|
}
|
|
|
|
|
|
|
|
static __constructor void
|
|
|
|
wireguard_ctor(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
nvl_params = nvlist_create(0);
|
|
|
|
for (i = 0; i < nitems(wireguard_cmds); i++)
|
|
|
|
cmd_register(&wireguard_cmds[i]);
|
|
|
|
af_register(&af_wireguard);
|
|
|
|
clone_setdefcallback_prefix("wg", wg_create);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|