feat: add special handshake support

This commit is contained in:
Mark Puha 2025-06-13 17:51:33 +02:00
parent 71219488c1
commit 3be9c02b19
11 changed files with 1576 additions and 1011 deletions

View file

@ -11,7 +11,7 @@ while read -r -d $'\t' device; do
if [[ $device != "$last_device" ]]; then if [[ $device != "$last_device" ]]; then
[[ -z $last_device ]] && printf '\n' || printf '%s,\n' "$end" [[ -z $last_device ]] && printf '\n' || printf '%s,\n' "$end"
last_device="$device" last_device="$device"
read -r private_key public_key listen_port jc jmin jmax s1 s2 h1 h2 h3 h4 fwmark read -r private_key public_key listen_port jc jmin jmax s1 s2 h1 h2 h3 h4 i1 i2 i3 i4 i5 j1 j2 j3 itime fwmark
printf '\t"%s": {' "$device" printf '\t"%s": {' "$device"
delim=$'\n' delim=$'\n'
[[ $private_key == "(none)" ]] || { printf '%s\t\t"privateKey": "%s"' "$delim" "$private_key"; delim=$',\n'; } [[ $private_key == "(none)" ]] || { printf '%s\t\t"privateKey": "%s"' "$delim" "$private_key"; delim=$',\n'; }
@ -26,6 +26,15 @@ while read -r -d $'\t' device; do
[[ $h2 == "2" ]] || { printf '%s\t\t"h2": %u' "$delim" $(( $h2 )); delim=$',\n'; } [[ $h2 == "2" ]] || { printf '%s\t\t"h2": %u' "$delim" $(( $h2 )); delim=$',\n'; }
[[ $h3 == "3" ]] || { printf '%s\t\t"h3": %u' "$delim" $(( $h3 )); delim=$',\n'; } [[ $h3 == "3" ]] || { printf '%s\t\t"h3": %u' "$delim" $(( $h3 )); delim=$',\n'; }
[[ $h4 == "4" ]] || { printf '%s\t\t"h4": %u' "$delim" $(( $h4 )); delim=$',\n'; } [[ $h4 == "4" ]] || { printf '%s\t\t"h4": %u' "$delim" $(( $h4 )); delim=$',\n'; }
[[ $i1 == "(none)" ]] || { printf '%s\t\t"i1": "%s"' "$delim" "$i1"; delim=$',\n'; }
[[ $i2 == "(none)" ]] || { printf '%s\t\t"i2": "%s"' "$delim" "$i2"; delim=$',\n'; }
[[ $i3 == "(none)" ]] || { printf '%s\t\t"i3": "%s"' "$delim" "$i3"; delim=$',\n'; }
[[ $i4 == "(none)" ]] || { printf '%s\t\t"i4": "%s"' "$delim" "$i4"; delim=$',\n'; }
[[ $i5 == "(none)" ]] || { printf '%s\t\t"i5": "%s"' "$delim" "$i5"; delim=$',\n'; }
[[ $j1 == "(none)" ]] || { printf '%s\t\t"j1": "%s"' "$delim" "$j1"; delim=$',\n'; }
[[ $j2 == "(none)" ]] || { printf '%s\t\t"j2": "%s"' "$delim" "$j2"; delim=$',\n'; }
[[ $j3 == "(none)" ]] || { printf '%s\t\t"j3": "%s"' "$delim" "$j3"; delim=$',\n'; }
[[ $itime == "0" ]] || { printf '%s\t\t"itime": %u' "$delim" $(( itime )); delim=$',\n'; }
[[ $fwmark == "off" ]] || { printf '%s\t\t"fwmark": %u' "$delim" $(( $fwmark )); delim=$',\n'; } [[ $fwmark == "off" ]] || { printf '%s\t\t"fwmark": %u' "$delim" $(( $fwmark )); delim=$',\n'; }
printf '%s\t\t"peers": {' "$delim"; end=$'\n\t\t}\n\t}' printf '%s\t\t"peers": {' "$delim"; end=$'\n\t\t}\n\t}'
delim=$'\n' delim=$'\n'

View file

@ -607,7 +607,7 @@ static bool process_line(struct config_ctx *ctx, const char *line)
if (ret) if (ret)
ctx->device->flags |= WGDEVICE_HAS_J3; ctx->device->flags |= WGDEVICE_HAS_J3;
} else if (key_match("ITIME")) { } else if (key_match("ITIME")) {
ret = parse_string(&ctx->device->itime, "Itime", value); ret = parse_uint32(&ctx->device->itime, "Itime", value);
if (ret) if (ret)
ctx->device->flags |= WGDEVICE_HAS_ITIME; ctx->device->flags |= WGDEVICE_HAS_ITIME;
} else } else
@ -872,7 +872,7 @@ struct wgdevice *config_read_cmd(const char *argv[], int argc)
argv += 2; argv += 2;
argc -=2; argc -=2;
} else if (!strcmp(argv[0], "itime") && argc >= 2 && !peer) { } else if (!strcmp(argv[0], "itime") && argc >= 2 && !peer) {
if (!parse_string(&device->itime, "itime", argv[1])) if (!parse_uint32(&device->itime, "itime", argv[1]))
goto error; goto error;
device->flags |= WGDEVICE_HAS_ITIME; device->flags |= WGDEVICE_HAS_ITIME;

View file

@ -139,7 +139,7 @@ struct wgdevice
char* j1; char* j1;
char* j2; char* j2;
char* j3; char* j3;
char* itime; uint32_t itime;
}; };
#define for_each_wgpeer(__dev, __peer) \ #define for_each_wgpeer(__dev, __peer) \

View file

