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
[[ -z $last_device ]] && printf '\n' || printf '%s,\n' "$end"
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"
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'; }
[[ $h3 == "3" ]] || { printf '%s\t\t"h3": %u' "$delim" $(( $h3 )); 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'; }
printf '%s\t\t"peers": {' "$delim"; end=$'\n\t\t}\n\t}'
delim=$'\n'

View file

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

View file

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

View file

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

View file

@ -3,370 +3,521 @@
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
#include "containers.h"
#include <errno.h>
#include <net/if.h>
#include <net/if_wg.h>
#include <netinet/in.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/types.h>
#include <net/if.h>
#include <net/if_wg.h>
#include <netinet/in.h>
#include "containers.h"
#include <time.h>
#define IPC_SUPPORTS_KERNEL_INTERFACE
static int get_dgram_socket(void)
{
static int sock = -1;
if (sock < 0)
sock = socket(AF_INET, SOCK_DGRAM, 0);
return sock;
static int sock = -1;
if (sock < 0)
sock = socket(AF_INET, SOCK_DGRAM, 0);
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 ifg_req *ifg;
int s = get_dgram_socket(), ret = 0;
struct ifgroupreq ifgr = {.ifgr_name = "wg"};
struct ifg_req* ifg;
int s = get_dgram_socket(), ret = 0;
if (s < 0)
return -errno;
if (s < 0)
return -errno;
if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0)
return errno == ENOENT ? 0 : -errno;
if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0)
return errno == ENOENT ? 0 : -errno;
ifgr.ifgr_groups = calloc(1, ifgr.ifgr_len);
if (!ifgr.ifgr_groups)
return -errno;
if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0) {
ret = -errno;
goto out;
}
ifgr.ifgr_groups = calloc(1, ifgr.ifgr_len);
if (!ifgr.ifgr_groups)
return -errno;
if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0)
{
ret = -errno;
goto out;
}
for (ifg = ifgr.ifgr_groups; ifg && ifgr.ifgr_len > 0; ++ifg) {
if ((ret = string_list_add(list, ifg->ifgrq_member)) < 0)
goto out;
ifgr.ifgr_len -= sizeof(struct ifg_req);
}
for (ifg = ifgr.ifgr_groups; ifg && ifgr.ifgr_len > 0; ++ifg)
{
if ((ret = string_list_add(list, ifg->ifgrq_member)) < 0)
goto out;
ifgr.ifgr_len -= sizeof(struct ifg_req);
}
out:
free(ifgr.ifgr_groups);
return ret;
free(ifgr.ifgr_groups);
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_interface_io *wg_iface;
struct wg_peer_io *wg_peer;
struct wg_aip_io *wg_aip;
struct wgdevice *dev;
struct wgpeer *peer;
struct wgallowedip *aip;
int s = get_dgram_socket(), ret;
struct wg_data_io wgdata = {.wgd_size = 0};
struct wg_interface_io* wg_iface;
struct wg_peer_io* wg_peer;
struct wg_aip_io* wg_aip;
struct wgdevice* dev;
struct wgpeer* peer;
struct wgallowedip* aip;
int s = get_dgram_socket(), ret;
if (s < 0)
return -errno;
if (s < 0)
return -errno;
*device = NULL;
strlcpy(wgdata.wgd_name, iface, sizeof(wgdata.wgd_name));
for (size_t last_size = wgdata.wgd_size;; last_size = wgdata.wgd_size) {
if (ioctl(s, SIOCGWG, (caddr_t)&wgdata) < 0)
goto out;
if (last_size >= wgdata.wgd_size)
break;
wgdata.wgd_interface = realloc(wgdata.wgd_interface, wgdata.wgd_size);
if (!wgdata.wgd_interface)
goto out;
}
*device = NULL;
strlcpy(wgdata.wgd_name, iface, sizeof(wgdata.wgd_name));
for (size_t last_size = wgdata.wgd_size;; last_size = wgdata.wgd_size)
{
if (ioctl(s, SIOCGWG, (caddr_t)&wgdata) < 0)
goto out;
if (last_size >= wgdata.wgd_size)
break;
wgdata.wgd_interface = realloc(wgdata.wgd_interface, wgdata.wgd_size);
if (!wgdata.wgd_interface)
goto out;
}
wg_iface = wgdata.wgd_interface;
dev = calloc(1, sizeof(*dev));
if (!dev)
goto out;
strlcpy(dev->name, iface, sizeof(dev->name));
wg_iface = wgdata.wgd_interface;
dev = calloc(1, sizeof(*dev));
if (!dev)
goto out;
strlcpy(dev->name, iface, sizeof(dev->name));
if (wg_iface->i_flags & WG_INTERFACE_HAS_RTABLE) {
dev->fwmark = wg_iface->i_rtable;
dev->flags |= WGDEVICE_HAS_FWMARK;
}
if (wg_iface->i_flags & WG_INTERFACE_HAS_RTABLE)
{
dev->fwmark = wg_iface->i_rtable;
dev->flags |= WGDEVICE_HAS_FWMARK;
}
if (wg_iface->i_flags & WG_INTERFACE_HAS_PORT) {
dev->listen_port = wg_iface->i_port;
dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
}
if (wg_iface->i_flags & WG_INTERFACE_HAS_PORT)
{
dev->listen_port = wg_iface->i_port;
dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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 (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;
}
if (dev->first_peer == NULL)
dev->first_peer = peer;
else
dev->last_peer->next_peer = peer;
dev->last_peer = peer;
if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_I1)
{
wg_iface->i_i1 = strdup(dev->i1);
wg_iface->i_flags |= WGDEVICE_HAS_I1;
}
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_iface->i_flags & WG_INTERFACE_DEVICE_HAS_I2)
{
wg_iface->i_i2 = strdup(dev->i2);
wg_iface->i_flags |= WGDEVICE_HAS_I2;
}
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_iface->i_flags & WG_INTERFACE_DEVICE_HAS_I3)
{
wg_iface->i_i3 = strdup(dev->i3);
wg_iface->i_flags |= WGDEVICE_HAS_I3;
}
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_iface->i_flags & WG_INTERFACE_DEVICE_HAS_I4)
{
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))
memcpy(&peer->endpoint.addr, &wg_peer->p_sa, wg_peer->p_sa.sa_len);
if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_I5)
{
wg_iface->i_i5 = strdup(dev->i5);
wg_iface->i_flags |= WGDEVICE_HAS_I5;
}
peer->rx_bytes = wg_peer->p_rxbytes;
peer->tx_bytes = wg_peer->p_txbytes;
if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_J1)
{
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;
peer->last_handshake_time.tv_nsec = wg_peer->p_last_handshake.tv_nsec;
if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_J2)
{
wg_iface->i_j2 = strdup(dev->j2);
wg_iface->i_flags |= WGDEVICE_HAS_J2;
}
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 (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_J3)
{
wg_iface->i_j3 = strdup(dev->j3);
wg_iface->i_flags |= WGDEVICE_HAS_J3;
}
if (peer->first_allowedip == NULL)
peer->first_allowedip = aip;
else
peer->last_allowedip->next_allowedip = aip;
peer->last_allowedip = aip;
if (wg_iface->i_flags & WG_INTERFACE_DEVICE_HAS_ITIME)
{
wg_iface->i_itime = dev->itime;
wg_iface->i_flags |= WGDEVICE_HAS_ITIME;
}
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;
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)
dev->first_peer = peer;
else
dev->last_peer->next_peer = peer;
dev->last_peer = peer;
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:
ret = -errno;
free(wgdata.wgd_interface);
return ret;
ret = -errno;
free(wgdata.wgd_interface);
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_interface_io *wg_iface;
struct wg_peer_io *wg_peer;
struct wg_aip_io *wg_aip;
struct wgpeer *peer;
struct wgallowedip *aip;
int s = get_dgram_socket(), ret;
size_t peer_count, aip_count;
struct wg_data_io wgdata = {.wgd_size = sizeof(struct wg_interface_io)};
struct wg_interface_io* wg_iface;
struct wg_peer_io* wg_peer;
struct wg_aip_io* wg_aip;
struct wgpeer* peer;
struct wgallowedip* aip;
int s = get_dgram_socket(), ret;
size_t peer_count, aip_count;
if (s < 0)
return -errno;
if (s < 0)
return -errno;
for_each_wgpeer(dev, peer) {
wgdata.wgd_size += sizeof(struct wg_peer_io);
for_each_wgallowedip(peer, aip)
wgdata.wgd_size += sizeof(struct wg_aip_io);
}
wg_iface = wgdata.wgd_interface = calloc(1, wgdata.wgd_size);
if (!wgdata.wgd_interface)
return -errno;
strlcpy(wgdata.wgd_name, dev->name, sizeof(wgdata.wgd_name));
for_each_wgpeer(dev, peer)
{
wgdata.wgd_size += sizeof(struct wg_peer_io);
for_each_wgallowedip(peer, aip) wgdata.wgd_size += sizeof(struct wg_aip_io);
}
wg_iface = wgdata.wgd_interface = calloc(1, wgdata.wgd_size);
if (!wgdata.wgd_interface)
return -errno;
strlcpy(wgdata.wgd_name, dev->name, sizeof(wgdata.wgd_name));
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;
}
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;
}
if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) {
wg_iface->i_port = dev->listen_port;
wg_iface->i_flags |= WG_INTERFACE_HAS_PORT;
}
if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
{
wg_iface->i_port = dev->listen_port;
wg_iface->i_flags |= WG_INTERFACE_HAS_PORT;
}
if (dev->flags & WGDEVICE_HAS_FWMARK) {
wg_iface->i_rtable = dev->fwmark;
wg_iface->i_flags |= WG_INTERFACE_HAS_RTABLE;
}
if (dev->flags & WGDEVICE_HAS_FWMARK)
{
wg_iface->i_rtable = dev->fwmark;
wg_iface->i_flags |= WG_INTERFACE_HAS_RTABLE;
}
if (dev->flags & WGDEVICE_REPLACE_PEERS)
wg_iface->i_flags |= WG_INTERFACE_REPLACE_PEERS;
if (dev->flags & WGDEVICE_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) {
wg_iface->i_junk_packet_count = dev->junk_packet_count;
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_JC;
}
if (dev->flags & WGDEVICE_HAS_JMIN)
{
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) {
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_JMAX)
{
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) {
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_S1)
{
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) {
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_S2)
{
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) {
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_H1)
{
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) {
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_H2)
{
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) {
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_H3)
{
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) {
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_H4)
{
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) {
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_I1)
{
wg_iface->i_i1 = strdup(dev->i1);
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_I1;
}
peer_count = 0;
wg_peer = &wg_iface->i_peers[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 (dev->flags & WGDEVICE_HAS_I2)
{
wg_iface->i_i2 = strdup(dev->i2);
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_I2;
}
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 (dev->flags & WGDEVICE_HAS_I3)
{
wg_iface->i_i3 = strdup(dev->i3);
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_I3;
}
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 (dev->flags & WGDEVICE_HAS_I4)
{
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) &&
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 (dev->flags & WGDEVICE_HAS_I5)
{
wg_iface->i_i5 = strdup(dev->i5);
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_I5;
}
if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
wg_peer->p_flags |= WG_PEER_REPLACE_AIPS;
if (dev->flags & WGDEVICE_HAS_J1)
{
wg_iface->i_j1 = strdup(dev->j1);
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_J1;
}
if (peer->flags & WGPEER_REMOVE_ME)
wg_peer->p_flags |= WG_PEER_REMOVE;
if (dev->flags & WGDEVICE_HAS_J2)
{
wg_iface->i_j2 = strdup(dev->j2);
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_J2;
}
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 (dev->flags & WGDEVICE_HAS_J3)
{
wg_iface->i_j3 = strdup(dev->j3);
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_J3;
}
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 (dev->flags & WGDEVICE_HAS_ITIME)
{
wg_iface->i_itime = dev->itime;
wg_iface->i_flags |= WG_INTERFACE_DEVICE_HAS_ITIME;
}
if (ioctl(s, SIOCSWG, (caddr_t)&wgdata) < 0)
goto out;
errno = 0;
peer_count = 0;
wg_peer = &wg_iface->i_peers[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:
ret = -errno;
free(wgdata.wgd_interface);
return ret;
ret = -errno;
free(wgdata.wgd_interface);
return ret;
}

View file

@ -3,6 +3,10 @@
* 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 <errno.h>
#include <limits.h>
@ -14,10 +18,6 @@
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include "containers.h"
#include "curve25519.h"
#include "encoding.h"
#include "ctype.h"
#ifdef _WIN32
#include "ipc-uapi-windows.h"
@ -25,322 +25,460 @@
#include "ipc-uapi-unix.h"
#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];
struct wgpeer *peer;
struct wgallowedip *allowedip;
FILE *f;
int ret, set_errno = -EPROTO;
socklen_t addr_len;
size_t line_buffer_len = 0, line_len;
char *key = NULL, *value;
char hex[WG_KEY_LEN_HEX], ip[INET6_ADDRSTRLEN], host[4096 + 1], service[512 + 1];
struct wgpeer* peer;
struct wgallowedip* allowedip;
FILE* f;
int ret, set_errno = -EPROTO;
socklen_t addr_len;
size_t line_buffer_len = 0, line_len;
char * key = NULL, *value;
f = userspace_interface_file(dev->name);
if (!f)
return -errno;
fprintf(f, "set=1\n");
f = userspace_interface_file(dev->name);
if (!f)
return -errno;
fprintf(f, "set=1\n");
if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) {
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_FWMARK)
fprintf(f, "fwmark=%u\n", dev->fwmark);
if (dev->flags & WGDEVICE_REPLACE_PEERS)
fprintf(f, "replace_peers=true\n");
if (dev->flags & WGDEVICE_HAS_JC)
fprintf(f, "jc=%u\n", dev->junk_packet_count);
if (dev->flags & WGDEVICE_HAS_JMIN)
fprintf(f, "jmin=%u\n", dev->junk_packet_min_size);
if (dev->flags & WGDEVICE_HAS_JMAX)
fprintf(f, "jmax=%u\n", dev->junk_packet_max_size);
if (dev->flags & WGDEVICE_HAS_S1)
fprintf(f, "s1=%u\n", dev->init_packet_junk_size);
if (dev->flags & WGDEVICE_HAS_S2)
fprintf(f, "s2=%u\n", dev->response_packet_junk_size);
if (dev->flags & WGDEVICE_HAS_H1)
fprintf(f, "h1=%u\n", dev->init_packet_magic_header);
if (dev->flags & WGDEVICE_HAS_H2)
fprintf(f, "h2=%u\n", dev->response_packet_magic_header);
if (dev->flags & WGDEVICE_HAS_H3)
fprintf(f, "h3=%u\n", dev->underload_packet_magic_header);
if (dev->flags & WGDEVICE_HAS_H4)
fprintf(f, "h4=%u\n", dev->transport_packet_magic_header);
if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)
{
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_FWMARK)
fprintf(f, "fwmark=%u\n", dev->fwmark);
if (dev->flags & WGDEVICE_REPLACE_PEERS)
fprintf(f, "replace_peers=true\n");
if (dev->flags & WGDEVICE_HAS_JC)
fprintf(f, "jc=%u\n", dev->junk_packet_count);
if (dev->flags & WGDEVICE_HAS_JMIN)
fprintf(f, "jmin=%u\n", dev->junk_packet_min_size);
if (dev->flags & WGDEVICE_HAS_JMAX)
fprintf(f, "jmax=%u\n", dev->junk_packet_max_size);
if (dev->flags & WGDEVICE_HAS_S1)
fprintf(f, "s1=%u\n", dev->init_packet_junk_size);
if (dev->flags & WGDEVICE_HAS_S2)
fprintf(f, "s2=%u\n", dev->response_packet_junk_size);
if (dev->flags & WGDEVICE_HAS_H1)
fprintf(f, "h1=%u\n", dev->init_packet_magic_header);
if (dev->flags & WGDEVICE_HAS_H2)
fprintf(f, "h2=%u\n", dev->response_packet_magic_header);
if (dev->flags & WGDEVICE_HAS_H3)
fprintf(f, "h3=%u\n", dev->underload_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) {
key_to_hex(hex, peer->public_key);
fprintf(f, "public_key=%s\n", hex);
if (peer->flags & WGPEER_HAS_ADVANCED_SECURITY) {
ret = -EINVAL;
goto out;
}
if (peer->flags & WGPEER_REMOVE_ME) {
fprintf(f, "remove=true\n");
continue;
}
if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
key_to_hex(hex, peer->preshared_key);
fprintf(f, "preshared_key=%s\n", hex);
}
if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) {
addr_len = 0;
if (peer->endpoint.addr.sa_family == AF_INET)
addr_len = sizeof(struct sockaddr_in);
else if (peer->endpoint.addr.sa_family == AF_INET6)
addr_len = sizeof(struct sockaddr_in6);
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, ':'))
fprintf(f, "endpoint=[%s]:%s\n", host, service);
else
fprintf(f, "endpoint=%s:%s\n", host, service);
}
}
if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL)
fprintf(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);
for_each_wgpeer(dev, peer)
{
key_to_hex(hex, peer->public_key);
fprintf(f, "public_key=%s\n", hex);
if (peer->flags & WGPEER_HAS_ADVANCED_SECURITY)
{
ret = -EINVAL;
goto out;
}
if (peer->flags & WGPEER_REMOVE_ME)
{
fprintf(f, "remove=true\n");
continue;
}
if (peer->flags & WGPEER_HAS_PRESHARED_KEY)
{
key_to_hex(hex, peer->preshared_key);
fprintf(f, "preshared_key=%s\n", hex);
}
if (peer->endpoint.addr.sa_family == AF_INET ||
peer->endpoint.addr.sa_family == AF_INET6)
{
addr_len = 0;
if (peer->endpoint.addr.sa_family == AF_INET)
addr_len = sizeof(struct sockaddr_in);
else if (peer->endpoint.addr.sa_family == AF_INET6)
addr_len = sizeof(struct sockaddr_in6);
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, ':'))
fprintf(f, "endpoint=[%s]:%s\n", host, service);
else
fprintf(f, "endpoint=%s:%s\n", host, service);
}
}
if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL)
fprintf(
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) {
line_len = strlen(key);
ret = set_errno;
if (line_len == 1 && key[0] == '\n')
goto out;
value = strchr(key, '=');
if (!value || line_len == 0 || key[line_len - 1] != '\n')
break;
*value++ = key[--line_len] = '\0';
while (getline(&key, &line_buffer_len, f) > 0)
{
line_len = strlen(key);
ret = set_errno;
if (line_len == 1 && key[0] == '\n')
goto out;
value = strchr(key, '=');
if (!value || line_len == 0 || key[line_len - 1] != '\n')
break;
*value++ = key[--line_len] = '\0';
if (!strcmp(key, "errno")) {
long long num;
char *end;
if (value[0] != '-' && !char_is_digit(value[0]))
break;
num = strtoll(value, &end, 10);
if (*end || num > INT_MAX || num < INT_MIN)
break;
set_errno = num;
}
}
ret = errno ? -errno : -EPROTO;
if (!strcmp(key, "errno"))
{
long long num;
char* end;
if (value[0] != '-' && !char_is_digit(value[0]))
break;
num = strtoll(value, &end, 10);
if (*end || num > INT_MAX || num < INT_MIN)
break;
set_errno = num;
}
}
ret = errno ? -errno : -EPROTO;
out:
free(key);
fclose(f);
errno = -ret;
return ret;
free(key);
fclose(f);
errno = -ret;
return ret;
}
#define NUM(max) ({ \
unsigned long long num; \
char *end; \
if (!char_is_digit(value[0])) \
break; \
num = strtoull(value, &end, 10); \
if (*end || num > max) \
break; \
num; \
})
#define NUM(max) \
({ \
unsigned long long num; \
char* end; \
if (!char_is_digit(value[0])) \
break; \
num = strtoull(value, &end, 10); \
if (*end || num > max) \
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 wgpeer *peer = NULL;
struct wgallowedip *allowedip = NULL;
size_t line_buffer_len = 0, line_len;
char *key = NULL, *value;
FILE *f;
int ret = -EPROTO;
struct wgdevice* dev;
struct wgpeer* peer = NULL;
struct wgallowedip* allowedip = NULL;
size_t line_buffer_len = 0, line_len;
char * key = NULL, *value;
FILE* f;
int ret = -EPROTO;
*out = dev = calloc(1, sizeof(*dev));
if (!dev)
return -errno;
*out = dev = calloc(1, sizeof(*dev));
if (!dev)
return -errno;
f = userspace_interface_file(iface);
if (!f) {
ret = -errno;
free(dev);
*out = NULL;
return ret;
}
f = userspace_interface_file(iface);
if (!f)
{
ret = -errno;
free(dev);
*out = NULL;
return ret;
}
fprintf(f, "get=1\n\n");
fflush(f);
fprintf(f, "get=1\n\n");
fflush(f);
strncpy(dev->name, iface, IFNAMSIZ - 1);
dev->name[IFNAMSIZ - 1] = '\0';
strncpy(dev->name, iface, IFNAMSIZ - 1);
dev->name[IFNAMSIZ - 1] = '\0';
while (getline(&key, &line_buffer_len, f) > 0) {
line_len = strlen(key);
if (line_len == 1 && key[0] == '\n')
goto err;
value = strchr(key, '=');
if (!value || line_len == 0 || key[line_len - 1] != '\n')
break;
*value++ = key[--line_len] = '\0';
while (getline(&key, &line_buffer_len, f) > 0)
{
line_len = strlen(key);
if (line_len == 1 && key[0] == '\n')
goto err;
value = strchr(key, '=');
if (!value || line_len == 0 || key[line_len - 1] != '\n')
break;
*value++ = key[--line_len] = '\0';
if (!peer && !strcmp(key, "private_key")) {
if (!key_from_hex(dev->private_key, value))
break;
curve25519_generate_public(dev->public_key, dev->private_key);
dev->flags |= WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_PUBLIC_KEY;
} else if (!peer && !strcmp(key, "listen_port")) {
dev->listen_port = NUM(0xffffU);
dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
} else if (!peer && !strcmp(key, "fwmark")) {
dev->fwmark = NUM(0xffffffffU);
dev->flags |= WGDEVICE_HAS_FWMARK;
} else if(!peer && !strcmp(key, "jc")) {
dev->junk_packet_count = NUM(0xffffU);
dev->flags |= WGDEVICE_HAS_JC;
} else if(!peer && !strcmp(key, "jmin")) {
dev->junk_packet_min_size = NUM(0xffffU);
dev->flags |= WGDEVICE_HAS_JMIN;
} else if(!peer && !strcmp(key, "jmax")) {
dev->junk_packet_max_size = NUM(0xffffU);
dev->flags |= WGDEVICE_HAS_JMAX;
} else if(!peer && !strcmp(key, "s1")) {
dev->init_packet_junk_size = NUM(0xffffU);
dev->flags |= WGDEVICE_HAS_S1;
} else if(!peer && !strcmp(key, "s2")) {
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 (!strcmp(key, "public_key")) {
struct wgpeer *new_peer = calloc(1, sizeof(*new_peer));
if (!peer && !strcmp(key, "private_key"))
{
if (!key_from_hex(dev->private_key, value))
break;
curve25519_generate_public(dev->public_key, dev->private_key);
dev->flags |= WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_PUBLIC_KEY;
}
else if (!peer && !strcmp(key, "listen_port"))
{
dev->listen_port = NUM(0xffffU);
dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
}
else if (!peer && !strcmp(key, "fwmark"))
{
dev->fwmark = NUM(0xffffffffU);
dev->flags |= WGDEVICE_HAS_FWMARK;
}
else if (!peer && !strcmp(key, "jc"))
{
dev->junk_packet_count = NUM(0xffffU);
dev->flags |= WGDEVICE_HAS_JC;
}
else if (!peer && !strcmp(key, "jmin"))
{
dev->junk_packet_min_size = NUM(0xffffU);
dev->flags |= WGDEVICE_HAS_JMIN;
}
else if (!peer && !strcmp(key, "jmax"))
{
dev->junk_packet_max_size = NUM(0xffffU);
dev->flags |= WGDEVICE_HAS_JMAX;
}
else if (!peer && !strcmp(key, "s1"))
{
dev->init_packet_junk_size = NUM(0xffffU);
dev->flags |= WGDEVICE_HAS_S1;
}
else if (!peer && !strcmp(key, "s2"))
{
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) {
ret = -ENOMEM;
goto err;
}
allowedip = NULL;
if (peer)
peer->next_peer = new_peer;
else
dev->first_peer = new_peer;
peer = new_peer;
if (!key_from_hex(peer->public_key, value))
break;
peer->flags |= WGPEER_HAS_PUBLIC_KEY;
} else if (peer && !strcmp(key, "preshared_key")) {
if (!key_from_hex(peer->preshared_key, value))
break;
if (!key_is_zero(peer->preshared_key))
peer->flags |= WGPEER_HAS_PRESHARED_KEY;
} else if (peer && !strcmp(key, "endpoint")) {
char *begin, *end;
struct addrinfo *resolved;
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_DGRAM,
.ai_protocol = IPPROTO_UDP
};
if (!strlen(value))
break;
if (value[0] == '[') {
begin = &value[1];
end = strchr(value, ']');
if (!end)
break;
*end++ = '\0';
if (*end++ != ':' || !*end)
break;
} else {
begin = value;
end = strrchr(value, ':');
if (!end || !*(end + 1))
break;
*end++ = '\0';
}
if (getaddrinfo(begin, end, &hints, &resolved) != 0) {
ret = ENETUNREACH;
goto err;
}
if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) ||
(resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)))
memcpy(&peer->endpoint.addr, resolved->ai_addr, resolved->ai_addrlen);
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 (!new_peer)
{
ret = -ENOMEM;
goto err;
}
allowedip = NULL;
if (peer)
peer->next_peer = new_peer;
else
dev->first_peer = new_peer;
peer = new_peer;
if (!key_from_hex(peer->public_key, value))
break;
peer->flags |= WGPEER_HAS_PUBLIC_KEY;
}
else if (peer && !strcmp(key, "preshared_key"))
{
if (!key_from_hex(peer->preshared_key, value))
break;
if (!key_is_zero(peer->preshared_key))
peer->flags |= WGPEER_HAS_PRESHARED_KEY;
}
else if (peer && !strcmp(key, "endpoint"))
{
char * begin, *end;
struct addrinfo* resolved;
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_DGRAM,
.ai_protocol = IPPROTO_UDP};
if (!strlen(value))
break;
if (value[0] == '[')
{
begin = &value[1];
end = strchr(value, ']');
if (!end)
break;
*end++ = '\0';
if (*end++ != ':' || !*end)
break;
}
else
{
begin = value;
end = strrchr(value, ':');
if (!end || !*(end + 1))
break;
*end++ = '\0';
}
if (getaddrinfo(begin, end, &hints, &resolved) != 0)
{
ret = ENETUNREACH;
goto err;
}
if ((resolved->ai_family == AF_INET &&
resolved->ai_addrlen == sizeof(struct sockaddr_in)) ||
(resolved->ai_family == AF_INET6 &&
resolved->ai_addrlen == sizeof(struct sockaddr_in6)))
memcpy(&peer->endpoint.addr, resolved->ai_addr, resolved->ai_addrlen);
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]))
break;
new_allowedip = calloc(1, sizeof(*new_allowedip));
if (!new_allowedip) {
ret = -ENOMEM;
goto err;
}
if (allowedip)
allowedip->next_allowedip = new_allowedip;
else
peer->first_allowedip = new_allowedip;
allowedip = new_allowedip;
allowedip->family = AF_UNSPEC;
if (strchr(ip, ':')) {
if (inet_pton(AF_INET6, ip, &allowedip->ip6) == 1)
allowedip->family = AF_INET6;
} else {
if (inet_pton(AF_INET, ip, &allowedip->ip4) == 1)
allowedip->family = AF_INET;
}
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))
break;
} else if (peer && !strcmp(key, "last_handshake_time_sec"))
peer->last_handshake_time.tv_sec = NUM(0x7fffffffffffffffULL);
else if (peer && !strcmp(key, "last_handshake_time_nsec"))
peer->last_handshake_time.tv_nsec = NUM(0x7fffffffffffffffULL);
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;
if (!mask || !char_is_digit(mask[0]))
break;
new_allowedip = calloc(1, sizeof(*new_allowedip));
if (!new_allowedip)
{
ret = -ENOMEM;
goto err;
}
if (allowedip)
allowedip->next_allowedip = new_allowedip;
else
peer->first_allowedip = new_allowedip;
allowedip = new_allowedip;
allowedip->family = AF_UNSPEC;
if (strchr(ip, ':'))
{
if (inet_pton(AF_INET6, ip, &allowedip->ip6) == 1)
allowedip->family = AF_INET6;
}
else
{
if (inet_pton(AF_INET, ip, &allowedip->ip4) == 1)
allowedip->family = AF_INET;
}
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))
break;
}
else if (peer && !strcmp(key, "last_handshake_time_sec"))
peer->last_handshake_time.tv_sec = NUM(0x7fffffffffffffffULL);
else if (peer && !strcmp(key, "last_handshake_time_nsec"))
peer->last_handshake_time.tv_nsec = NUM(0x7fffffffffffffffULL);
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:
free(key);
if (ret) {
free_wgdevice(dev);
*out = NULL;
}
fclose(f);
errno = -ret;
return ret;
free(key);
if (ret)
{
free_wgdevice(dev);
*out = NULL;
}
fclose(f);
errno = -ret;
return ret;
}
#undef NUM

