diff --git a/contrib/wireguard-tools/config.c b/contrib/wireguard-tools/config.c --- a/contrib/wireguard-tools/config.c +++ b/contrib/wireguard-tools/config.c @@ -433,6 +433,78 @@ return false; } +static inline bool parse_uint16(uint16_t *device_value, const char *name, const char *value) { + + if (!strlen(value)) { + fprintf(stderr, "Unable to parse empty string\n"); + return false; + } + + char *end; + uint32_t ret; + ret = strtoul(value, &end, 10); + + if (*end || ret > UINT16_MAX) { + fprintf(stderr, "Unable to parse %s: `%s'\n", name, value); + exit(1); + } + *device_value = (uint16_t)ret; + return true; +} + +static inline bool parse_uint32(uint32_t *device_value, const char *name, const char *value) { + + if (!strlen(value)) { + fprintf(stderr, "Unable to parse empty string\n"); + return false; + } + + char *end; + uint64_t ret; + ret = strtoul(value, &end, 10); + if (*end || ret > UINT32_MAX) { + fprintf(stderr, "Unable to parse %s: `%s'\n", name, value); + exit(1); + } + *device_value = (uint32_t)ret; + return true; +} + +static inline bool parse_bool(bool *device_value, const char *name, const char *value) { + + if (!strlen(value)) { + fprintf(stderr, "Unable to parse empty string\n"); + return false; + } + + if (!strcasecmp(value, "off")) { + *device_value = false; + return true; + } + + if (!strcasecmp(value, "on")) { + *device_value = true; + return true; + } + + if (!char_is_digit(value[0])) + goto err; + + char *end; + uint32_t ret; + ret = strtoul(value, &end, 10); + + if (*end) { + fprintf(stderr, "Unable to parse %s: `%s'\n", name, value); + exit(1); + } + *device_value = ret != 0; + return true; +err: + fprintf(stderr, "Boolean value is neither on/off nor 0/1: `%s'\n", value); + return false; +} + static bool process_line(struct config_ctx *ctx, const char *line) { const char *value; @@ -473,6 +545,42 @@ ret = parse_key(ctx->device->private_key, value); if (ret) ctx->device->flags |= WGDEVICE_HAS_PRIVATE_KEY; + } else if (key_match("Jc")) { + ret = parse_uint16(&ctx->device->junk_packet_count, "Jc", value); + if (ret) + ctx->device->flags |= WGDEVICE_HAS_JC; + } else if (key_match("Jmin")) { + ret = parse_uint16(&ctx->device->junk_packet_min_size, "Jmin", value); + if (ret) + ctx->device->flags |= WGDEVICE_HAS_JMIN; + } else if (key_match("Jmax")) { + ret = parse_uint16(&ctx->device->junk_packet_max_size, "Jmax", value); + if (ret) + ctx->device->flags |= WGDEVICE_HAS_JMAX; + } else if (key_match("S1")) { + ret = parse_uint16(&ctx->device->init_packet_junk_size, "S1", value); + if (ret) + ctx->device->flags |= WGDEVICE_HAS_S1; + } else if (key_match("S2")) { + ret = parse_uint16(&ctx->device->response_packet_junk_size, "S2", value); + if (ret) + ctx->device->flags |= WGDEVICE_HAS_S2; + } else if (key_match("H1")) { + ret = parse_uint32(&ctx->device->init_packet_magic_header, "H1", value); + if (ret) + ctx->device->flags |= WGDEVICE_HAS_H1; + } else if (key_match("H2")) { + ret = parse_uint32(&ctx->device->response_packet_magic_header, "H2", value); + if (ret) + ctx->device->flags |= WGDEVICE_HAS_H2; + } else if (key_match("H3")) { + ret = parse_uint32(&ctx->device->underload_packet_magic_header, "H3", value); + if (ret) + ctx->device->flags |= WGDEVICE_HAS_H3; + } else if (key_match("H4")) { + ret = parse_uint32(&ctx->device->transport_packet_magic_header, "H4", value); + if (ret) + ctx->device->flags |= WGDEVICE_HAS_H4; } else goto error; } else if (ctx->is_peer_section) { @@ -490,6 +598,10 @@ ret = parse_key(ctx->last_peer->preshared_key, value); if (ret) ctx->last_peer->flags |= WGPEER_HAS_PRESHARED_KEY; + } else if (key_match("AdvancedSecurity")) { + ret = parse_bool(&ctx->last_peer->advanced_security, "AdvancedSecurity", value); + if (ret) + ctx->last_peer->flags |= WGPEER_HAS_ADVANCED_SECURITY; } else goto error; } else @@ -611,6 +723,60 @@ device->flags |= WGDEVICE_HAS_PRIVATE_KEY; argv += 2; argc -= 2; + } else if (!strcmp(argv[0], "jc") && argc >= 2 && !peer) { + if (!parse_uint16(&device->junk_packet_count, "jc", argv[1])) + goto error; + device->flags |= WGDEVICE_HAS_JC; + argv += 2; + argc -= 2; + } else if (!strcmp(argv[0], "jmin") && argc >= 2 && !peer) { + if (!parse_uint16(&device->junk_packet_min_size, "jmin", argv[1])) + goto error; + device->flags |= WGDEVICE_HAS_JMIN; + argv += 2; + argc -= 2; + } else if (!strcmp(argv[0], "jmax") && argc >= 2 && !peer) { + if (!parse_uint16(&device->junk_packet_max_size, "jmax", argv[1])) + goto error; + device->flags |= WGDEVICE_HAS_JMAX; + argv += 2; + argc -= 2; + } else if (!strcmp(argv[0], "s1") && argc >= 2 && !peer) { + if (!parse_uint16(&device->init_packet_junk_size, "s1", argv[1])) + goto error; + device->flags |= WGDEVICE_HAS_S1; + argv += 2; + argc -= 2; + } else if (!strcmp(argv[0], "s2") && argc >= 2 && !peer) { + if (!parse_uint16(&device->response_packet_junk_size, "s2", argv[1])) + goto error; + device->flags |= WGDEVICE_HAS_S2; + argv += 2; + argc -= 2; + } else if (!strcmp(argv[0], "h1") && argc >= 2 && !peer) { + if (!parse_uint32(&device->init_packet_magic_header, "h1", argv[1])) + goto error; + device->flags |= WGDEVICE_HAS_H1; + argv += 2; + argc -= 2; + } else if (!strcmp(argv[0], "h2") && argc >= 2 && !peer) { + if (!parse_uint32(&device->response_packet_magic_header, "h2", argv[1])) + goto error; + device->flags |= WGDEVICE_HAS_H2; + argv += 2; + argc -= 2; + } else if (!strcmp(argv[0], "h3") && argc >= 2 && !peer) { + if (!parse_uint32(&device->underload_packet_magic_header, "h3", argv[1])) + goto error; + device->flags |= WGDEVICE_HAS_H3; + argv += 2; + argc -= 2; + } else if (!strcmp(argv[0], "h4") && argc >= 2 && !peer) { + if (!parse_uint32(&device->transport_packet_magic_header, "h4", argv[1])) + goto error; + device->flags |= WGDEVICE_HAS_H4; + argv += 2; + argc -= 2; } else if (!strcmp(argv[0], "peer") && argc >= 2) { struct wgpeer *new_peer = calloc(1, sizeof(*new_peer)); @@ -661,6 +827,12 @@ peer->flags |= WGPEER_HAS_PRESHARED_KEY; argv += 2; argc -= 2; + } else if (!strcmp(argv[0], "advanced-security") && argc >= 2 && peer) { + if (!parse_bool(&peer->advanced_security, "AdvancedSecurity", argv[1])) + goto error; + peer->flags |= WGPEER_HAS_ADVANCED_SECURITY; + argv += 2; + argc -= 2; } else { fprintf(stderr, "Invalid argument: %s\n", argv[0]); goto error; diff --git a/contrib/wireguard-tools/containers.h b/contrib/wireguard-tools/containers.h --- a/contrib/wireguard-tools/containers.h +++ b/contrib/wireguard-tools/containers.h @@ -7,6 +7,7 @@ #define CONTAINERS_H #include +#include #include #include #include @@ -48,7 +49,8 @@ WGPEER_REPLACE_ALLOWEDIPS = 1U << 1, WGPEER_HAS_PUBLIC_KEY = 1U << 2, WGPEER_HAS_PRESHARED_KEY = 1U << 3, - WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4 + WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4, + WGPEER_HAS_ADVANCED_SECURITY = 1U << 5 }; struct wgpeer { @@ -67,6 +69,8 @@ uint64_t rx_bytes, tx_bytes; uint16_t persistent_keepalive_interval; + bool advanced_security; + struct wgallowedip *first_allowedip, *last_allowedip; struct wgpeer *next_peer; }; @@ -76,7 +80,16 @@ WGDEVICE_HAS_PRIVATE_KEY = 1U << 1, WGDEVICE_HAS_PUBLIC_KEY = 1U << 2, WGDEVICE_HAS_LISTEN_PORT = 1U << 3, - WGDEVICE_HAS_FWMARK = 1U << 4 + WGDEVICE_HAS_FWMARK = 1U << 4, + WGDEVICE_HAS_JC = 1U << 5, + WGDEVICE_HAS_JMIN = 1U << 6, + WGDEVICE_HAS_JMAX = 1U << 7, + WGDEVICE_HAS_S1 = 1U << 8, + WGDEVICE_HAS_S2 = 1U << 9, + WGDEVICE_HAS_H1 = 1U << 10, + WGDEVICE_HAS_H2 = 1U << 11, + WGDEVICE_HAS_H3 = 1U << 12, + WGDEVICE_HAS_H4 = 1U << 13 }; struct wgdevice { @@ -92,6 +105,16 @@ uint16_t listen_port; struct wgpeer *first_peer, *last_peer; + + uint16_t junk_packet_count; + uint16_t junk_packet_min_size; + uint16_t junk_packet_max_size; + uint16_t init_packet_junk_size; + uint16_t response_packet_junk_size; + uint32_t init_packet_magic_header; + uint32_t response_packet_magic_header; + uint32_t underload_packet_magic_header; + uint32_t transport_packet_magic_header; }; #define for_each_wgpeer(__dev, __peer) for ((__peer) = (__dev)->first_peer; (__peer); (__peer) = (__peer)->next_peer) diff --git a/contrib/wireguard-tools/ipc-freebsd.h b/contrib/wireguard-tools/ipc-freebsd.h --- a/contrib/wireguard-tools/ipc-freebsd.h +++ b/contrib/wireguard-tools/ipc-freebsd.h @@ -91,6 +91,70 @@ 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, "user-cookie")) { number = nvlist_get_number(nvl_device, "user-cookie"); if (number <= UINT32_MAX) { @@ -272,6 +336,24 @@ 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) diff --git a/contrib/wireguard-tools/ipc-uapi.h b/contrib/wireguard-tools/ipc-uapi.h --- a/contrib/wireguard-tools/ipc-uapi.h +++ b/contrib/wireguard-tools/ipc-uapi.h @@ -51,10 +51,32 @@ 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); 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; @@ -183,6 +205,33 @@ } 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)); diff --git a/contrib/wireguard-tools/set.c b/contrib/wireguard-tools/set.c --- a/contrib/wireguard-tools/set.c +++ b/contrib/wireguard-tools/set.c @@ -18,7 +18,7 @@ int ret = 1; if (argc < 3) { - fprintf(stderr, "Usage: %s %s [listen-port ] [fwmark ] [private-key ] [peer [remove] [preshared-key ] [endpoint :] [persistent-keepalive ] [allowed-ips [+|-]/[,[+|-]/]...] ]...\n", PROG_NAME, argv[0]); + fprintf(stderr, "Usage: %s %s [listen-port ] [fwmark ] [private-key ] [peer [remove] [preshared-key ] [endpoint :] [persistent-keepalive ] [allowed-ips [+|-]/[,[+|-]/] [advanced-security ]...] ]...\n", PROG_NAME, argv[0]); return 1; } diff --git a/contrib/wireguard-tools/show.c b/contrib/wireguard-tools/show.c --- a/contrib/wireguard-tools/show.c +++ b/contrib/wireguard-tools/show.c @@ -220,6 +220,24 @@ terminal_printf(" " TERMINAL_BOLD "listening port" TERMINAL_RESET ": %u\n", device->listen_port); if (device->fwmark) terminal_printf(" " TERMINAL_BOLD "fwmark" TERMINAL_RESET ": 0x%x\n", device->fwmark); + if (device->junk_packet_count) + terminal_printf(" " TERMINAL_BOLD "jc" TERMINAL_RESET ": %u\n", device->junk_packet_count); + if (device->junk_packet_min_size) + terminal_printf(" " TERMINAL_BOLD "jmin" TERMINAL_RESET ": %u\n", device->junk_packet_min_size); + if (device->junk_packet_max_size) + terminal_printf(" " TERMINAL_BOLD "jmax" TERMINAL_RESET ": %u\n", device->junk_packet_max_size); + if (device->init_packet_junk_size) + terminal_printf(" " TERMINAL_BOLD "s1" TERMINAL_RESET ": %u\n", device->init_packet_junk_size); + if (device->response_packet_junk_size) + terminal_printf(" " TERMINAL_BOLD "s2" TERMINAL_RESET ": %u\n", device->response_packet_junk_size); + if (device->init_packet_magic_header) + terminal_printf(" " TERMINAL_BOLD "h1" TERMINAL_RESET ": %u\n", device->init_packet_magic_header); + if (device->response_packet_magic_header) + terminal_printf(" " TERMINAL_BOLD "h2" TERMINAL_RESET ": %u\n", device->response_packet_magic_header); + if (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) + terminal_printf(" " TERMINAL_BOLD "h4" TERMINAL_RESET ": %u\n", device->transport_packet_magic_header); if (device->first_peer) { sort_peers(device); terminal_printf("\n"); @@ -260,6 +278,15 @@ printf("%s\t", maybe_key(device->private_key, device->flags & WGDEVICE_HAS_PRIVATE_KEY)); printf("%s\t", maybe_key(device->public_key, device->flags & WGDEVICE_HAS_PUBLIC_KEY)); printf("%u\t", device->listen_port); + printf("%u\t", device->junk_packet_count); + printf("%u\t", device->junk_packet_min_size); + printf("%u\t", device->junk_packet_max_size); + printf("%u\t", device->init_packet_junk_size); + printf("%u\t", device->response_packet_junk_size); + printf("%u\t", device->init_packet_magic_header); + printf("%u\t", device->response_packet_magic_header); + printf("%u\t", device->underload_packet_magic_header); + printf("%u\t", device->transport_packet_magic_header); if (device->fwmark) printf("0x%x\n", device->fwmark); else @@ -311,6 +338,42 @@ printf("0x%x\n", device->fwmark); else printf("off\n"); + } else if(!strcmp(param, "jc")) { + if (with_interface) + printf("%s\t", device->name); + printf("%u\n", device->junk_packet_count); + } else if(!strcmp(param, "jmin")) { + if (with_interface) + printf("%s\t", device->name); + printf("%u\n", device->junk_packet_min_size); + } else if(!strcmp(param, "jmax")) { + if (with_interface) + printf("%s\t", device->name); + printf("%u\n", device->junk_packet_max_size); + } else if(!strcmp(param, "s1")) { + if (with_interface) + printf("%s\t", device->name); + printf("%u\n", device->init_packet_junk_size); + } else if(!strcmp(param, "s2")) { + if (with_interface) + printf("%s\t", device->name); + printf("%u\n", device->response_packet_junk_size); + } else if(!strcmp(param, "h1")) { + if (with_interface) + printf("%s\t", device->name); + printf("%u\n", device->init_packet_magic_header); + } else if(!strcmp(param, "h2")) { + if (with_interface) + printf("%s\t", device->name); + printf("%u\n", device->response_packet_magic_header); + } else if(!strcmp(param, "h3")) { + if (with_interface) + printf("%s\t", device->name); + printf("%u\n", device->underload_packet_magic_header); + } else if(!strcmp(param, "h4")) { + if (with_interface) + printf("%s\t", device->name); + printf("%u\n", device->transport_packet_magic_header); } else if (!strcmp(param, "endpoints")) { for_each_wgpeer(device, peer) { if (with_interface) diff --git a/contrib/wireguard-tools/showconf.c b/contrib/wireguard-tools/showconf.c --- a/contrib/wireguard-tools/showconf.c +++ b/contrib/wireguard-tools/showconf.c @@ -46,6 +46,25 @@ key_to_base64(base64, device->private_key); printf("PrivateKey = %s\n", base64); } + if (device->flags & WGDEVICE_HAS_JC) + printf("Jc = %u\n", device->junk_packet_count); + if (device->flags & WGDEVICE_HAS_JMIN) + printf("Jmin = %u\n", device->junk_packet_min_size); + if (device->flags & WGDEVICE_HAS_JMAX) + printf("Jmax = %u\n", device->junk_packet_max_size); + if (device->flags & WGDEVICE_HAS_S1) + printf("S1 = %u\n", device->init_packet_junk_size); + if (device->flags & WGDEVICE_HAS_S2) + printf("S2 = %u\n", device->response_packet_junk_size); + if (device->flags & WGDEVICE_HAS_H1) + printf("H1 = %u\n", device->init_packet_magic_header); + if (device->flags & WGDEVICE_HAS_H2) + printf("H2 = %u\n", device->response_packet_magic_header); + if (device->flags & WGDEVICE_HAS_H3) + printf("H3 = %u\n", device->underload_packet_magic_header); + if (device->flags & WGDEVICE_HAS_H4) + printf("H4 = %u\n", device->transport_packet_magic_header); + printf("\n"); for_each_wgpeer(device, peer) { key_to_base64(base64, peer->public_key); @@ -54,6 +73,9 @@ key_to_base64(base64, peer->preshared_key); printf("PresharedKey = %s\n", base64); } + if (peer->flags & WGPEER_HAS_ADVANCED_SECURITY) { + printf("AdvancedSecurity = %s\n", peer->advanced_security ? "on" : "off"); + } if (peer->first_allowedip) printf("AllowedIPs = "); for_each_wgallowedip(peer, allowedip) { diff --git a/sys/dev/wg/if_wg.c b/sys/dev/wg/if_wg.c --- a/sys/dev/wg/if_wg.c +++ b/sys/dev/wg/if_wg.c @@ -70,10 +70,14 @@ #define DPRINTF(sc, ...) if (if_getflags(sc->sc_ifp) & IFF_DEBUG) if_printf(sc->sc_ifp, ##__VA_ARGS__) /* First byte indicating packet type on the wire */ -#define WG_PKT_INITIATION htole32(1) -#define WG_PKT_RESPONSE htole32(2) -#define WG_PKT_COOKIE htole32(3) -#define WG_PKT_DATA htole32(4) +#define WG_PKT_X_DEFAULT(x, y) ((x) ? (x) : (y)) + +#define WG_PKT_INITIATION(sc) WG_PKT_X_DEFAULT(sc->sc_socket.so_pkt_initiation, htole32(1)) +#define WG_PKT_RESPONSE(sc) WG_PKT_X_DEFAULT(sc->sc_socket.so_pkt_response, htole32(2)) +#define WG_PKT_COOKIE(sc) WG_PKT_X_DEFAULT(sc->sc_socket.so_pkt_cookie, htole32(3)) +#define WG_PKT_DATA(sc) WG_PKT_X_DEFAULT(sc->sc_socket.so_pkt_data, htole32(4)) + +#define WG_PKT_DEFAULT_MAX htole32(4) #define WG_PKT_PADDING 16 #define WG_KEY_SIZE 32 @@ -215,6 +219,16 @@ uint32_t so_user_cookie; int so_fibnum; in_port_t so_port; + + uint32_t so_junk_packet_count; // Jc + uint32_t so_junk_packet_min_size; // Jmin + uint32_t so_junk_packet_max_size; // Jmax + uint32_t so_init_packet_junk_size; // S1 + uint32_t so_response_packet_junk_size; // S2 + uint32_t so_pkt_initiation; // H1 + uint32_t so_pkt_response; // H2 + uint32_t so_pkt_cookie; // H3 + uint32_t so_pkt_data; // H4 }; struct wg_softc { @@ -1288,21 +1302,78 @@ wg_send_buf(peer->p_sc, &endpoint, buf, len); } +static void +wg_peer_send_buf_junk(struct wg_peer *peer, uint8_t *buf, size_t len, size_t junk_len) +{ + uint8_t *junk_buf; + size_t total_len; + + if (junk_len == 0) { + wg_peer_send_buf(peer, buf, len); + return; + } + + total_len = len + junk_len; + junk_buf = malloc(total_len, M_DEVBUF, M_NOWAIT); + if (junk_buf == NULL) + return; + + arc4random_buf(junk_buf, junk_len); + memcpy(junk_buf + junk_len, buf, len); + + wg_peer_send_buf(peer, junk_buf, total_len); + + free(junk_buf, M_DEVBUF); +} + +static void +wg_send_junk_packets(struct wg_peer *peer) +{ + struct wg_softc *sc = peer->p_sc; + uint8_t *buf; + size_t size; + int i; + + for (i = 0; i < sc->sc_socket.so_junk_packet_count; i++) { + /* Generate random size between min and max */ + size = arc4random_uniform(sc->sc_socket.so_junk_packet_max_size - + sc->sc_socket.so_junk_packet_min_size + 1) + + sc->sc_socket.so_junk_packet_min_size; + + buf = malloc(size, M_DEVBUF, M_NOWAIT); + if (buf == NULL) + continue; + + /* Fill with random data */ + arc4random_buf(buf, size); + + /* Send to peer's endpoint */ + wg_peer_send_buf(peer, buf, size); + + free(buf, M_DEVBUF); + } +} + static void wg_send_initiation(struct wg_peer *peer) { struct wg_pkt_initiation pkt; + struct wg_softc *sc = peer->p_sc; + + /* Send junk packets before handshake */ + wg_send_junk_packets(peer); if (noise_create_initiation(peer->p_remote, &pkt.s_idx, pkt.ue, pkt.es, pkt.ets) != 0) return; - DPRINTF(peer->p_sc, "Sending handshake initiation to peer %" PRIu64 "\n", peer->p_id); + DPRINTF(sc, "Sending handshake initiation to peer %" PRIu64 "\n", peer->p_id); - pkt.t = WG_PKT_INITIATION; + pkt.t = WG_PKT_INITIATION(sc); cookie_maker_mac(&peer->p_cookie, &pkt.m, &pkt, sizeof(pkt) - sizeof(pkt.m)); - wg_peer_send_buf(peer, (uint8_t *)&pkt, sizeof(pkt)); + wg_peer_send_buf_junk(peer, (uint8_t *)&pkt, sizeof(pkt), + sc->sc_socket.so_init_packet_junk_size); wg_timers_event_handshake_initiated(peer); } @@ -1318,10 +1389,11 @@ DPRINTF(peer->p_sc, "Sending handshake response to peer %" PRIu64 "\n", peer->p_id); wg_timers_event_session_derived(peer); - pkt.t = WG_PKT_RESPONSE; + pkt.t = WG_PKT_RESPONSE(peer->p_sc); cookie_maker_mac(&peer->p_cookie, &pkt.m, &pkt, sizeof(pkt)-sizeof(pkt.m)); - wg_peer_send_buf(peer, (uint8_t*)&pkt, sizeof(pkt)); + wg_peer_send_buf_junk(peer, (uint8_t*)&pkt, sizeof(pkt), + peer->p_sc->sc_socket.so_response_packet_junk_size); } static void @@ -1332,7 +1404,7 @@ DPRINTF(sc, "Sending cookie response for denied handshake message\n"); - pkt.t = WG_PKT_COOKIE; + pkt.t = WG_PKT_COOKIE(sc); pkt.r_idx = idx; cookie_checker_create_payload(&sc->sc_cookie, cm, pkt.nonce, @@ -1389,8 +1461,8 @@ if ((pkt->p_mbuf = m = m_pullup(m, m->m_pkthdr.len)) == NULL) goto error; - switch (*mtod(m, uint32_t *)) { - case WG_PKT_INITIATION: + uint32_t t = *mtod(m, uint32_t *); + if ( t == WG_PKT_INITIATION(sc)) { init = mtod(m, struct wg_pkt_initiation *); res = cookie_checker_validate_macs(&sc->sc_cookie, &init->m, @@ -1423,8 +1495,7 @@ wg_peer_set_endpoint(peer, e); wg_send_response(peer); - break; - case WG_PKT_RESPONSE: + } else if (t == WG_PKT_RESPONSE(sc)) { resp = mtod(m, struct wg_pkt_response *); res = cookie_checker_validate_macs(&sc->sc_cookie, &resp->m, @@ -1457,8 +1528,7 @@ wg_peer_set_endpoint(peer, e); wg_timers_event_session_derived(peer); wg_timers_event_handshake_complete(peer); - break; - case WG_PKT_COOKIE: + } else if (t == WG_PKT_COOKIE(sc)) { cook = mtod(m, struct wg_pkt_cookie *); if ((remote = noise_remote_index(sc->sc_local, cook->r_idx)) == NULL) { @@ -1477,7 +1547,7 @@ } goto not_authenticated; - default: + } else { panic("invalid packet in handshake queue"); } @@ -1595,7 +1665,7 @@ if (m == NULL) goto out; data = mtod(m, struct wg_pkt_data *); - data->t = WG_PKT_DATA; + data->t = WG_PKT_DATA(sc); data->r_idx = idx; data->nonce = htole64(pkt->p_nonce); @@ -2035,6 +2105,46 @@ return (pkt); } +static struct mbuf * +wg_skip_junk_input(struct mbuf *m, struct wg_softc *sc) +{ + size_t assumed_offset = 0; + uint32_t assumed_type = 0; + + if (!sc->sc_socket.so_init_packet_junk_size && + !sc->sc_socket.so_response_packet_junk_size) + return m; + + /* Check if this is an initiation packet with junk */ + if (m->m_pkthdr.len == sizeof(struct wg_pkt_initiation) + + sc->sc_socket.so_init_packet_junk_size) { + assumed_offset = sc->sc_socket.so_init_packet_junk_size; + assumed_type = WG_PKT_INITIATION(sc); + } + /* Check if this is a response packet with junk */ + else if (m->m_pkthdr.len == sizeof(struct wg_pkt_response) + + sc->sc_socket.so_response_packet_junk_size) { + assumed_offset = sc->sc_socket.so_response_packet_junk_size; + assumed_type = WG_PKT_RESPONSE(sc); + } + + if (assumed_offset == 0) + return m; + + m = m_pullup(m, assumed_offset + sizeof(uint32_t)); + if (m == NULL) + return m; + + /* verify packet type */ + if (*(uint32_t *)mtodo(m, assumed_offset) != assumed_type) { + return m; + } + + m_adj(m, assumed_offset); + + return m; +} + static bool wg_input(struct mbuf *m, int offset, struct inpcb *inpcb, const struct sockaddr *sa, void *_sc) @@ -2064,6 +2174,9 @@ /* Caller provided us with `sa`, no need for this header. */ m_adj(m, offset + sizeof(struct udphdr)); + /* Check if this packet has junk to skip, doing m_pullup if needed*/ + m = wg_skip_junk_input(m, sc); + /* Pullup enough to read packet type */ if ((m = m_pullup(m, sizeof(uint32_t))) == NULL) { if_inc_counter(sc->sc_ifp, IFCOUNTER_IQDROPS, 1); @@ -2097,11 +2210,11 @@ } if ((m->m_pkthdr.len == sizeof(struct wg_pkt_initiation) && - *mtod(m, uint32_t *) == WG_PKT_INITIATION) || + *mtod(m, uint32_t *) == WG_PKT_INITIATION(sc)) || (m->m_pkthdr.len == sizeof(struct wg_pkt_response) && - *mtod(m, uint32_t *) == WG_PKT_RESPONSE) || + *mtod(m, uint32_t *) == WG_PKT_RESPONSE(sc)) || (m->m_pkthdr.len == sizeof(struct wg_pkt_cookie) && - *mtod(m, uint32_t *) == WG_PKT_COOKIE)) { + *mtod(m, uint32_t *) == WG_PKT_COOKIE(sc))) { if (wg_queue_enqueue_handshake(&sc->sc_handshake_queue, pkt) != 0) { if_inc_counter(sc->sc_ifp, IFCOUNTER_IQDROPS, 1); @@ -2109,7 +2222,7 @@ } GROUPTASK_ENQUEUE(&sc->sc_handshake); } else if (m->m_pkthdr.len >= sizeof(struct wg_pkt_data) + - NOISE_AUTHTAG_LEN && *mtod(m, uint32_t *) == WG_PKT_DATA) { + NOISE_AUTHTAG_LEN && *mtod(m, uint32_t *) == WG_PKT_DATA(sc)) { /* Pullup whole header to read r_idx below. */ if ((pkt->p_mbuf = m_pullup(m, sizeof(struct wg_pkt_data))) == NULL) @@ -2600,6 +2713,8 @@ nvlist_t *nvl; ssize_t size; int err; + uint64_t s1 = 0, s2 = 0; + uint64_t h[5] = {0}; ifp = sc->sc_ifp; if (wgd->wgd_size == 0 || wgd->wgd_data == NULL) @@ -2638,6 +2753,88 @@ sc->sc_socket.so_port = new_port; } } + if (nvlist_exists_number(nvl, "jc")) { + uint64_t jc = nvlist_get_number(nvl, "jc"); + if (jc > UINT8_MAX) { + err = EINVAL; + goto out_locked; + } + sc->sc_socket.so_junk_packet_count = jc; + } + if (nvlist_exists_number(nvl, "jmin")) { + uint64_t jmin = nvlist_get_number(nvl, "jmin"); + if (jmin > 1200) { + err = EINVAL; + goto out_locked; + } + sc->sc_socket.so_junk_packet_min_size = jmin; + } + if (nvlist_exists_number(nvl, "jmax")) { + uint64_t jmax = nvlist_get_number(nvl, "jmax"); + if (jmax > ETHERMTU || jmax < sc->sc_socket.so_junk_packet_min_size) { + err = EINVAL; + goto out_locked; + } + sc->sc_socket.so_junk_packet_max_size = jmax; + } + if (nvlist_exists_number(nvl, "s1")) { + s1 = nvlist_get_number(nvl, "s1"); + if (s1 + sizeof(struct wg_pkt_initiation) > ETHERMTU) { + err = EINVAL; + goto out_locked; + } + sc->sc_socket.so_init_packet_junk_size = s1; + } + if (nvlist_exists_number(nvl, "s2")) { + s2 = nvlist_get_number(nvl, "s2"); + if (s2 + sizeof(struct wg_pkt_response) > ETHERMTU) { + err = EINVAL; + goto out_locked; + } + sc->sc_socket.so_response_packet_junk_size = s2; + } + if (s1 && s2 && s1 == s2) { + err = EINVAL; + goto out_locked; + } + if (nvlist_exists_number(nvl, "h1")) { + h[1] = nvlist_get_number(nvl, "h1"); + sc->sc_socket.so_pkt_initiation = h[1]; + } + if (nvlist_exists_number(nvl, "h2")) { + h[2] = nvlist_get_number(nvl, "h2"); + sc->sc_socket.so_pkt_response = h[2]; + } + if (nvlist_exists_number(nvl, "h3")) { + h[3] = nvlist_get_number(nvl, "h3"); + sc->sc_socket.so_pkt_cookie = h[3]; + } + if (nvlist_exists_number(nvl, "h4")) { + h[4] = nvlist_get_number(nvl, "h4"); + sc->sc_socket.so_pkt_data = h[4]; + } + + // Check magic headers + for(int i = 1; i <= 4; i++) { + if (h[i] == 0) { + continue; + } + + // Magic headers should be greater than WG_PKT_DATA + if (h[i] <= WG_PKT_DEFAULT_MAX) { + err = EINVAL; + goto out_locked; + } + + // Magic headers should be different + for(int j = i - 1; j > 0; j--) { + if (h[j] && h[i] == h[j]) { + err = EINVAL; + goto out_locked; + } + } + } + if (nvlist_exists_binary(nvl, "private-key")) { const void *key = nvlist_get_binary(nvl, "private-key", &size); if (size != WG_KEY_SIZE) { @@ -2724,6 +2921,24 @@ if (sc->sc_socket.so_port != 0) nvlist_add_number(nvl, "listen-port", sc->sc_socket.so_port); + if (sc->sc_socket.so_junk_packet_count > 0) + nvlist_add_number(nvl, "jc", sc->sc_socket.so_junk_packet_count); + if (sc->sc_socket.so_junk_packet_min_size > 0) + nvlist_add_number(nvl, "jmin", sc->sc_socket.so_junk_packet_min_size); + if (sc->sc_socket.so_junk_packet_max_size > 0) + nvlist_add_number(nvl, "jmax", sc->sc_socket.so_junk_packet_max_size); + if (sc->sc_socket.so_init_packet_junk_size > 0) + nvlist_add_number(nvl, "s1", sc->sc_socket.so_init_packet_junk_size); + if (sc->sc_socket.so_response_packet_junk_size > 0) + nvlist_add_number(nvl, "s2", sc->sc_socket.so_response_packet_junk_size); + if (sc->sc_socket.so_pkt_initiation > 0) + nvlist_add_number(nvl, "h1", sc->sc_socket.so_pkt_initiation); + if (sc->sc_socket.so_pkt_response > 0) + nvlist_add_number(nvl, "h2", sc->sc_socket.so_pkt_response); + if (sc->sc_socket.so_pkt_cookie > 0) + nvlist_add_number(nvl, "h3", sc->sc_socket.so_pkt_cookie); + if (sc->sc_socket.so_pkt_data > 0) + nvlist_add_number(nvl, "h4", sc->sc_socket.so_pkt_data); if (sc->sc_socket.so_user_cookie != 0) nvlist_add_number(nvl, "user-cookie", sc->sc_socket.so_user_cookie); if (noise_local_keys(sc->sc_local, public_key, private_key) == 0) { @@ -2987,6 +3202,15 @@ sc->sc_ucred = crhold(curthread->td_ucred); sc->sc_socket.so_fibnum = curthread->td_proc->p_fibnum; sc->sc_socket.so_port = 0; + sc->sc_socket.so_junk_packet_count = 0; + sc->sc_socket.so_junk_packet_min_size = 0; + sc->sc_socket.so_junk_packet_max_size = 0; + sc->sc_socket.so_init_packet_junk_size = 0; + sc->sc_socket.so_response_packet_junk_size = 0; + sc->sc_socket.so_pkt_initiation = 0; + sc->sc_socket.so_pkt_response = 0; + sc->sc_socket.so_pkt_cookie = 0; + sc->sc_socket.so_pkt_data = 0; TAILQ_INIT(&sc->sc_peers); sc->sc_peers_num = 0;