@ -5,438 +5,606 @@
*/ */
#include <assert.h> #include <assert.h>
#include <dev/wg/if_wg.h>
#include <sys/nv.h> #include <sys/nv.h>
#include <sys/sockio.h> #include <sys/sockio.h>
#include <dev/wg/if_wg.h>
#define IPC_SUPPORTS_KERNEL_INTERFACE #define IPC_SUPPORTS_KERNEL_INTERFACE
static int get_dgram_socket(void) static int get_dgram_socket(void)
{ {
static int sock = -1; static int sock = -1;
if (sock < 0) if (sock < 0)
sock = socket(AF_INET, SOCK_DGRAM, 0); sock = socket(AF_INET, SOCK_DGRAM, 0);
return sock; return sock;
} }
static int kernel_get_wireguard_interfaces(struct string_list *list) static int kernel_get_wireguard_interfaces(struct string_list* list)
{ {
struct ifgroupreq ifgr = { .ifgr_name = "wg" }; struct ifgroupreq ifgr = {.ifgr_name = "wg"};
struct ifg_req *ifg; struct ifg_req* ifg;
int s = get_dgram_socket(), ret = 0; int s = get_dgram_socket(), ret = 0;
if (s < 0) if (s < 0)
return -errno; return -errno;
if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0) if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0)
return errno == ENOENT ? 0 : -errno; return errno == ENOENT ? 0 : -errno;
ifgr.ifgr_groups = calloc(1, ifgr.ifgr_len); ifgr.ifgr_groups = calloc(1, ifgr.ifgr_len);
if (!ifgr.ifgr_groups) if (!ifgr.ifgr_groups)
return -errno; return -errno;
if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0) { if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0)
ret = -errno; {
goto out; ret = -errno;
} goto out;
}
for (ifg = ifgr.ifgr_groups; ifg && ifgr.ifgr_len > 0; ++ifg) { for (ifg = ifgr.ifgr_groups; ifg && ifgr.ifgr_len > 0; ++ifg)
if ((ret = string_list_add(list, ifg->ifgrq_member)) < 0) {
goto out; if ((ret = string_list_add(list, ifg->ifgrq_member)) < 0)
ifgr.ifgr_len -= sizeof(struct ifg_req); goto out;
} ifgr.ifgr_len -= sizeof(struct ifg_req);
}
out: out:
free(ifgr.ifgr_groups); free(ifgr.ifgr_groups);
return ret; return ret;
} }
static int kernel_get_device(struct wgdevice **device, const char *ifname) static int kernel_get_device(struct wgdevice** device, const char* ifname)
{ {
struct wg_data_io wgd = { 0 }; struct wg_data_io wgd = {0};
nvlist_t *nvl_device = NULL; nvlist_t* nvl_device = NULL;
const nvlist_t *const *nvl_peers; const nvlist_t* const* nvl_peers;
struct wgdevice *dev = NULL; struct wgdevice* dev = NULL;
size_t size, peer_count, i; size_t size, peer_count, i;
uint64_t number; uint64_t number;
const void *binary; const void* binary;
int ret = 0, s; int ret = 0, s;
*device = NULL; *device = NULL;
s = get_dgram_socket(); s = get_dgram_socket();
if (s < 0) if (s < 0)
goto err; goto err;
strlcpy(wgd.wgd_name, ifname, sizeof(wgd.wgd_name)); strlcpy(wgd.wgd_name, ifname, sizeof(wgd.wgd_name));
if (ioctl(s, SIOCGWG, &wgd) < 0) if (ioctl(s, SIOCGWG, &wgd) < 0)
goto err; goto err;
wgd.wgd_data = malloc(wgd.wgd_size); wgd.wgd_data = malloc(wgd.wgd_size);
if (!wgd.wgd_data) if (!wgd.wgd_data)
goto err; goto err;
if (ioctl(s, SIOCGWG, &wgd) < 0) if (ioctl(s, SIOCGWG, &wgd) < 0)
goto err; goto err;
dev = calloc(1, sizeof(*dev)); dev = calloc(1, sizeof(*dev));
if (!dev) if (!dev)
goto err; goto err;
strlcpy(dev->name, ifname, sizeof(dev->name)); strlcpy(dev->name, ifname, sizeof(dev->name));
nvl_device = nvlist_unpack(wgd.wgd_data, wgd.wgd_size, 0); nvl_device = nvlist_unpack(wgd.wgd_data, wgd.wgd_size, 0);
if (!nvl_device) if (!nvl_device)
goto err; goto err;
if (nvlist_exists_number(nvl_device, "listen-port")) { if (nvlist_exists_number(nvl_device, "listen-port"))
number = nvlist_get_number(nvl_device, "listen-port"); {
if (number <= UINT16_MAX) { number = nvlist_get_number(nvl_device, "listen-port");
dev->listen_port = number; if (number <= UINT16_MAX)
dev->flags |= WGDEVICE_HAS_LISTEN_PORT; {
} dev->listen_port = number;
} dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
if (nvlist_exists_number(nvl_device, "jc")) { }
number = nvlist_get_number(nvl_device, "jc"); }
if (number <= UINT16_MAX){ if (nvlist_exists_number(nvl_device, "jc"))
dev->junk_packet_count = number; {
dev->flags |= WGDEVICE_HAS_JC; number = nvlist_get_number(nvl_device, "jc");
} if (number <= UINT16_MAX)
} {
if (nvlist_exists_number(nvl_device, "jmin")) { dev->junk_packet_count = number;
number = nvlist_get_number(nvl_device, "jmin"); dev->flags |= WGDEVICE_HAS_JC;
if (number <= UINT16_MAX){ }
dev->junk_packet_min_size = number; }
dev->flags |= WGDEVICE_HAS_JMIN; if (nvlist_exists_number(nvl_device, "jmin"))
} {
} number = nvlist_get_number(nvl_device, "jmin");
if (nvlist_exists_number(nvl_device, "jmax")) { if (number <= UINT16_MAX)
number = nvlist_get_number(nvl_device, "jmax"); {
if (number <= UINT16_MAX){ dev->junk_packet_min_size = number;
dev->junk_packet_max_size = number; dev->flags |= WGDEVICE_HAS_JMIN;
dev->flags |= WGDEVICE_HAS_JMAX; }
} }
} if (nvlist_exists_number(nvl_device, "jmax"))
if (nvlist_exists_number(nvl_device, "s1")) { {
number = nvlist_get_number(nvl_device, "s1"); number = nvlist_get_number(nvl_device, "jmax");
if (number <= UINT16_MAX){ if (number <= UINT16_MAX)
dev->init_packet_junk_size = number; {
dev->flags |= WGDEVICE_HAS_S1; dev->junk_packet_max_size = number;
} dev->flags |= WGDEVICE_HAS_JMAX;
} }
if (nvlist_exists_number(nvl_device, "s2")) { }
number = nvlist_get_number(nvl_device, "s2"); if (nvlist_exists_number(nvl_device, "s1"))
if (number <= UINT16_MAX){ {
dev->response_packet_junk_size = number; number = nvlist_get_number(nvl_device, "s1");
dev->flags |= WGDEVICE_HAS_S2; if (number <= UINT16_MAX)
} {
} dev->init_packet_junk_size = number;
if (nvlist_exists_number(nvl_device, "h1")) { dev->flags |= WGDEVICE_HAS_S1;
number = nvlist_get_number(nvl_device, "h1"); }
if (number <= UINT32_MAX){ }
dev->init_packet_magic_header = number; if (nvlist_exists_number(nvl_device, "s2"))
dev->flags |= WGDEVICE_HAS_H1; {
} number = nvlist_get_number(nvl_device, "s2");
} if (number <= UINT16_MAX)
if (nvlist_exists_number(nvl_device, "h2")) { {
number = nvlist_get_number(nvl_device, "h2"); dev->response_packet_junk_size = number;
if (number <= UINT32_MAX){ dev->flags |= WGDEVICE_HAS_S2;
dev->response_packet_magic_header = number; }
dev->flags |= WGDEVICE_HAS_H2; }
} if (nvlist_exists_number(nvl_device, "h1"))
} {
if (nvlist_exists_number(nvl_device, "h3")) { number = nvlist_get_number(nvl_device, "h1");
number = nvlist_get_number(nvl_device, "h3"); if (number <= UINT32_MAX)
if (number <= UINT32_MAX){ {
dev->underload_packet_magic_header = number; dev->init_packet_magic_header = number;
dev->flags |= WGDEVICE_HAS_H3; dev->flags |= WGDEVICE_HAS_H1;
} }
} }
if (nvlist_exists_number(nvl_device, "h4")) { if (nvlist_exists_number(nvl_device, "h2"))
number = nvlist_get_number(nvl_device, "h4"); {
if (number <= UINT32_MAX){ number = nvlist_get_number(nvl_device, "h2");
dev->transport_packet_magic_header = number; if (number <= UINT32_MAX)
dev->flags |= WGDEVICE_HAS_H4; {
} dev->response_packet_magic_header = number;
} dev->flags |= WGDEVICE_HAS_H2;
}
}
if (nvlist_exists_number(nvl_device, "h3"))
{
number = nvlist_get_number(nvl_device, "h3");
if (number <= UINT32_MAX)
{
dev->underload_packet_magic_header = number;
dev->flags |= WGDEVICE_HAS_H3;
}
}
if (nvlist_exists_number(nvl_device, "h4"))
{
number = nvlist_get_number(nvl_device, "h4");
if (number <= UINT32_MAX)
{
dev->transport_packet_magic_header = number;
dev->flags |= WGDEVICE_HAS_H4;
}
}
if (nvlist_exists_binary(nvl_device, "i1"))
{
binary = nvlist_get_binary(nvl_device, "i1", &size);
if (binary && size < MAX_AWG_JUNK_LEN)
{
dev->i1 = strdup((const char*)binary);
dev->flags |= WGDEVICE_HAS_I1;
}
}
if (nvlist_exists_binary(nvl_device, "i2"))
{
binary = nvlist_get_binary(nvl_device, "i2", &size);
if (binary && size < MAX_AWG_JUNK_LEN)
{
dev->i2 = strdup((const char*)binary);
dev->flags |= WGDEVICE_HAS_I2;
}
}
if (nvlist_exists_binary(nvl_device, "i3"))
{
binary = nvlist_get_binary(nvl_device, "i3", &size);
if (binary && size < MAX_AWG_JUNK_LEN)
{
dev->i3 = strdup((const char*)binary);
dev->flags |= WGDEVICE_HAS_I3;
}
}
if (nvlist_exists_binary(nvl_device, "i4"))
{
binary = nvlist_get_binary(nvl_device, "i4", &size);
if (binary && size < MAX_AWG_JUNK_LEN)
{
dev->i4 = strdup((const char*)binary);
dev->flags |= WGDEVICE_HAS_I4;
}
}
if (nvlist_exists_binary(nvl_device, "i5"))
{
binary = nvlist_get_binary(nvl_device, "i5", &size);
if (binary && size < MAX_AWG_JUNK_LEN)
{
dev->i5 = strdup((const char*)binary);
dev->flags |= WGDEVICE_HAS_I5;
}
}
if (nvlist_exists_binary(nvl_device, "j1"))
{
binary = nvlist_get_binary(nvl_device, "j1", &size);
if (binary && size < MAX_AWG_JUNK_LEN)
{
dev->j1 = strdup((const char*)binary);
dev->flags |= WGDEVICE_HAS_J1;
}
}
if (nvlist_exists_binary(nvl_device, "j2"))
{
binary = nvlist_get_binary(nvl_device, "j2", &size);
if (binary && size < MAX_AWG_JUNK_LEN)
{
dev->j2 = strdup((const char*)binary);
dev->flags |= WGDEVICE_HAS_J2;
}
}
if (nvlist_exists_binary(nvl_device, "j3"))
{
binary = nvlist_get_binary(nvl_device, "j3", &size);
if (binary && size < MAX_AWG_JUNK_LEN)
{
dev->j3 = strdup((const char*)binary);
dev->flags |= WGDEVICE_HAS_J3;
}
}
if (nvlist_exists_binary(nvl_device, "itime"))
{
number = nvlist_get_number(nvl_device, "itime");
if (number <= INT32_MAX)
{
dev->itime = number;
dev->flags |= WGDEVICE_HAS_ITIME;
}
}
if (nvlist_exists_number(nvl_device, "user-cookie")) { if (nvlist_exists_number(nvl_device, "user-cookie"))
number = nvlist_get_number(nvl_device, "user-cookie"); {
if (number <= UINT32_MAX) { number = nvlist_get_number(nvl_device, "user-cookie");
dev->fwmark = number; if (number <= UINT32_MAX)
dev->flags |= WGDEVICE_HAS_FWMARK; {
} dev->fwmark = number;
} dev->flags |= WGDEVICE_HAS_FWMARK;
if (nvlist_exists_binary(nvl_device, "public-key")) { }
binary = nvlist_get_binary(nvl_device, "public-key", &size); }
if (binary && size == sizeof(dev->public_key)) { if (nvlist_exists_binary(nvl_device, "public-key"))
memcpy(dev->public_key, binary, sizeof(dev->public_key)); {
dev->flags |= WGDEVICE_HAS_PUBLIC_KEY; binary = nvlist_get_binary(nvl_device, "public-key", &size);
} if (binary && size == sizeof(dev->public_key))
} {
if (nvlist_exists_binary(nvl_device, "private-key")) { memcpy(dev->public_key, binary, sizeof(dev->public_key));
binary = nvlist_get_binary(nvl_device, "private-key", &size); dev->flags |= WGDEVICE_HAS_PUBLIC_KEY;
if (binary && size == sizeof(dev->private_key)) { }
memcpy(dev->private_key, binary, sizeof(dev->private_key)); }
dev->flags |= WGDEVICE_HAS_PRIVATE_KEY; if (nvlist_exists_binary(nvl_device, "private-key"))
} {
} binary = nvlist_get_binary(nvl_device, "private-key", &size);
if (!nvlist_exists_nvlist_array(nvl_device, "peers")) if (binary && size == sizeof(dev->private_key))
goto skip_peers; {
nvl_peers = nvlist_get_nvlist_array(nvl_device, "peers", &peer_count); memcpy(dev->private_key, binary, sizeof(dev->private_key));
if (!nvl_peers) dev->flags |= WGDEVICE_HAS_PRIVATE_KEY;
goto skip_peers; }
for (i = 0; i < peer_count; ++i) { }
struct wgpeer *peer; if (!nvlist_exists_nvlist_array(nvl_device, "peers"))
struct wgallowedip *aip = NULL; goto skip_peers;
const nvlist_t *const *nvl_aips; nvl_peers = nvlist_get_nvlist_array(nvl_device, "peers", &peer_count);
size_t aip_count, j; if (!nvl_peers)
goto skip_peers;
for (i = 0; i < peer_count; ++i)
{
struct wgpeer* peer;
struct wgallowedip* aip = NULL;
const nvlist_t* const* nvl_aips;
size_t aip_count, j;
peer = calloc(1, sizeof(*peer)); peer = calloc(1, sizeof(*peer));
if (!peer) if (!peer)
goto err_peer; goto err_peer;
if (nvlist_exists_binary(nvl_peers[i], "public-key")) { if (nvlist_exists_binary(nvl_peers[i], "public-key"))
binary = nvlist_get_binary(nvl_peers[i], "public-key", &size); {
if (binary && size == sizeof(peer->public_key)) { binary = nvlist_get_binary(nvl_peers[i], "public-key", &size);
memcpy(peer->public_key, binary, sizeof(peer->public_key)); if (binary && size == sizeof(peer->public_key))
peer->flags |= WGPEER_HAS_PUBLIC_KEY; {
} memcpy(peer->public_key, binary, sizeof(peer->public_key));
} peer->flags |= WGPEER_HAS_PUBLIC_KEY;
if (nvlist_exists_binary(nvl_peers[i], "preshared-key")) { }
binary = nvlist_get_binary(nvl_peers[i], "preshared-key", &size); }
if (binary && size == sizeof(peer->preshared_key)) { if (nvlist_exists_binary(nvl_peers[i], "preshared-key"))
memcpy(peer->preshared_key, binary, sizeof(peer->preshared_key)); {
if (!key_is_zero(peer->preshared_key)) binary = nvlist_get_binary(nvl_peers[i], "preshared-key", &size);
peer->flags |= WGPEER_HAS_PRESHARED_KEY; if (binary && size == sizeof(peer->preshared_key))
} {
} memcpy(peer->preshared_key, binary, sizeof(peer->preshared_key));
if (nvlist_exists_number(nvl_peers[i], "persistent-keepalive-interval")) { if (!key_is_zero(peer->preshared_key))
number = nvlist_get_number(nvl_peers[i], "persistent-keepalive-interval"); peer->flags |= WGPEER_HAS_PRESHARED_KEY;
if (number <= UINT16_MAX) { }
peer->persistent_keepalive_interval = number; }
peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL; if (nvlist_exists_number(nvl_peers[i], "persistent-keepalive-interval"))
} {
} number = nvlist_get_number(nvl_peers[i], "persistent-keepalive-interval");
if (nvlist_exists_binary(nvl_peers[i], "endpoint")) { if (number <= UINT16_MAX)
const struct sockaddr *endpoint = nvlist_get_binary(nvl_peers[i], "endpoint", &size); {
if (endpoint && size <= sizeof(peer->endpoint) && size >= sizeof(peer->endpoint.addr) && peer->persistent_keepalive_interval = number;
(endpoint->sa_family == AF_INET || endpoint->sa_family == AF_INET6)) peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
memcpy(&peer->endpoint.addr, endpoint, size); }
} }
if (nvlist_exists_number(nvl_peers[i], "rx-bytes")) if (nvlist_exists_binary(nvl_peers[i], "endpoint"))
peer->rx_bytes = nvlist_get_number(nvl_peers[i], "rx-bytes"); {
if (nvlist_exists_number(nvl_peers[i], "tx-bytes")) const struct sockaddr* endpoint =
peer->tx_bytes = nvlist_get_number(nvl_peers[i], "tx-bytes"); nvlist_get_binary(nvl_peers[i], "endpoint", &size);
if (nvlist_exists_binary(nvl_peers[i], "last-handshake-time")) { if (endpoint && size <= sizeof(peer->endpoint) &&
binary = nvlist_get_binary(nvl_peers[i], "last-handshake-time", &size); size >= sizeof(peer->endpoint.addr) &&
if (binary && size == sizeof(peer->last_handshake_time)) (endpoint->sa_family == AF_INET || endpoint->sa_family == AF_INET6))
memcpy(&peer->last_handshake_time, binary, sizeof(peer->last_handshake_time)); memcpy(&peer->endpoint.addr, endpoint, size);
} }
if (nvlist_exists_number(nvl_peers[i], "rx-bytes"))
peer->rx_bytes = nvlist_get_number(nvl_peers[i], "rx-bytes");
if (nvlist_exists_number(nvl_peers[i], "tx-bytes"))
peer->tx_bytes = nvlist_get_number(nvl_peers[i], "tx-bytes");
if (nvlist_exists_binary(nvl_peers[i], "last-handshake-time"))
{
binary = nvlist_get_binary(nvl_peers[i], "last-handshake-time", &size);
if (binary && size == sizeof(peer->last_handshake_time))
memcpy(
&peer->last_handshake_time,
binary,
sizeof(peer->last_handshake_time));
}
if (!nvlist_exists_nvlist_array(nvl_peers[i], "allowed-ips")) if (!nvlist_exists_nvlist_array(nvl_peers[i], "allowed-ips"))
goto skip_allowed_ips; goto skip_allowed_ips;
nvl_aips = nvlist_get_nvlist_array(nvl_peers[i], "allowed-ips", &aip_count); nvl_aips = nvlist_get_nvlist_array(nvl_peers[i], "allowed-ips", &aip_count);
if (!aip_count || !nvl_aips) if (!aip_count || !nvl_aips)
goto skip_allowed_ips; goto skip_allowed_ips;
for (j = 0; j < aip_count; ++j) { for (j = 0; j < aip_count; ++j)
if (!nvlist_exists_number(nvl_aips[j], "cidr")) {
continue; if (!nvlist_exists_number(nvl_aips[j], "cidr"))
if (!nvlist_exists_binary(nvl_aips[j], "ipv4") && !nvlist_exists_binary(nvl_aips[j], "ipv6")) continue;
continue; if (!nvlist_exists_binary(nvl_aips[j], "ipv4") &&
aip = calloc(1, sizeof(*aip)); !nvlist_exists_binary(nvl_aips[j], "ipv6"))
if (!aip) continue;
goto err_allowed_ips; aip = calloc(1, sizeof(*aip));
number = nvlist_get_number(nvl_aips[j], "cidr"); if (!aip)
if (nvlist_exists_binary(nvl_aips[j], "ipv4")) { goto err_allowed_ips;
binary = nvlist_get_binary(nvl_aips[j], "ipv4", &size); number = nvlist_get_number(nvl_aips[j], "cidr");
if (!binary || number > 32) { if (nvlist_exists_binary(nvl_aips[j], "ipv4"))
ret = EINVAL; {
goto err_allowed_ips; binary = nvlist_get_binary(nvl_aips[j], "ipv4", &size);
} if (!binary || number > 32)
aip->family = AF_INET; {
aip->cidr = number; ret = EINVAL;
memcpy(&aip->ip4, binary, sizeof(aip->ip4)); goto err_allowed_ips;
} else { }
assert(nvlist_exists_binary(nvl_aips[j], "ipv6")); aip->family = AF_INET;
binary = nvlist_get_binary(nvl_aips[j], "ipv6", &size); aip->cidr = number;
if (!binary || number > 128) { memcpy(&aip->ip4, binary, sizeof(aip->ip4));
ret = EINVAL; }
goto err_allowed_ips; else
} {
aip->family = AF_INET6; assert(nvlist_exists_binary(nvl_aips[j], "ipv6"));
aip->cidr = number; binary = nvlist_get_binary(nvl_aips[j], "ipv6", &size);
memcpy(&aip->ip6, binary, sizeof(aip->ip6)); if (!binary || number > 128)
} {
ret = EINVAL;
goto err_allowed_ips;
}
aip->family = AF_INET6;
aip->cidr = number;
memcpy(&aip->ip6, binary, sizeof(aip->ip6));
}
if (!peer->first_allowedip) if (!peer->first_allowedip)
peer->first_allowedip = aip; peer->first_allowedip = aip;
else else
peer->last_allowedip->next_allowedip = aip; peer->last_allowedip->next_allowedip = aip;
peer->last_allowedip = aip; peer->last_allowedip = aip;
aip = NULL; aip = NULL;
continue; continue;
err_allowed_ips: err_allowed_ips:
if (!ret) if (!ret)
ret = -errno; ret = -errno;
free(aip); free(aip);
goto err_peer; goto err_peer;
} }
/* Nothing leaked, hopefully -- ownership transferred or aip freed. */ /* Nothing leaked, hopefully -- ownership transferred or aip freed. */
assert(aip == NULL); assert(aip == NULL);
skip_allowed_ips: skip_allowed_ips:
if (!dev->first_peer) if (!dev->first_peer)
dev->first_peer = peer; dev->first_peer = peer;
else else
dev->last_peer->next_peer = peer; dev->last_peer->next_peer = peer;
dev->last_peer = peer; dev->last_peer = peer;
continue; continue;
err_peer: err_peer:
if (!ret) if (!ret)
ret = -errno; ret = -errno;
free(peer); free(peer);
goto err; goto err;
} }
skip_peers: skip_peers:
free(wgd.wgd_data); free(wgd.wgd_data);
nvlist_destroy(nvl_device); nvlist_destroy(nvl_device);
*device = dev; *device = dev;
return 0; return 0;
err: err:
if (!ret) if (!ret)
ret = -errno; ret = -errno;
free(wgd.wgd_data); free(wgd.wgd_data);
nvlist_destroy(nvl_device); nvlist_destroy(nvl_device);
free(dev); free(dev);
return ret; return ret;
} }
static int kernel_set_device(struct wgdevice* dev)
static int kernel_set_device(struct wgdevice *dev)
{ {
struct wg_data_io wgd = { 0 }; struct wg_data_io wgd = {0};
nvlist_t *nvl_device = NULL, **nvl_peers = NULL; nvlist_t * nvl_device = NULL, **nvl_peers = NULL;
size_t peer_count = 0, i = 0; size_t peer_count = 0, i = 0;
struct wgpeer *peer; struct wgpeer* peer;
int ret = 0, s; int ret = 0, s;
strlcpy(wgd.wgd_name, dev->name, sizeof(wgd.wgd_name)); strlcpy(wgd.wgd_name, dev->name, sizeof(wgd.wgd_name));
nvl_device = nvlist_create(0); nvl_device = nvlist_create(0);
if (!nvl_device) if (!nvl_device)
goto err; goto err;
for_each_wgpeer(dev, peer) for_each_wgpeer(dev, peer)++ peer_count;
++peer_count; if (peer_count)
if (peer_count) { {
nvl_peers = calloc(peer_count, sizeof(*nvl_peers)); nvl_peers = calloc(peer_count, sizeof(*nvl_peers));
if (!nvl_peers) if (!nvl_peers)
goto err; goto err;
} }
if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)
nvlist_add_binary(nvl_device, "private-key", dev->private_key, sizeof(dev->private_key)); nvlist_add_binary(
if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) nvl_device, "private-key", dev->private_key, sizeof(dev->private_key));
nvlist_add_number(nvl_device, "listen-port", dev->listen_port); if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
if (dev->flags & WGDEVICE_HAS_JC) nvlist_add_number(nvl_device, "listen-port", dev->listen_port);
nvlist_add_number(nvl_device, "jc", dev->junk_packet_count); if (dev->flags & WGDEVICE_HAS_JC)
if (dev->flags & WGDEVICE_HAS_JMIN) nvlist_add_number(nvl_device, "jc", dev->junk_packet_count);
nvlist_add_number(nvl_device, "jmin", dev->junk_packet_min_size); if (dev->flags & WGDEVICE_HAS_JMIN)
if (dev->flags & WGDEVICE_HAS_JMAX) nvlist_add_number(nvl_device, "jmin", dev->junk_packet_min_size);
nvlist_add_number(nvl_device, "jmax", dev->junk_packet_max_size); if (dev->flags & WGDEVICE_HAS_JMAX)
if (dev->flags & WGDEVICE_HAS_S1) nvlist_add_number(nvl_device, "jmax", dev->junk_packet_max_size);
nvlist_add_number(nvl_device, "s1", dev->init_packet_junk_size); if (dev->flags & WGDEVICE_HAS_S1)
if (dev->flags & WGDEVICE_HAS_S2) nvlist_add_number(nvl_device, "s1", dev->init_packet_junk_size);
nvlist_add_number(nvl_device, "s2", dev->response_packet_junk_size); if (dev->flags & WGDEVICE_HAS_S2)
if (dev->flags & WGDEVICE_HAS_H1) nvlist_add_number(nvl_device, "s2", dev->response_packet_junk_size);
nvlist_add_number(nvl_device, "h1", dev->init_packet_magic_header); if (dev->flags & WGDEVICE_HAS_H1)
if (dev->flags & WGDEVICE_HAS_H2) nvlist_add_number(nvl_device, "h1", dev->init_packet_magic_header);
nvlist_add_number(nvl_device, "h2", dev->response_packet_magic_header); if (dev->flags & WGDEVICE_HAS_H2)
if (dev->flags & WGDEVICE_HAS_H3) nvlist_add_number(nvl_device, "h2", dev->response_packet_magic_header);
nvlist_add_number(nvl_device, "h3", dev->underload_packet_magic_header); if (dev->flags & WGDEVICE_HAS_H3)
if (dev->flags & WGDEVICE_HAS_H4) nvlist_add_number(nvl_device, "h3", dev->underload_packet_magic_header);
nvlist_add_number(nvl_device, "h4", dev->transport_packet_magic_header); if (dev->flags & WGDEVICE_HAS_H4)
if (dev->flags & WGDEVICE_HAS_FWMARK) nvlist_add_number(nvl_device, "h4", dev->transport_packet_magic_header);
nvlist_add_number(nvl_device, "user-cookie", dev->fwmark); if (dev->flags & WGDEVICE_HAS_I1)
if (dev->flags & WGDEVICE_REPLACE_PEERS) nvlist_add_binary(nvl_device, "i1", dev->i1, strlen(dev->i1) + 1);
nvlist_add_bool(nvl_device, "replace-peers", true); if (dev->flags & WGDEVICE_HAS_I2)
nvlist_add_binary(nvl_device, "i2", dev->i2, strlen(dev->i2) + 1);
if (dev->flags & WGDEVICE_HAS_I3)
nvlist_add_binary(nvl_device, "i3", dev->i3, strlen(dev->i3) + 1);
if (dev->flags & WGDEVICE_HAS_I4)
nvlist_add_binary(nvl_device, "i4", dev->i4, strlen(dev->i4) + 1);
if (dev->flags & WGDEVICE_HAS_I5)
nvlist_add_binary(nvl_device, "i5", dev->i5, strlen(dev->i5) + 1);
if (dev->flags & WGDEVICE_HAS_J1)
nvlist_add_binary(nvl_device, "j1", dev->j1, strlen(dev->j1) + 1);
if (dev->flags & WGDEVICE_HAS_J2)
nvlist_add_binary(nvl_device, "j2", dev->j2, strlen(dev->j2) + 1);
if (dev->flags & WGDEVICE_HAS_J3)
nvlist_add_binary(nvl_device, "j3", dev->j3, strlen(dev->j3) + 1);
if (dev->flags & WGDEVICE_HAS_ITIME)
nvlist_add_number(nvl_device, "itime", dev->itime);
for_each_wgpeer(dev, peer) { if (dev->flags & WGDEVICE_HAS_FWMARK)
size_t aip_count = 0, j = 0; nvlist_add_number(nvl_device, "user-cookie", dev->fwmark);
nvlist_t **nvl_aips = NULL; if (dev->flags & WGDEVICE_REPLACE_PEERS)
struct wgallowedip *aip; nvlist_add_bool(nvl_device, "replace-peers", true);
nvl_peers[i] = nvlist_create(0); for_each_wgpeer(dev, peer)
if (!nvl_peers[i]) {
goto err_peer; size_t aip_count = 0, j = 0;
for_each_wgallowedip(peer, aip) nvlist_t** nvl_aips = NULL;
++aip_count; struct wgallowedip* aip;
if (aip_count) {
nvl_aips = calloc(aip_count, sizeof(*nvl_aips));
if (!nvl_aips)
goto err_peer;
}
nvlist_add_binary(nvl_peers[i], "public-key", peer->public_key, sizeof(peer->public_key));
if (peer->flags & WGPEER_HAS_PRESHARED_KEY)
nvlist_add_binary(nvl_peers[i], "preshared-key", peer->preshared_key, sizeof(peer->preshared_key));
if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL)
nvlist_add_number(nvl_peers[i], "persistent-keepalive-interval", peer->persistent_keepalive_interval);
if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
nvlist_add_binary(nvl_peers[i], "endpoint", &peer->endpoint.addr, peer->endpoint.addr.sa_len);
if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
nvlist_add_bool(nvl_peers[i], "replace-allowedips", true);
if (peer->flags & WGPEER_REMOVE_ME)
nvlist_add_bool(nvl_peers[i], "remove", true);
for_each_wgallowedip(peer, aip) {
nvl_aips[j] = nvlist_create(0);
if (!nvl_aips[j])
goto err_peer;
nvlist_add_number(nvl_aips[j], "cidr", aip->cidr);
if (aip->family == AF_INET)
nvlist_add_binary(nvl_aips[j], "ipv4", &aip->ip4, sizeof(aip->ip4));
else if (aip->family == AF_INET6)
nvlist_add_binary(nvl_aips[j], "ipv6", &aip->ip6, sizeof(aip->ip6));
++j;
}
if (j) {
nvlist_add_nvlist_array(nvl_peers[i], "allowed-ips", (const nvlist_t *const *)nvl_aips, j);
for (j = 0; j < aip_count; ++j)
nvlist_destroy(nvl_aips[j]);
free(nvl_aips);
}
++i;
continue;
err_peer: nvl_peers[i] = nvlist_create(0);
ret = -errno; if (!nvl_peers[i])
for (j = 0; j < aip_count && nvl_aips; ++j) goto err_peer;
nvlist_destroy(nvl_aips[j]); for_each_wgallowedip(peer, aip)++ aip_count;
free(nvl_aips); if (aip_count)
nvlist_destroy(nvl_peers[i]); {
nvl_peers[i] = NULL; nvl_aips = calloc(aip_count, sizeof(*nvl_aips));
goto err; if (!nvl_aips)
} goto err_peer;
if (i) { }
nvlist_add_nvlist_array(nvl_device, "peers", (const nvlist_t *const *)nvl_peers, i); nvlist_add_binary(
for (i = 0; i < peer_count; ++i) nvl_peers[i], "public-key", peer->public_key, sizeof(peer->public_key));
nvlist_destroy(nvl_peers[i]); if (peer->flags & WGPEER_HAS_PRESHARED_KEY)
free(nvl_peers); nvlist_add_binary(
nvl_peers = NULL; nvl_peers[i],
} "preshared-key",
wgd.wgd_data = nvlist_pack(nvl_device, &wgd.wgd_size); peer->preshared_key,
nvlist_destroy(nvl_device); sizeof(peer->preshared_key));
nvl_device = NULL; if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL)
if (!wgd.wgd_data) nvlist_add_number(
goto err; nvl_peers[i],
s = get_dgram_socket(); "persistent-keepalive-interval",
if (s < 0) peer->persistent_keepalive_interval);
return -errno; if (peer->endpoint.addr.sa_family == AF_INET ||
return ioctl(s, SIOCSWG, &wgd); peer->endpoint.addr.sa_family == AF_INET6)
nvlist_add_binary(
nvl_peers[i],
"endpoint",
&peer->endpoint.addr,
peer->endpoint.addr.sa_len);
if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
nvlist_add_bool(nvl_peers[i], "replace-allowedips", true);
if (peer->flags & WGPEER_REMOVE_ME)
nvlist_add_bool(nvl_peers[i], "remove", true);
for_each_wgallowedip(peer, aip)
{
nvl_aips[j] = nvlist_create(0);
if (!nvl_aips[j])
goto err_peer;
nvlist_add_number(nvl_aips[j], "cidr", aip->cidr);
if (aip->family == AF_INET)
nvlist_add_binary(nvl_aips[j], "ipv4", &aip->ip4, sizeof(aip->ip4));
else if (aip->family == AF_INET6)
nvlist_add_binary(nvl_aips[j], "ipv6", &aip->ip6, sizeof(aip->ip6));
++j;
}
if (j)
{
nvlist_add_nvlist_array(
nvl_peers[i], "allowed-ips", (const nvlist_t* const*)nvl_aips, j);
for (j = 0; j < aip_count; ++j)
nvlist_destroy(nvl_aips[j]);
free(nvl_aips);
}
++i;
continue;
err_peer:
ret = -errno;
for (j = 0; j < aip_count && nvl_aips; ++j)
nvlist_destroy(nvl_aips[j]);
free(nvl_aips);
nvlist_destroy(nvl_peers[i]);
nvl_peers[i] = NULL;
goto err;
}
if (i)
{
nvlist_add_nvlist_array(
nvl_device, "peers", (const nvlist_t* const*)nvl_peers, i);
for (i = 0; i < peer_count; ++i)
nvlist_destroy(nvl_peers[i]);
free(nvl_peers);
nvl_peers = NULL;
}
wgd.wgd_data = nvlist_pack(nvl_device, &wgd.wgd_size);
nvlist_destroy(nvl_device);
nvl_device = NULL;
if (!wgd.wgd_data)
goto err;
s = get_dgram_socket();
if (s < 0)
return -errno;
return ioctl(s, SIOCSWG, &wgd);
err: err:
if (!ret) if (!ret)
ret = -errno; ret = -errno;
for (i = 0; i < peer_count && nvl_peers; ++i) for (i = 0; i < peer_count && nvl_peers; ++i)
nvlist_destroy(nvl_peers[i]); nvlist_destroy(nvl_peers[i]);
free(nvl_peers); free(nvl_peers);
nvlist_destroy(nvl_device); nvlist_destroy(nvl_device);
return ret; return ret;
} }