View file

@ -18,7 +18,7 @@ int set_main(int argc, const char *argv[])
int ret = 1;
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;
}

View file

@ -202,7 +202,7 @@ static char *bytes(uint64_t b)
static const char *COMMAND_NAME;
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)
@ -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);
if (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) {
sort_peers(device);
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->underload_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)
printf("0x%x\n", device->fwmark);
else
@ -374,6 +403,42 @@ static bool ugly_print(struct wgdevice *device, const char *param, bool with_int
if (with_interface)
printf("%s\t", device->name);
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")) {
for_each_wgpeer(device, peer) {
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);
if (device->flags & WGDEVICE_HAS_H4)
printf("H4 = %u\n", device->transport_packet_magic_header);
if (device->flags & WGDEVICE_HAS_I1)
printf("I1 = %s\n", device->i1);
if (device->flags & WGDEVICE_HAS_I2)
@ -82,7 +81,7 @@ int showconf_main(int argc, const char *argv[])
if (device->flags & WGDEVICE_HAS_J3)
printf("J3 = %s\n", device->j3);
if (device->flags & WGDEVICE_HAS_ITIME)
printf("Itime = %s\n", device->itime);
printf("Itime = %d\n", device->itime);
printf("\n");
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;
} else if (!strncasecmp(clean, "H4=", 3) && j > 4) {
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);
@ -1322,4 +1340,4 @@ int main(int argc, char *argv[])
return 1;
}
return 0;
}
}