View file

@ -646,53 +646,70 @@ static int parse_device(const struct nlattr* attr, void* data)
device->transport_packet_magic_header = mnl_attr_get_u32(attr); device->transport_packet_magic_header = mnl_attr_get_u32(attr);
device->flags |= WGDEVICE_HAS_H4; device->flags |= WGDEVICE_HAS_H4;
} }
break;
case WGDEVICE_A_I1: case WGDEVICE_A_I1:
if (!mnl_attr_validate(attr, MNL_TYPE_STRING) { if (!mnl_attr_validate(attr, MNL_TYPE_STRING))
{
device->i1 = strdup(mnl_attr_get_str(attr)); device->i1 = strdup(mnl_attr_get_str(attr));
device->flags |= WGDEVICE_HAS_I1; device->flags |= WGDEVICE_HAS_I1;
} }
case WGDEVICE_A_I2: break;
if (!mnl_attr_validate(attr, MNL_TYPE_STRING) { case WGDEVICE_A_I2:
if (!mnl_attr_validate(attr, MNL_TYPE_STRING))
{
device->i2 = strdup(mnl_attr_get_str(attr)); device->i2 = strdup(mnl_attr_get_str(attr));
device->flags |= WGDEVICE_HAS_I2; device->flags |= WGDEVICE_HAS_I2;
} }
case WGDEVICE_A_I3: break;
if (!mnl_attr_validate(attr, MNL_TYPE_STRING) { case WGDEVICE_A_I3:
if (!mnl_attr_validate(attr, MNL_TYPE_STRING))
{
device->i3 = strdup(mnl_attr_get_str(attr)); device->i3 = strdup(mnl_attr_get_str(attr));
device->flags |= WGDEVICE_HAS_I3; device->flags |= WGDEVICE_HAS_I3;
} }
case WGDEVICE_A_I4: break;
if (!mnl_attr_validate(attr, MNL_TYPE_STRING) { case WGDEVICE_A_I4:
if (!mnl_attr_validate(attr, MNL_TYPE_STRING))
{
device->i4 = strdup(mnl_attr_get_str(attr)); device->i4 = strdup(mnl_attr_get_str(attr));
device->flags |= WGDEVICE_HAS_I4; device->flags |= WGDEVICE_HAS_I4;
} }
case WGDEVICE_A_I5: break;
if (!mnl_attr_validate(attr, MNL_TYPE_STRING) { case WGDEVICE_A_I5:
if (!mnl_attr_validate(attr, MNL_TYPE_STRING))
{
device->i5 = strdup(mnl_attr_get_str(attr)); device->i5 = strdup(mnl_attr_get_str(attr));
device->flags |= WGDEVICE_HAS_I5; device->flags |= WGDEVICE_HAS_I5;
} }
case WGDEVICE_A_J1: break;
if (!mnl_attr_validate(attr, MNL_TYPE_STRING) { case WGDEVICE_A_J1:
if (!mnl_attr_validate(attr, MNL_TYPE_STRING))
{
device->j1 = strdup(mnl_attr_get_str(attr)); device->j1 = strdup(mnl_attr_get_str(attr));
device->flags |= WGDEVICE_HAS_J1; device->flags |= WGDEVICE_HAS_J1;
} }
case WGDEVICE_A_J2: break;
if (!mnl_attr_validate(attr, MNL_TYPE_STRING) { case WGDEVICE_A_J2:
if (!mnl_attr_validate(attr, MNL_TYPE_STRING))
{
device->j2 = strdup(mnl_attr_get_str(attr)); device->j2 = strdup(mnl_attr_get_str(attr));
device->flags |= WGDEVICE_HAS_J2; device->flags |= WGDEVICE_HAS_J2;
} }
case WGDEVICE_A_J3: break;
if (!mnl_attr_validate(attr, MNL_TYPE_STRING) { case WGDEVICE_A_J3:
if (!mnl_attr_validate(attr, MNL_TYPE_STRING))
{
device->j3 = strdup(mnl_attr_get_str(attr)); device->j3 = strdup(mnl_attr_get_str(attr));
device->flags |= WGDEVICE_HAS_J3; device->flags |= WGDEVICE_HAS_J3;
} }
case WGDEVICE_A_ITIME: break;
if (!mnl_attr_validate(attr, MNL_TYPE_STRING) { case WGDEVICE_A_ITIME:
device->itime = strdup(mnl_attr_get_str(attr)); if (!mnl_attr_validate(attr, MNL_TYPE_STRING))
{
device->itime = mnl_attr_get_u32(attr);
device->flags |= WGDEVICE_HAS_ITIME; device->flags |= WGDEVICE_HAS_ITIME;
} }
break; break;
} }
return MNL_CB_OK; return MNL_CB_OK;

View file

@ -3,370 +3,521 @@
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */
#include "containers.h"
#include <errno.h> #include <errno.h>
#include <net/if.h>
#include <net/if_wg.h>
#include <netinet/in.h>
#include <stddef.h> #include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/sockio.h> #include <sys/sockio.h>
#include <sys/types.h> #include <sys/types.h>
#include <net/if.h> #include <time.h>
#include <net/if_wg.h>
#include <netinet/in.h>
#include "containers.h"
#define IPC_SUPPORTS_KERNEL_INTERFACE #define IPC_SUPPORTS_KERNEL_INTERFACE
static int get_dgram_socket(void) static int get_dgram_socket(void)
{ {
static int sock = -1; static int sock = -1;
if (sock < 0) if (sock < 0)
sock = socket(AF_INET, SOCK_DGRAM, 0); sock = socket(AF_INET, SOCK_DGRAM, 0);
return sock; return sock;
} }
static int kernel_get_wireguard_interfaces(struct string_list *list) static int kernel_get_wireguard_interfaces(struct string_list* list)
{ {
struct ifgroupreq ifgr = { .ifgr_name = "wg" }; struct ifgroupreq ifgr = {.ifgr_name = "wg"};
struct ifg_req *ifg; struct ifg_req* ifg;
int s = get_dgram_socket(), ret = 0; int s = get_dgram_socket(), ret = 0;
if (s < 0) if (s < 0)
return -errno; return -errno;
if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0) if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0)
return errno == ENOENT ? 0 : -errno; return errno == ENOENT ? 0 : -errno;
ifgr.ifgr_groups = calloc(1, ifgr.ifgr_len); ifgr.ifgr_groups = calloc(1, ifgr.ifgr_len);
if (!ifgr.ifgr_groups) if (!ifgr.ifgr_groups)
return -errno; return -errno;
if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0) { if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0)
ret = -errno; {
goto out; ret = -errno;
} goto out;
}
for (ifg = ifgr.ifgr_groups; ifg && ifgr.ifgr_len > 0; ++ifg) { for (ifg = ifgr.ifgr_groups; ifg && ifgr.ifgr_len > 0; ++ifg)
if ((ret = string_list_add(list, ifg->ifgrq_member)) < 0) {
goto out; if ((ret = string_list_add(list, ifg->ifgrq_member)) < 0)
ifgr.ifgr_len -= sizeof(struct ifg_req); goto out;
} ifgr.ifgr_len -= sizeof(struct ifg_req);
}
out: out:
free(ifgr.ifgr_groups); free(ifgr.ifgr_groups);
return ret; return ret;
} }
static int kernel_get_device(struct wgdevice **device, const char *iface) static int kernel_get_device(struct wgdevice** device, const char* iface)
{ {
struct wg_data_io wgdata = { .wgd_size = 0 }; struct wg_data_io wgdata = {.wgd_size = 0};
struct wg_interface_io *wg_iface; struct wg_interface_io* wg_iface;
struct wg_peer_io *wg_peer; struct wg_peer_io* wg_peer;
struct wg_aip_io *wg_aip; struct wg_aip_io* wg_aip;
struct wgdevice *dev; struct wgdevice* dev;
struct wgpeer *peer; struct wgpeer* peer;
struct wgallowedip *aip; struct wgallowedip* aip;
int s = get_dgram_socket(), ret; int s = get_dgram_socket(), ret;
if (s < 0) if (s < 0)
return -errno; return -errno;
*device = NULL; *device = NULL;
strlcpy(wgdata.wgd_name, iface, sizeof(wgdata.wgd_name)); strlcpy(wgdata.wgd_name, iface, sizeof(wgdata.wgd_name));
for (size_t last_size = wgdata.wgd_size;; last_size = wgdata.wgd_size) { for (size_t last_size = wgdata.wgd_size;; last_size = wgdata.wgd_size)
if (ioctl(s, SIOCGWG, (caddr_t)&wgdata) < 0) {
goto out; if (ioctl(s, SIOCGWG, (caddr_t)&wgdata) < 0)
if (last_size >= wgdata.wgd_size) goto out;
break; if (last_size >= wgdata.wgd_size)
wgdata.wgd_interface = realloc(wgdata.wgd_interface, wgdata.wgd_size); break;
if (!wgdata.wgd_interface) wgdata.wgd_interface = realloc(wgdata.wgd_interface, wgdata.wgd_size);
goto out; if (!wgdata.wgd_interface)
} goto out;
}
wg_iface = wgdata.wgd_interface; wg_iface = wgdata.wgd_interface;
dev = calloc(1, sizeof(*dev)); dev = calloc(1, sizeof(*dev));
if (!dev) if (!dev)
goto out; goto out;
strlcpy(dev->name, iface, sizeof(dev->name)); strlcpy(dev->name, iface, sizeof(dev->name));
if (wg_iface->i_flags & WG_INTERFACE_HAS_RTABLE) { if (wg_iface->i_flags & WG_INTERFACE_HAS_RTABLE)
dev->fwmark = wg_iface->i_rtable; {
dev->flags |= WGDEVICE_HAS_FWMARK; dev->fwmark = wg_iface->i_rtable;
} dev->flags |= WGDEVICE_HAS_FWMARK;
}
if (wg_iface->i_flags & WG_INTERFACE_HAS_PORT) { if (wg_iface->i_flags & WG_INTERFACE_HAS_PORT)
dev->listen_port = wg_iface->i_port; {
dev->flags |= WGDEVICE_HAS_LISTEN_PORT; dev->listen_port = wg_iface->i_port;
} dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
}
if (wg_iface->i_flags & WG_INTERFACE_HAS_PUBLIC) { if (wg_iface->i_flags & WG_INTERFACE_HAS_PUBLIC)
memcpy(dev->public_key, wg_iface->i_public, sizeof(dev->public_key)); {
dev->flags |= WGDEVICE_HAS_PUBLIC_KEY; memcpy(dev->public_key, wg_iface->i_public, sizeof(dev->public_key));
} dev->flags |= WGDEVICE_HAS_PUBLIC_KEY;
}
if (wg_iface->i_flags & WG_INTERFACE_HAS_PRIVATE) { if (wg_iface->i_flags & WG_INTERFACE_HAS_PRIVATE)
memcpy(dev->private_key, wg_iface->i_private, sizeof(dev->private_key)); {
dev->flags |= WGDEVICE_HAS_PRIVATE_KEY; memcpy(dev->private_key, wg_iface->i_private, sizeof(dev->private_key));
} dev->flags |= WGDEVICE_HAS_PRIVATE_KEY;
}
if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_JC) { if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_JC)
dev->junk_packet_count = wg_iface->i_junk_packet_count; {
dev->flags |= WGDEVICE_HAS_JC; dev->junk_packet_count = wg_iface->i_junk_packet_count;
} dev->flags |= WGDEVICE_HAS_JC;
}
if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_JMIN) { if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_JMIN)
dev->junk_packet_min_size = wg_iface->i_junk_packet_min_size; {
dev->flags |= WGDEVICE_HAS_JMIN; dev->junk_packet_min_size = wg_iface->i_junk_packet_min_size;
} dev->flags |= WGDEVICE_HAS_JMIN;
}
if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_JMAX) { if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_JMAX)
dev->junk_packet_max_size = wg_iface->i_junk_packet_max_size; {
dev->flags |= WGDEVICE_HAS_JMAX; dev->junk_packet_max_size = wg_iface->i_junk_packet_max_size;
} dev->flags |= WGDEVICE_HAS_JMAX;
}
if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_S1) { if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_S1)
dev->init_packet_junk_size = wg_iface->i_init_packet_junk_size; {
dev->flags |= WGDEVICE_HAS_S1; dev->init_packet_junk_size = wg_iface->i_init_packet_junk_size;
} dev->flags |= WGDEVICE_HAS_S1;
}
if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_S2) { if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_S2)
dev->response_packet_junk_size = wg_iface->i_response_packet_junk_size; {
dev->flags |= WGDEVICE_HAS_S2; dev->response_packet_junk_size = wg_iface->i_response_packet_junk_size;
} dev->flags |= WGDEVICE_HAS_S2;
}
if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_H1) { if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_H1)
dev->init_packet_magic_header = wg_iface->i_init_packet_magic_header; {
dev->flags |= WGDEVICE_HAS_H1; dev->init_packet_magic_header = wg_iface->i_init_packet_magic_header;
} dev->flags |= WGDEVICE_HAS_H1;
}
if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_H2) { if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_H2)
dev->response_packet_magic_header = wg_iface->i_response_packet_magic_header; {
dev->flags |= WGDEVICE_HAS_H2; dev->response_packet_magic_header = wg_iface->i_response_packet_magic_header;
} dev->flags |= WGDEVICE_HAS_H2;
}
if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_H3) { if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_H3)
dev->underload_packet_magic_header = wg_iface->i_underload_packet_magic_header; {
dev->flags |= WGDEVICE_HAS_H3; dev->underload_packet_magic_header = wg_iface->i_underload_packet_magic_header;
} dev->flags |= WGDEVICE_HAS_H3;
}
if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_H4) { if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_H4)
dev->transport_packet_magic_header = wg_iface->i_transport_packet_magic_header; {
dev->flags |= WGDEVICE_HAS_H4; dev->transport_packet_magic_header = wg_iface->i_transport_packet_magic_header;
} dev->flags |= WGDEVICE_HAS_H4;
}
wg_peer = &wg_iface->i_peers[0];
for (size_t i = 0; i < wg_iface->i_peers_count; ++i) {
peer = calloc(1, sizeof(*peer));
if (!peer)
goto out;
if (dev->first_peer == NULL) if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_I1)
dev->first_peer = peer; {
else wg_iface->i_i1 = strdup(dev->i1);
dev->last_peer->next_peer = peer; wg_iface->i_flags |= WGDEVICE_HAS_I1;
dev->last_peer = peer; }
if (wg_peer->p_flags & WG_PEER_HAS_PUBLIC) { if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_I2)
memcpy(peer->public_key, wg_peer->p_public, sizeof(peer->public_key)); {
peer->flags |= WGPEER_HAS_PUBLIC_KEY; wg_iface->i_i2 = strdup(dev->i2);
} wg_iface->i_flags |= WGDEVICE_HAS_I2;
}
if (wg_peer->p_flags & WG_PEER_HAS_PSK) { if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_I3)
memcpy(peer->preshared_key, wg_peer->p_psk, sizeof(peer->preshared_key)); {
if (!key_is_zero(peer->preshared_key)) wg_iface->i_i3 = strdup(dev->i3);
peer->flags |= WGPEER_HAS_PRESHARED_KEY; wg_iface->i_flags |= WGDEVICE_HAS_I3;
} }
if (wg_peer->p_flags & WG_PEER_HAS_PKA) { if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_I4)
peer->persistent_keepalive_interval = wg_peer->p_pka; {
peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL; wg_iface->i_i4 = strdup(dev->i4);
} wg_iface->i_flags |= WGDEVICE_HAS_I4;
}
if (wg_peer->p_flags & WG_PEER_HAS_ENDPOINT && wg_peer->p_sa.sa_len <= sizeof(peer->endpoint.addr)) if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_I5)
memcpy(&peer->endpoint.addr, &wg_peer->p_sa, wg_peer->p_sa.sa_len); {
wg_iface->i_i5 = strdup(dev->i5);
wg_iface->i_flags |= WGDEVICE_HAS_I5;
}
peer->rx_bytes = wg_peer->p_rxbytes; if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_J1)
peer->tx_bytes = wg_peer->p_txbytes; {
wg_iface->i_j1 = strdup(dev->j1);
wg_iface->i_flags |= WGDEVICE_HAS_J1;
}
peer->last_handshake_time.tv_sec = wg_peer->p_last_handshake.tv_sec; if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_J2)
peer->last_handshake_time.tv_nsec = wg_peer->p_last_handshake.tv_nsec; {
wg_iface->i_j2 = strdup(dev->j2);
wg_iface->i_flags |= WGDEVICE_HAS_J2;
}
wg_aip = &wg_peer->p_aips[0]; if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_J3)
for (size_t j = 0; j < wg_peer->p_aips_count; ++j) { {
aip = calloc(1, sizeof(*aip)); wg_iface->i_j3 = strdup(dev->j3);
if (!aip) wg_iface->i_flags |= WGDEVICE_HAS_J3;
goto out; }
if (peer->first_allowedip == NULL) if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_ITIME)
peer->first_allowedip = aip; {
else wg_iface->i_itime = dev->itime;
peer->last_allowedip->next_allowedip = aip; wg_iface->i_flags |= WGDEVICE_HAS_ITIME;
peer->last_allowedip = aip; }
aip->family = wg_aip->a_af; wg_peer = &wg_iface->i_peers[0];
if (wg_aip->a_af == AF_INET) { for (size_t i = 0; i < wg_iface->i_peers_count; ++i)
memcpy(&aip->ip4, &wg_aip->a_ipv4, sizeof(aip->ip4)); {
aip->cidr = wg_aip->a_cidr; peer = calloc(1, sizeof(*peer));
} else if (wg_aip->a_af == AF_INET6) { if (!peer)
memcpy(&aip->ip6, &wg_aip->a_ipv6, sizeof(aip->ip6)); goto out;
aip->cidr = wg_aip->a_cidr;
} if (dev->first_peer == NULL)
++wg_aip; dev->first_peer = peer;
} else
wg_peer = (struct wg_peer_io *)wg_aip; dev->last_peer->next_peer = peer;
} dev->last_peer = peer;
*device = dev;
errno = 0; if (wg_peer->p_flags & WG_PEER_HAS_PUBLIC)
{
memcpy(peer->public_key, wg_peer->p_public, sizeof(peer->public_key));
peer->flags |= WGPEER_HAS_PUBLIC_KEY;
}
if (wg_peer->p_flags & WG_PEER_HAS_PSK)
{
memcpy(peer->preshared_key, wg_peer->p_psk, sizeof(peer->preshared_key));
if (!key_is_zero(peer->preshared_key))
peer->flags |= WGPEER_HAS_PRESHARED_KEY;
}
if (wg_peer->p_flags & WG_PEER_HAS_PKA)
{
peer->persistent_keepalive_interval = wg_peer->p_pka;
peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
}
if (wg_peer->p_flags & WG_PEER_HAS_ENDPOINT &&
wg_peer->p_sa.sa_len <= sizeof(peer->endpoint.addr))
memcpy(&peer->endpoint.addr, &wg_peer->p_sa, wg_peer->p_sa.sa_len);
peer->rx_bytes = wg_peer->p_rxbytes;
peer->tx_bytes = wg_peer->p_txbytes;
peer->last_handshake_time.tv_sec = wg_peer->p_last_handshake.tv_sec;
peer->last_handshake_time.tv_nsec = wg_peer->p_last_handshake.tv_nsec;
wg_aip = &wg_peer->p_aips[0];
for (size_t j = 0; j < wg_peer->p_aips_count; ++j)
{
aip = calloc(1, sizeof(*aip));
if (!aip)
goto out;
if (peer->first_allowedip == NULL)
peer->first_allowedip = aip;
else
peer->last_allowedip->next_allowedip = aip;
peer->last_allowedip = aip;
aip->family = wg_aip->a_af;
if (wg_aip->a_af == AF_INET)
{
memcpy(&aip->ip4, &wg_aip->a_ipv4, sizeof(aip->ip4));
aip->cidr = wg_aip->a_cidr;
}
else if (wg_aip->a_af == AF_INET6)
{
memcpy(&aip->ip6, &wg_aip->a_ipv6, sizeof(aip->ip6));
aip->cidr = wg_aip->a_cidr;
}
++wg_aip;
}
wg_peer = (struct wg_peer_io*)wg_aip;
}
*device = dev;
errno = 0;
out: out:
ret = -errno; ret = -errno;
free(wgdata.wgd_interface); free(wgdata.wgd_interface);
return ret; return ret;
} }
static int kernel_set_device(struct wgdevice *dev) static int kernel_set_device(struct wgdevice* dev)
{ {
struct wg_data_io wgdata = { .wgd_size = sizeof(struct wg_interface_io) }; struct wg_data_io wgdata = {.wgd_size = sizeof(struct wg_interface_io)};
struct wg_interface_io *wg_iface; struct wg_interface_io* wg_iface;
struct wg_peer_io *wg_peer; struct wg_peer_io* wg_peer;
struct wg_aip_io *wg_aip; struct wg_aip_io* wg_aip;
struct wgpeer *peer; struct wgpeer* peer;
struct wgallowedip *aip; struct wgallowedip* aip;
int s = get_dgram_socket(), ret; int s = get_dgram_socket(), ret;
size_t peer_count, aip_count; size_t peer_count, aip_count;
if (s < 0) if (s < 0)
return -errno; return -errno;
for_each_wgpeer(dev, peer) { for_each_wgpeer(dev, peer)
wgdata.wgd_size += sizeof(struct wg_peer_io); {
for_each_wgallowedip(peer, aip) wgdata.wgd_size += sizeof(struct wg_peer_io);
wgdata.wgd_size += sizeof(struct wg_aip_io); for_each_wgallowedip(peer, aip) wgdata.wgd_size += sizeof(struct wg_aip_io);
} }
wg_iface = wgdata.wgd_interface = calloc(1, wgdata.wgd_size); wg_iface = wgdata.wgd_interface = calloc(1, wgdata.wgd_size);
if (!wgdata.wgd_interface) if (!wgdata.wgd_interface)
return -errno; return -errno;
strlcpy(wgdata.wgd_name, dev->name, sizeof(wgdata.wgd_name)); strlcpy(wgdata.wgd_name, dev->name, sizeof(wgdata.wgd_name));
if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) { if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)
memcpy(wg_iface->i_private, dev->private_key, sizeof(wg_iface->i_private)); {
wg_iface->i_flags |= WG_INTERFACE_HAS_PRIVATE; memcpy(wg_iface->i_private, dev->private_key, sizeof(wg_iface->i_private));
} wg_iface->i_flags |= WG_INTERFACE_HAS_PRIVATE;
}
if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) { if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
wg_iface->i_port = dev->listen_port; {
wg_iface->i_flags |= WG_INTERFACE_HAS_PORT; wg_iface->i_port = dev->listen_port;
} wg_iface->i_flags |= WG_INTERFACE_HAS_PORT;
}
if (dev->flags & WGDEVICE_HAS_FWMARK) { if (dev->flags & WGDEVICE_HAS_FWMARK)
wg_iface->i_rtable = dev->fwmark; {
wg_iface->i_flags |= WG_INTERFACE_HAS_RTABLE; wg_iface->i_rtable = dev->fwmark;
} wg_iface->i_flags |= WG_INTERFACE_HAS_RTABLE;
}
if (dev->flags & WGDEVICE_REPLACE_PEERS) if (dev->flags & WGDEVICE_REPLACE_PEERS)
wg_iface->i_flags |= WG_INTERFACE_REPLACE_PEERS; wg_iface->i_flags |= WG_INTERFACE_REPLACE_PEERS;
if (dev->flags & WGDEVICE_HAS_JC)
{
wg_iface->i_junk_packet_count = dev->junk_packet_count;
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_JC;
}
if (dev->flags & WGDEVICE_HAS_JC) { if (dev->flags & WGDEVICE_HAS_JMIN)
wg_iface->i_junk_packet_count = dev->junk_packet_count; {
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_JC; wg_iface->i_junk_packet_min_size = dev->junk_packet_min_size;
} wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_JMIN;
}
if (dev->flags & WGDEVICE_HAS_JMIN) { if (dev->flags & WGDEVICE_HAS_JMAX)
wg_iface->i_junk_packet_min_size = dev->junk_packet_min_size; {
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_JMIN; wg_iface->i_junk_packet_max_size = dev->junk_packet_max_size;
} wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_JMAX;
}
if (dev->flags & WGDEVICE_HAS_JMAX) { if (dev->flags & WGDEVICE_HAS_S1)
wg_iface->i_junk_packet_max_size = dev->junk_packet_max_size; {
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_JMAX; wg_iface->i_init_packet_junk_size = dev->init_packet_junk_size;
} wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_S1;
}
if (dev->flags & WGDEVICE_HAS_S1) { if (dev->flags & WGDEVICE_HAS_S2)
wg_iface->i_init_packet_junk_size = dev->init_packet_junk_size; {
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_S1; wg_iface->i_response_packet_junk_size = dev->response_packet_junk_size;
} wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_S2;
}
if (dev->flags & WGDEVICE_HAS_S2) { if (dev->flags & WGDEVICE_HAS_H1)
wg_iface->i_response_packet_junk_size = dev->response_packet_junk_size; {
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_S2; wg_iface->i_init_packet_magic_header = dev->init_packet_magic_header;
} wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_H1;
}
if (dev->flags & WGDEVICE_HAS_H1) { if (dev->flags & WGDEVICE_HAS_H2)
wg_iface->i_init_packet_magic_header = dev->init_packet_magic_header; {
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_H1; wg_iface->i_response_packet_magic_header = dev->response_packet_magic_header;
} wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_H2;
}
if (dev->flags & WGDEVICE_HAS_H2) { if (dev->flags & WGDEVICE_HAS_H3)
wg_iface->i_response_packet_magic_header = dev->response_packet_magic_header; {
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_H2; wg_iface->i_underload_packet_magic_header = dev->underload_packet_magic_header;
} wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_H3;
}
if (dev->flags & WGDEVICE_HAS_H3) { if (dev->flags & WGDEVICE_HAS_H4)
wg_iface->i_underload_packet_magic_header = dev->underload_packet_magic_header; {
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_H3; wg_iface->i_transport_packet_magic_header = dev->transport_packet_magic_header;
} wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_H4;
}
if (dev->flags & WGDEVICE_HAS_H4) { if (dev->flags & WGDEVICE_HAS_I1)
wg_iface->i_transport_packet_magic_header = dev->transport_packet_magic_header; {
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_H4; wg_iface->i_i1 = strdup(dev->i1);
} wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_I1;
}
peer_count = 0; if (dev->flags & WGDEVICE_HAS_I2)
wg_peer = &wg_iface->i_peers[0]; {
for_each_wgpeer(dev, peer) { wg_iface->i_i2 = strdup(dev->i2);
wg_peer->p_flags = WG_PEER_HAS_PUBLIC; wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_I2;
memcpy(wg_peer->p_public, peer->public_key, sizeof(wg_peer->p_public)); }
if (peer->flags & WGPEER_HAS_PRESHARED_KEY) { if (dev->flags & WGDEVICE_HAS_I3)
memcpy(wg_peer->p_psk, peer->preshared_key, sizeof(wg_peer->p_psk)); {
wg_peer->p_flags |= WG_PEER_HAS_PSK; wg_iface->i_i3 = strdup(dev->i3);
} wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_I3;
}
if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) { if (dev->flags & WGDEVICE_HAS_I4)
wg_peer->p_pka = peer->persistent_keepalive_interval; {
wg_peer->p_flags |= WG_PEER_HAS_PKA; wg_iface->i_i4 = strdup(dev->i4);
} wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_I4;
}
if ((peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) && if (dev->flags & WGDEVICE_HAS_I5)
peer->endpoint.addr.sa_len <= sizeof(wg_peer->p_endpoint)) { {
memcpy(&wg_peer->p_endpoint, &peer->endpoint.addr, peer->endpoint.addr.sa_len); wg_iface->i_i5 = strdup(dev->i5);
wg_peer->p_flags |= WG_PEER_HAS_ENDPOINT; wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_I5;
} }
if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS) if (dev->flags & WGDEVICE_HAS_J1)
wg_peer->p_flags |= WG_PEER_REPLACE_AIPS; {
wg_iface->i_j1 = strdup(dev->j1);
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_J1;
}
if (peer->flags & WGPEER_REMOVE_ME) if (dev->flags & WGDEVICE_HAS_J2)
wg_peer->p_flags |= WG_PEER_REMOVE; {
wg_iface->i_j2 = strdup(dev->j2);
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_J2;
}
aip_count = 0; if (dev->flags & WGDEVICE_HAS_J3)
wg_aip = &wg_peer->p_aips[0]; {
for_each_wgallowedip(peer, aip) { wg_iface->i_j3 = strdup(dev->j3);
wg_aip->a_af = aip->family; wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_J3;
wg_aip->a_cidr = aip->cidr; }
if (aip->family == AF_INET) if (dev->flags & WGDEVICE_HAS_ITIME)
memcpy(&wg_aip->a_ipv4, &aip->ip4, sizeof(wg_aip->a_ipv4)); {
else if (aip->family == AF_INET6) wg_iface->i_itime = dev->itime;
memcpy(&wg_aip->a_ipv6, &aip->ip6, sizeof(wg_aip->a_ipv6)); wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_ITIME;
else }
continue;
++aip_count;
++wg_aip;
}
wg_peer->p_aips_count = aip_count;
++peer_count;
wg_peer = (struct wg_peer_io *)wg_aip;
}
wg_iface->i_peers_count = peer_count;
if (ioctl(s, SIOCSWG, (caddr_t)&wgdata) < 0) peer_count = 0;
goto out; wg_peer = &wg_iface->i_peers[0];
errno = 0; for_each_wgpeer(dev, peer)
{
wg_peer->p_flags = WG_PEER_HAS_PUBLIC;
memcpy(wg_peer->p_public, peer->public_key, sizeof(wg_peer->p_public));
if (peer->flags & WGPEER_HAS_PRESHARED_KEY)
{
memcpy(wg_peer->p_psk, peer->preshared_key, sizeof(wg_peer->p_psk));
wg_peer->p_flags |= WG_PEER_HAS_PSK;
}
if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL)
{
wg_peer->p_pka = peer->persistent_keepalive_interval;
wg_peer->p_flags |= WG_PEER_HAS_PKA;
}
if ((peer->endpoint.addr.sa_family == AF_INET ||
peer->endpoint.addr.sa_family == AF_INET6) &&
peer->endpoint.addr.sa_len <= sizeof(wg_peer->p_endpoint))
{
memcpy(
&wg_peer->p_endpoint, &peer->endpoint.addr, peer->endpoint.addr.sa_len);
wg_peer->p_flags |= WG_PEER_HAS_ENDPOINT;
}
if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
wg_peer->p_flags |= WG_PEER_REPLACE_AIPS;
if (peer->flags & WGPEER_REMOVE_ME)
wg_peer->p_flags |= WG_PEER_REMOVE;
aip_count = 0;
wg_aip = &wg_peer->p_aips[0];
for_each_wgallowedip(peer, aip)
{
wg_aip->a_af = aip->family;
wg_aip->a_cidr = aip->cidr;
if (aip->family == AF_INET)
memcpy(&wg_aip->a_ipv4, &aip->ip4, sizeof(wg_aip->a_ipv4));
else if (aip->family == AF_INET6)
memcpy(&wg_aip->a_ipv6, &aip->ip6, sizeof(wg_aip->a_ipv6));
else
continue;
++aip_count;
++wg_aip;
}
wg_peer->p_aips_count = aip_count;
++peer_count;
wg_peer = (struct wg_peer_io*)wg_aip;
}
wg_iface->i_peers_count = peer_count;
if (ioctl(s, SIOCSWG, (caddr_t)&wgdata) < 0)
goto out;
errno = 0;
out: out:
ret = -errno; ret = -errno;
free(wgdata.wgd_interface); free(wgdata.wgd_interface);
return ret; return ret;
} }

View file

@ -3,6 +3,10 @@
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */
#include "containers.h"
#include "ctype.h"
#include "curve25519.h"
#include "encoding.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <errno.h> #include <errno.h>
#include <limits.h> #include <limits.h>
@ -14,10 +18,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/socket.h> #include <sys/socket.h>
#include "containers.h"
#include "curve25519.h"
#include "encoding.h"
#include "ctype.h"
#ifdef _WIN32 #ifdef _WIN32
#include "ipc-uapi-windows.h" #include "ipc-uapi-windows.h"
@ -25,322 +25,460 @@
#include "ipc-uapi-unix.h" #include "ipc-uapi-unix.h"
#endif #endif
static int userspace_set_device(struct wgdevice *dev) static int userspace_set_device(struct wgdevice* dev)
{ {
char hex[WG_KEY_LEN_HEX], ip[INET6_ADDRSTRLEN], host[4096 + 1], service[512 + 1]; char hex[WG_KEY_LEN_HEX], ip[INET6_ADDRSTRLEN], host[4096 + 1], service[512 + 1];
struct wgpeer *peer; struct wgpeer* peer;
struct wgallowedip *allowedip; struct wgallowedip* allowedip;
FILE *f; FILE* f;
int ret, set_errno = -EPROTO; int ret, set_errno = -EPROTO;
socklen_t addr_len; socklen_t addr_len;
size_t line_buffer_len = 0, line_len; size_t line_buffer_len = 0, line_len;
char *key = NULL, *value; char * key = NULL, *value;
f = userspace_interface_file(dev->name); f = userspace_interface_file(dev->name);
if (!f) if (!f)
return -errno; return -errno;
fprintf(f, "set=1\n"); fprintf(f, "set=1\n");
if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) { if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)
key_to_hex(hex, dev->private_key); {
fprintf(f, "private_key=%s\n", hex); key_to_hex(hex, dev->private_key);
} fprintf(f, "private_key=%s\n", hex);
if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) }
fprintf(f, "listen_port=%u\n", dev->listen_port); if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
if (dev->flags & WGDEVICE_HAS_FWMARK) fprintf(f, "listen_port=%u\n", dev->listen_port);
fprintf(f, "fwmark=%u\n", dev->fwmark); if (dev->flags & WGDEVICE_HAS_FWMARK)
if (dev->flags & WGDEVICE_REPLACE_PEERS) fprintf(f, "fwmark=%u\n", dev->fwmark);
fprintf(f, "replace_peers=true\n"); if (dev->flags & WGDEVICE_REPLACE_PEERS)
if (dev->flags & WGDEVICE_HAS_JC) fprintf(f, "replace_peers=true\n");
fprintf(f, "jc=%u\n", dev->junk_packet_count); if (dev->flags & WGDEVICE_HAS_JC)
if (dev->flags & WGDEVICE_HAS_JMIN) fprintf(f, "jc=%u\n", dev->junk_packet_count);
fprintf(f, "jmin=%u\n", dev->junk_packet_min_size); if (dev->flags & WGDEVICE_HAS_JMIN)
if (dev->flags & WGDEVICE_HAS_JMAX) fprintf(f, "jmin=%u\n", dev->junk_packet_min_size);
fprintf(f, "jmax=%u\n", dev->junk_packet_max_size); if (dev->flags & WGDEVICE_HAS_JMAX)
if (dev->flags & WGDEVICE_HAS_S1) fprintf(f, "jmax=%u\n", dev->junk_packet_max_size);
fprintf(f, "s1=%u\n", dev->init_packet_junk_size); if (dev->flags & WGDEVICE_HAS_S1)
if (dev->flags & WGDEVICE_HAS_S2) fprintf(f, "s1=%u\n", dev->init_packet_junk_size);
fprintf(f, "s2=%u\n", dev->response_packet_junk_size); if (dev->flags & WGDEVICE_HAS_S2)
if (dev->flags & WGDEVICE_HAS_H1) fprintf(f, "s2=%u\n", dev->response_packet_junk_size);
fprintf(f, "h1=%u\n", dev->init_packet_magic_header); if (dev->flags & WGDEVICE_HAS_H1)
if (dev->flags & WGDEVICE_HAS_H2) fprintf(f, "h1=%u\n", dev->init_packet_magic_header);
fprintf(f, "h2=%u\n", dev->response_packet_magic_header); if (dev->flags & WGDEVICE_HAS_H2)
if (dev->flags & WGDEVICE_HAS_H3) fprintf(f, "h2=%u\n", dev->response_packet_magic_header);
fprintf(f, "h3=%u\n", dev->underload_packet_magic_header); if (dev->flags & WGDEVICE_HAS_H3)
if (dev->flags & WGDEVICE_HAS_H4) fprintf(f, "h3=%u\n", dev->underload_packet_magic_header);
fprintf(f, "h4=%u\n", dev->transport_packet_magic_header); if (dev->flags & WGDEVICE_HAS_H4)
fprintf(f, "h4=%u\n", dev->transport_packet_magic_header);
if (dev->flags & WGDEVICE_HAS_I1)
fprintf(f, "i1=%s\n", dev->i1);
if (dev->flags & WGDEVICE_HAS_I2)
fprintf(f, "i2=%s\n", dev->i2);
if (dev->flags & WGDEVICE_HAS_I3)
fprintf(f, "i3=%s\n", dev->i3);
if (dev->flags & WGDEVICE_HAS_I4)
fprintf(f, "i4=%s\n", dev->i4);
if (dev->flags & WGDEVICE_HAS_I5)
fprintf(f, "i5=%s\n", dev->i5);
if (dev->flags & WGDEVICE_HAS_J1)
fprintf(f, "j1=%s\n", dev->j1);
if (dev->flags & WGDEVICE_HAS_J2)
fprintf(f, "j2=%s\n", dev->j2);
if (dev->flags & WGDEVICE_HAS_J3)
fprintf(f, "j3=%s\n", dev->j3);
if (dev->flags & WGDEVICE_HAS_ITIME)
fprintf(f, "itime=%u\n", dev->itime);
for_each_wgpeer(dev, peer) { for_each_wgpeer(dev, peer)
key_to_hex(hex, peer->public_key); {
fprintf(f, "public_key=%s\n", hex); key_to_hex(hex, peer->public_key);
if (peer->flags & WGPEER_HAS_ADVANCED_SECURITY) { fprintf(f, "public_key=%s\n", hex);
ret = -EINVAL; if (peer->flags & WGPEER_HAS_ADVANCED_SECURITY)
goto out; {
} ret = -EINVAL;
if (peer->flags & WGPEER_REMOVE_ME) { goto out;
fprintf(f, "remove=true\n"); }
continue; if (peer->flags & WGPEER_REMOVE_ME)
} {
if (peer->flags & WGPEER_HAS_PRESHARED_KEY) { fprintf(f, "remove=true\n");
key_to_hex(hex, peer->preshared_key); continue;
fprintf(f, "preshared_key=%s\n", hex); }
} if (peer->flags & WGPEER_HAS_PRESHARED_KEY)
if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) { {
addr_len = 0; key_to_hex(hex, peer->preshared_key);
if (peer->endpoint.addr.sa_family == AF_INET) fprintf(f, "preshared_key=%s\n", hex);
addr_len = sizeof(struct sockaddr_in); }
else if (peer->endpoint.addr.sa_family == AF_INET6) if (peer->endpoint.addr.sa_family == AF_INET ||
addr_len = sizeof(struct sockaddr_in6); peer->endpoint.addr.sa_family == AF_INET6)
if (!getnameinfo(&peer->endpoint.addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) { {
if (peer->endpoint.addr.sa_family == AF_INET6 && strchr(host, ':')) addr_len = 0;
fprintf(f, "endpoint=[%s]:%s\n", host, service); if (peer->endpoint.addr.sa_family == AF_INET)
else addr_len = sizeof(struct sockaddr_in);
fprintf(f, "endpoint=%s:%s\n", host, service); else if (peer->endpoint.addr.sa_family == AF_INET6)
} addr_len = sizeof(struct sockaddr_in6);
} if (!getnameinfo(
if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) &peer->endpoint.addr,
fprintf(f, "persistent_keepalive_interval=%u\n", peer->persistent_keepalive_interval); addr_len,
if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS) host,
fprintf(f, "replace_allowed_ips=true\n"); sizeof(host),
for_each_wgallowedip(peer, allowedip) { service,
if (allowedip->family == AF_INET) { sizeof(service),
if (!inet_ntop(AF_INET, &allowedip->ip4, ip, INET6_ADDRSTRLEN)) NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST))
continue; {
} else if (allowedip->family == AF_INET6) { if (peer->endpoint.addr.sa_family == AF_INET6 && strchr(host, ':'))
if (!inet_ntop(AF_INET6, &allowedip->ip6, ip, INET6_ADDRSTRLEN)) fprintf(f, "endpoint=[%s]:%s\n", host, service);
continue; else
} else fprintf(f, "endpoint=%s:%s\n", host, service);
continue; }
fprintf(f, "allowed_ip=%s/%d\n", ip, allowedip->cidr); }
} if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL)
} fprintf(
fprintf(f, "\n"); f,
fflush(f); "persistent_keepalive_interval=%u\n",
peer->persistent_keepalive_interval);
if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
fprintf(f, "replace_allowed_ips=true\n");
for_each_wgallowedip(peer, allowedip)
{
if (allowedip->family == AF_INET)
{
if (!inet_ntop(AF_INET, &allowedip->ip4, ip, INET6_ADDRSTRLEN))
continue;
}
else if (allowedip->family == AF_INET6)
{
if (!inet_ntop(AF_INET6, &allowedip->ip6, ip, INET6_ADDRSTRLEN))
continue;
}
else
continue;
fprintf(f, "allowed_ip=%s/%d\n", ip, allowedip->cidr);
}
}
fprintf(f, "\n");
fflush(f);
while (getline(&key, &line_buffer_len, f) > 0) { while (getline(&key, &line_buffer_len, f) > 0)
line_len = strlen(key); {
ret = set_errno; line_len = strlen(key);
if (line_len == 1 && key[0] == '\n') ret = set_errno;
goto out; if (line_len == 1 && key[0] == '\n')
value = strchr(key, '='); goto out;
if (!value || line_len == 0 || key[line_len - 1] != '\n') value = strchr(key, '=');
break; if (!value || line_len == 0 || key[line_len - 1] != '\n')
*value++ = key[--line_len] = '\0'; break;
*value++ = key[--line_len] = '\0';
if (!strcmp(key, "errno")) { if (!strcmp(key, "errno"))
long long num; {
char *end; long long num;
if (value[0] != '-' && !char_is_digit(value[0])) char* end;
break; if (value[0] != '-' && !char_is_digit(value[0]))
num = strtoll(value, &end, 10); break;
if (*end || num > INT_MAX || num < INT_MIN) num = strtoll(value, &end, 10);
break; if (*end || num > INT_MAX || num < INT_MIN)
set_errno = num; break;
} set_errno = num;
} }
ret = errno ? -errno : -EPROTO; }
ret = errno ? -errno : -EPROTO;
out: out:
free(key); free(key);
fclose(f); fclose(f);
errno = -ret; errno = -ret;
return ret; return ret;
} }
#define NUM(max) ({ \ #define NUM(max) \
unsigned long long num; \ ({ \
char *end; \ unsigned long long num; \
if (!char_is_digit(value[0])) \ char* end; \
break; \ if (!char_is_digit(value[0])) \
num = strtoull(value, &end, 10); \ break; \
if (*end || num > max) \ num = strtoull(value, &end, 10); \
break; \ if (*end || num > max) \
num; \ break; \
}) num; \
})
static int userspace_get_device(struct wgdevice **out, const char *iface) static int userspace_get_device(struct wgdevice** out, const char* iface)
{ {
struct wgdevice *dev; struct wgdevice* dev;
struct wgpeer *peer = NULL; struct wgpeer* peer = NULL;
struct wgallowedip *allowedip = NULL; struct wgallowedip* allowedip = NULL;
size_t line_buffer_len = 0, line_len; size_t line_buffer_len = 0, line_len;
char *key = NULL, *value; char * key = NULL, *value;
FILE *f; FILE* f;
int ret = -EPROTO; int ret = -EPROTO;
*out = dev = calloc(1, sizeof(*dev)); *out = dev = calloc(1, sizeof(*dev));
if (!dev) if (!dev)
return -errno; return -errno;
f = userspace_interface_file(iface); f = userspace_interface_file(iface);
if (!f) { if (!f)
ret = -errno; {
free(dev); ret = -errno;
*out = NULL; free(dev);
return ret; *out = NULL;
} return ret;
}
fprintf(f, "get=1\n\n"); fprintf(f, "get=1\n\n");
fflush(f); fflush(f);
strncpy(dev->name, iface, IFNAMSIZ - 1); strncpy(dev->name, iface, IFNAMSIZ - 1);
dev->name[IFNAMSIZ - 1] = '\0'; dev->name[IFNAMSIZ - 1] = '\0';
while (getline(&key, &line_buffer_len, f) > 0) { while (getline(&key, &line_buffer_len, f) > 0)
line_len = strlen(key); {
if (line_len == 1 && key[0] == '\n') line_len = strlen(key);
goto err; if (line_len == 1 && key[0] == '\n')
value = strchr(key, '='); goto err;
if (!value || line_len == 0 || key[line_len - 1] != '\n') value = strchr(key, '=');
break; if (!value || line_len == 0 || key[line_len - 1] != '\n')
*value++ = key[--line_len] = '\0'; break;
*value++ = key[--line_len] = '\0';
if (!peer && !strcmp(key, "private_key")) { if (!peer && !strcmp(key, "private_key"))
if (!key_from_hex(dev->private_key, value)) {
break; if (!key_from_hex(dev->private_key, value))
curve25519_generate_public(dev->public_key, dev->private_key); break;
dev->flags |= WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_PUBLIC_KEY; curve25519_generate_public(dev->public_key, dev->private_key);
} else if (!peer && !strcmp(key, "listen_port")) { dev->flags |= WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_PUBLIC_KEY;
dev->listen_port = NUM(0xffffU); }
dev->flags |= WGDEVICE_HAS_LISTEN_PORT; else if (!peer && !strcmp(key, "listen_port"))
} else if (!peer && !strcmp(key, "fwmark")) { {
dev->fwmark = NUM(0xffffffffU); dev->listen_port = NUM(0xffffU);
dev->flags |= WGDEVICE_HAS_FWMARK; dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
} else if(!peer && !strcmp(key, "jc")) { }
dev->junk_packet_count = NUM(0xffffU); else if (!peer && !strcmp(key, "fwmark"))
dev->flags |= WGDEVICE_HAS_JC; {
} else if(!peer && !strcmp(key, "jmin")) { dev->fwmark = NUM(0xffffffffU);
dev->junk_packet_min_size = NUM(0xffffU); dev->flags |= WGDEVICE_HAS_FWMARK;
dev->flags |= WGDEVICE_HAS_JMIN; }
} else if(!peer && !strcmp(key, "jmax")) { else if (!peer && !strcmp(key, "jc"))
dev->junk_packet_max_size = NUM(0xffffU); {
dev->flags |= WGDEVICE_HAS_JMAX; dev->junk_packet_count = NUM(0xffffU);
} else if(!peer && !strcmp(key, "s1")) { dev->flags |= WGDEVICE_HAS_JC;
dev->init_packet_junk_size = NUM(0xffffU); }
dev->flags |= WGDEVICE_HAS_S1; else if (!peer && !strcmp(key, "jmin"))
} else if(!peer && !strcmp(key, "s2")) { {
dev->response_packet_junk_size = NUM(0xffffU); dev->junk_packet_min_size = NUM(0xffffU);
dev->flags |= WGDEVICE_HAS_S2; dev->flags |= WGDEVICE_HAS_JMIN;
} else if(!peer && !strcmp(key, "h1")) { }
dev->init_packet_magic_header = NUM(0xffffffffU); else if (!peer && !strcmp(key, "jmax"))
dev->flags |= WGDEVICE_HAS_H1; {
} else if(!peer && !strcmp(key, "h2")) { dev->junk_packet_max_size = NUM(0xffffU);
dev->response_packet_magic_header = NUM(0xffffffffU); dev->flags |= WGDEVICE_HAS_JMAX;
dev->flags |= WGDEVICE_HAS_H2; }
} else if(!peer && !strcmp(key, "h3")) { else if (!peer && !strcmp(key, "s1"))
dev->underload_packet_magic_header = NUM(0xffffffffU); {
dev->flags |= WGDEVICE_HAS_H3; dev->init_packet_junk_size = NUM(0xffffU);
} else if(!peer && !strcmp(key, "h4")) { dev->flags |= WGDEVICE_HAS_S1;
dev->transport_packet_magic_header = NUM(0xffffffffU); }
dev->flags |= WGDEVICE_HAS_H4; else if (!peer && !strcmp(key, "s2"))
} else if (!strcmp(key, "public_key")) { {
struct wgpeer *new_peer = calloc(1, sizeof(*new_peer)); dev->response_packet_junk_size = NUM(0xffffU);
dev->flags |= WGDEVICE_HAS_S2;
}
else if (!peer && !strcmp(key, "h1"))
{
dev->init_packet_magic_header = NUM(0xffffffffU);
dev->flags |= WGDEVICE_HAS_H1;
}
else if (!peer && !strcmp(key, "h2"))
{
dev->response_packet_magic_header = NUM(0xffffffffU);
dev->flags |= WGDEVICE_HAS_H2;
}
else if (!peer && !strcmp(key, "h3"))
{
dev->underload_packet_magic_header = NUM(0xffffffffU);
dev->flags |= WGDEVICE_HAS_H3;
}
else if (!peer && !strcmp(key, "h4"))
{
dev->transport_packet_magic_header = NUM(0xffffffffU);
dev->flags |= WGDEVICE_HAS_H4;
}
else if (!peer && !strcmp(key, "i1"))
{
dev->i1 = strdup(value);
dev->flags |= WGDEVICE_HAS_I1;
}
else if (!peer && !strcmp(key, "i2"))
{
dev->i2 = strdup(value);
dev->flags |= WGDEVICE_HAS_I2;
}
else if (!peer && !strcmp(key, "i3"))
{
dev->i3 = strdup(value);
dev->flags |= WGDEVICE_HAS_I3;
}
else if (!peer && !strcmp(key, "i4"))
{
dev->i4 = strdup(value);
dev->flags |= WGDEVICE_HAS_I4;
}
else if (!peer && !strcmp(key, "i5"))
{
dev->i5 = strdup(value);
dev->flags |= WGDEVICE_HAS_I5;
}
else if (!peer && !strcmp(key, "j1"))
{
dev->j1 = strdup(value);
dev->flags |= WGDEVICE_HAS_J1;
}
else if (!peer && !strcmp(key, "j2"))
{
dev->j2 = strdup(value);
dev->flags |= WGDEVICE_HAS_J2;
}
else if (!peer && !strcmp(key, "j3"))
{
dev->j3 = strdup(value);
dev->flags |= WGDEVICE_HAS_J3;
}
else if (!peer && !strcmp(key, "itime"))
{
dev->itime = NUM(0xffffffffU);
dev->flags |= WGDEVICE_HAS_ITIME;
}
else if (!strcmp(key, "public_key"))
{
struct wgpeer* new_peer = calloc(1, sizeof(*new_peer));
if (!new_peer) { if (!new_peer)
ret = -ENOMEM; {
goto err; ret = -ENOMEM;
} goto err;
allowedip = NULL; }
if (peer) allowedip = NULL;
peer->next_peer = new_peer; if (peer)
else peer->next_peer = new_peer;
dev->first_peer = new_peer; else
peer = new_peer; dev->first_peer = new_peer;
if (!key_from_hex(peer->public_key, value)) peer = new_peer;
break; if (!key_from_hex(peer->public_key, value))
peer->flags |= WGPEER_HAS_PUBLIC_KEY; break;
} else if (peer && !strcmp(key, "preshared_key")) { peer->flags |= WGPEER_HAS_PUBLIC_KEY;
if (!key_from_hex(peer->preshared_key, value)) }
break; else if (peer && !strcmp(key, "preshared_key"))
if (!key_is_zero(peer->preshared_key)) {
peer->flags |= WGPEER_HAS_PRESHARED_KEY; if (!key_from_hex(peer->preshared_key, value))
} else if (peer && !strcmp(key, "endpoint")) { break;
char *begin, *end; if (!key_is_zero(peer->preshared_key))
struct addrinfo *resolved; peer->flags |= WGPEER_HAS_PRESHARED_KEY;
struct addrinfo hints = { }
.ai_family = AF_UNSPEC, else if (peer && !strcmp(key, "endpoint"))
.ai_socktype = SOCK_DGRAM, {
.ai_protocol = IPPROTO_UDP char * begin, *end;
}; struct addrinfo* resolved;
if (!strlen(value)) struct addrinfo hints = {
break; .ai_family = AF_UNSPEC,
if (value[0] == '[') { .ai_socktype = SOCK_DGRAM,
begin = &value[1]; .ai_protocol = IPPROTO_UDP};
end = strchr(value, ']'); if (!strlen(value))
if (!end) break;
break; if (value[0] == '[')
*end++ = '\0'; {
if (*end++ != ':' || !*end) begin = &value[1];
break; end = strchr(value, ']');
} else { if (!end)
begin = value; break;
end = strrchr(value, ':'); *end++ = '\0';
if (!end || !*(end + 1)) if (*end++ != ':' || !*end)
break; break;
*end++ = '\0'; }
} else
if (getaddrinfo(begin, end, &hints, &resolved) != 0) { {
ret = ENETUNREACH; begin = value;
goto err; end = strrchr(value, ':');
} if (!end || !*(end + 1))
if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) || break;
(resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6))) *end++ = '\0';
memcpy(&peer->endpoint.addr, resolved->ai_addr, resolved->ai_addrlen); }
else { if (getaddrinfo(begin, end, &hints, &resolved) != 0)
freeaddrinfo(resolved); {
break; ret = ENETUNREACH;
} goto err;
freeaddrinfo(resolved); }
} else if (peer && !strcmp(key, "persistent_keepalive_interval")) { if ((resolved->ai_family == AF_INET &&
peer->persistent_keepalive_interval = NUM(0xffffU); resolved->ai_addrlen == sizeof(struct sockaddr_in)) ||
peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL; (resolved->ai_family == AF_INET6 &&
} else if (peer && !strcmp(key, "allowed_ip")) { resolved->ai_addrlen == sizeof(struct sockaddr_in6)))
struct wgallowedip *new_allowedip; memcpy(&peer->endpoint.addr, resolved->ai_addr, resolved->ai_addrlen);
char *end, *mask = value, *ip = strsep(&mask, "/"); else
{
freeaddrinfo(resolved);
break;
}
freeaddrinfo(resolved);
}
else if (peer && !strcmp(key, "persistent_keepalive_interval"))
{
peer->persistent_keepalive_interval = NUM(0xffffU);
peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
}
else if (peer && !strcmp(key, "allowed_ip"))
{
struct wgallowedip* new_allowedip;
char * end, *mask = value, *ip = strsep(&mask, "/");
if (!mask || !char_is_digit(mask[0])) if (!mask || !char_is_digit(mask[0]))
break; break;
new_allowedip = calloc(1, sizeof(*new_allowedip)); new_allowedip = calloc(1, sizeof(*new_allowedip));
if (!new_allowedip) { if (!new_allowedip)
ret = -ENOMEM; {
goto err; ret = -ENOMEM;
} goto err;
if (allowedip) }
allowedip->next_allowedip = new_allowedip; if (allowedip)
else allowedip->next_allowedip = new_allowedip;
peer->first_allowedip = new_allowedip; else
allowedip = new_allowedip; peer->first_allowedip = new_allowedip;
allowedip->family = AF_UNSPEC; allowedip = new_allowedip;
if (strchr(ip, ':')) { allowedip->family = AF_UNSPEC;
if (inet_pton(AF_INET6, ip, &allowedip->ip6) == 1) if (strchr(ip, ':'))
allowedip->family = AF_INET6; {
} else { if (inet_pton(AF_INET6, ip, &allowedip->ip6) == 1)
if (inet_pton(AF_INET, ip, &allowedip->ip4) == 1) allowedip->family = AF_INET6;
allowedip->family = AF_INET; }
} else
allowedip->cidr = strtoul(mask, &end, 10); {
if (*end || allowedip->family == AF_UNSPEC || (allowedip->family == AF_INET6 && allowedip->cidr > 128) || (allowedip->family == AF_INET && allowedip->cidr > 32)) if (inet_pton(AF_INET, ip, &allowedip->ip4) == 1)
break; allowedip->family = AF_INET;
} else if (peer && !strcmp(key, "last_handshake_time_sec")) }
peer->last_handshake_time.tv_sec = NUM(0x7fffffffffffffffULL); allowedip->cidr = strtoul(mask, &end, 10);
else if (peer && !strcmp(key, "last_handshake_time_nsec")) if (*end || allowedip->family == AF_UNSPEC ||
peer->last_handshake_time.tv_nsec = NUM(0x7fffffffffffffffULL); (allowedip->family == AF_INET6 && allowedip->cidr > 128) ||
else if (peer && !strcmp(key, "rx_bytes")) (allowedip->family == AF_INET && allowedip->cidr > 32))
peer->rx_bytes = NUM(0xffffffffffffffffULL); break;
else if (peer && !strcmp(key, "tx_bytes")) }
peer->tx_bytes = NUM(0xffffffffffffffffULL); else if (peer && !strcmp(key, "last_handshake_time_sec"))
else if (!strcmp(key, "errno")) peer->last_handshake_time.tv_sec = NUM(0x7fffffffffffffffULL);
ret = -NUM(0x7fffffffU); else if (peer && !strcmp(key, "last_handshake_time_nsec"))
} peer->last_handshake_time.tv_nsec = NUM(0x7fffffffffffffffULL);
ret = -EPROTO; else if (peer && !strcmp(key, "rx_bytes"))
peer->rx_bytes = NUM(0xffffffffffffffffULL);
else if (peer && !strcmp(key, "tx_bytes"))
peer->tx_bytes = NUM(0xffffffffffffffffULL);
else if (!strcmp(key, "errno"))
ret = -NUM(0x7fffffffU);
}
ret = -EPROTO;
err: err:
free(key); free(key);
if (ret) { if (ret)
free_wgdevice(dev); {
*out = NULL; free_wgdevice(dev);
} *out = NULL;
fclose(f); }
errno = -ret; fclose(f);
return ret; errno = -ret;
return ret;
} }
#undef NUM #undef NUM

View file

@ -18,7 +18,7 @@ int set_main(int argc, const char *argv[])
int ret = 1; int ret = 1;
if (argc < 3) { if (argc < 3) {
fprintf(stderr, "Usage: %s %s <interface> [listen-port <port>] [fwmark <mark>] [private-key <file path>] [peer <base64 public key> [remove] [preshared-key <file path>] [endpoint <ip>:<port>] [persistent-keepalive <interval seconds>] [allowed-ips <ip1>/<cidr1>[,<ip2>/<cidr2>] [advanced-security <on|off>]...] ]...\n", PROG_NAME, argv[0]); fprintf(stderr, "Usage: %s %s <interface> [listen-port <port>] [fwmark <mark>] [private-key <file path>] [jc <junk_count>] [jmin <min_value>] [jmax <max_value>] [s1 <init_junk>] [s2 <resp_junk>] [h1 <init_header>] [h2 <resp_header>] [h3 <cookie_header>] [h4 <transp_header>] [i1 <taged_junk>] [i2 <taged_junk>] [i3 <taged_junk>] [i4 <taged_junk>] [i5 <taged_junk>] [j1 <taged_junk>] [j2 <taged_junk>] [j3 <taged_junk>] [itime <itimeout>][peer <base64 public key> [remove] [preshared-key <file path>] [endpoint <ip>:<port>] [persistent-keepalive <interval seconds>] [allowed-ips <ip1>/<cidr1>[,<ip2>/<cidr2>] [advanced-security <on|off>]...] ]...\n", PROG_NAME, argv[0]);
return 1; return 1;
} }

View file

@ -202,7 +202,7 @@ static char *bytes(uint64_t b)
static const char *COMMAND_NAME; static const char *COMMAND_NAME;
static void show_usage(void) static void show_usage(void)
{ {
fprintf(stderr, "Usage: %s %s { <interface> | all | interfaces } [public-key | private-key | listen-port | fwmark | peers | preshared-keys | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive | dump]\n", PROG_NAME, COMMAND_NAME); fprintf(stderr, "Usage: %s %s { <interface> | all | interfaces } [public-key | private-key | listen-port | fwmark | peers | preshared-keys | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive | dump | jc | jmin | jmax | s1 | s2 | h1 | h2 | h3 | h4 | i1 | i2 | i3 | i4 | i5 | j1 | j2 | j3 | itime]\n", PROG_NAME, COMMAND_NAME);
} }
static void pretty_print(struct wgdevice *device) static void pretty_print(struct wgdevice *device)
@ -238,6 +238,25 @@ static void pretty_print(struct wgdevice *device)
terminal_printf(" " TERMINAL_BOLD "h3" TERMINAL_RESET ": %u\n", device->underload_packet_magic_header); terminal_printf(" " TERMINAL_BOLD "h3" TERMINAL_RESET ": %u\n", device->underload_packet_magic_header);
if (device->transport_packet_magic_header) if (device->transport_packet_magic_header)
terminal_printf(" " TERMINAL_BOLD "h4" TERMINAL_RESET ": %u\n", device->transport_packet_magic_header); terminal_printf(" " TERMINAL_BOLD "h4" TERMINAL_RESET ": %u\n", device->transport_packet_magic_header);
if (device->i1)
terminal_printf(" " TERMINAL_BOLD "i1" TERMINAL_RESET ": %s\n", device->i1);
if (device->i2)
terminal_printf(" " TERMINAL_BOLD "i2" TERMINAL_RESET ": %s\n", device->i2);
if (device->i3)
terminal_printf(" " TERMINAL_BOLD "i3" TERMINAL_RESET ": %s\n", device->i3);
if (device->i4)
terminal_printf(" " TERMINAL_BOLD "i4" TERMINAL_RESET ": %s\n", device->i4);
if (device->i5)
terminal_printf(" " TERMINAL_BOLD "i5" TERMINAL_RESET ": %s\n", device->i5);
if (device->j1)
terminal_printf(" " TERMINAL_BOLD "j1" TERMINAL_RESET ": %s\n", device->j1);
if (device->j2)
terminal_printf(" " TERMINAL_BOLD "j2" TERMINAL_RESET ": %s\n", device->j2);
if (device->j3)
terminal_printf(" " TERMINAL_BOLD "j3" TERMINAL_RESET ": %s\n", device->j3);
if (device->itime)
terminal_printf(" " TERMINAL_BOLD "itime" TERMINAL_RESET ": %d\n", device->itime);
if (device->first_peer) { if (device->first_peer) {
sort_peers(device); sort_peers(device);
terminal_printf("\n"); terminal_printf("\n");
@ -287,6 +306,16 @@ static void dump_print(struct wgdevice *device, bool with_interface)
printf("%u\t", device->response_packet_magic_header); printf("%u\t", device->response_packet_magic_header);
printf("%u\t", device->underload_packet_magic_header); printf("%u\t", device->underload_packet_magic_header);
printf("%u\t", device->transport_packet_magic_header); printf("%u\t", device->transport_packet_magic_header);
printf("%s\t", device->i1);
printf("%s\t", device->i2);
printf("%s\t", device->i3);
printf("%s\t", device->i4);
printf("%s\t", device->i5);
printf("%s\t", device->j1);
printf("%s\t", device->j2);
printf("%s\t", device->j3);
printf("%d\t", device->itime);
if (device->fwmark) if (device->fwmark)
printf("0x%x\n", device->fwmark); printf("0x%x\n", device->fwmark);
else else
@ -374,6 +403,42 @@ static bool ugly_print(struct wgdevice *device, const char *param, bool with_int
if (with_interface) if (with_interface)
printf("%s\t", device->name); printf("%s\t", device->name);
printf("%u\n", device->transport_packet_magic_header); printf("%u\n", device->transport_packet_magic_header);
} else if(!strcmp(param, "i1")) {
if (with_interface)
printf("%s\t", device->name);
printf("%s\n", device->i1);
} else if(!strcmp(param, "i2")) {
if (with_interface)
printf("%s\t", device->name);
printf("%s\n", device->i2);
} else if(!strcmp(param, "i3")) {
if (with_interface)
printf("%s\t", device->name);
printf("%s\n", device->i3);
} else if(!strcmp(param, "i4")) {
if (with_interface)
printf("%s\t", device->name);
printf("%s\n", device->i4);
} else if(!strcmp(param, "i5")) {
if (with_interface)
printf("%s\t", device->name);
printf("%s\n", device->i5);
} else if(!strcmp(param, "j1")) {
if (with_interface)
printf("%s\t", device->name);
printf("%s\n", device->j1);
} else if(!strcmp(param, "j2")) {
if (with_interface)
printf("%s\t", device->name);
printf("%s\n", device->j2);
} else if(!strcmp(param, "j3")) {
if (with_interface)
printf("%s\t", device->name);
printf("%s\n", device->j3);
} else if(!strcmp(param, "itime")) {
if (with_interface)
printf("%s\t", device->name);
printf("%d\n", device->itime);
} else if (!strcmp(param, "endpoints")) { } else if (!strcmp(param, "endpoints")) {
for_each_wgpeer(device, peer) { for_each_wgpeer(device, peer) {
if (with_interface) if (with_interface)

View file

@ -64,7 +64,6 @@ int showconf_main(int argc, const char *argv[])
printf("H3 = %u\n", device->underload_packet_magic_header); printf("H3 = %u\n", device->underload_packet_magic_header);
if (device->flags & WGDEVICE_HAS_H4) if (device->flags & WGDEVICE_HAS_H4)
printf("H4 = %u\n", device->transport_packet_magic_header); printf("H4 = %u\n", device->transport_packet_magic_header);
if (device->flags & WGDEVICE_HAS_I1) if (device->flags & WGDEVICE_HAS_I1)
printf("I1 = %s\n", device->i1); printf("I1 = %s\n", device->i1);
if (device->flags & WGDEVICE_HAS_I2) if (device->flags & WGDEVICE_HAS_I2)
@ -82,7 +81,7 @@ int showconf_main(int argc, const char *argv[])
if (device->flags & WGDEVICE_HAS_J3) if (device->flags & WGDEVICE_HAS_J3)
printf("J3 = %s\n", device->j3); printf("J3 = %s\n", device->j3);
if (device->flags & WGDEVICE_HAS_ITIME) if (device->flags & WGDEVICE_HAS_ITIME)
printf("Itime = %s\n", device->itime); printf("Itime = %d\n", device->itime);
printf("\n"); printf("\n");
for_each_wgpeer(device, peer) { for_each_wgpeer(device, peer) {

View file

@ -1278,6 +1278,24 @@ static void parse_options(char **iface, char **config, unsigned int *mtu, char *
is_asecurity_on = true; is_asecurity_on = true;
} else if (!strncasecmp(clean, "H4=", 3) && j > 4) { } else if (!strncasecmp(clean, "H4=", 3) && j > 4) {
is_asecurity_on = true; is_asecurity_on = true;
} else if (!strncasecmp(clean, "I1", 3) && j > 4) {
is_special_handshake_on = true;
} else if (!strncasecmp(clean, "I2", 3) && j > 4) {
is_special_handshake_on = true;
} else if (!strncasecmp(clean, "I3", 3) && j > 4) {
is_special_handshake_on = true;
} else if (!strncasecmp(clean, "I4", 3) && j > 4) {
is_special_handshake_on = true;
} else if (!strncasecmp(clean, "I5", 3) && j > 4) {
is_special_handshake_on = true;
} else if (!strncasecmp(clean, "J1", 3) && j > 4) {
is_special_handshake_on = true;
} else if (!strncasecmp(clean, "J2", 3) && j > 4) {
is_special_handshake_on = true;
} else if (!strncasecmp(clean, "J3", 3) && j > 4) {
is_special_handshake_on = true;
} else if (!strncasecmp(clean, "Itime", 3) && j > 4) {
is_special_handshake_on = true;
} }
} }
*config = concat_and_free(*config, "", line); *config = concat_and_free(*config, "", line);
@ -1322,4 +1340,4 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
return 0; return 0;
} }