Skip to content

Commit fbba3fe

Browse files
Add wg genconf cli command to generate random interface parameters
Generate a base AmneziaWG interface config with random PrivateKey, ListenPort, junk packet settings, packet junk sizes, and magic headers. Reuse the existing secure random implementation from genkey, avoid overlapping magic header ranges, and skip UDP ports that are already bound.
1 parent 5d6179a commit fbba3fe

8 files changed

Lines changed: 321 additions & 61 deletions

File tree

src/completion/wg.bash-completion

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ _awg_completion() {
55
local a
66

77
if [[ $COMP_CWORD -eq 1 ]]; then
8-
COMPREPLY+=( $(compgen -W "help show showconf set setconf addconf syncconf genkey genpsk pubkey" -- "${COMP_WORDS[1]}") )
8+
COMPREPLY+=( $(compgen -W "help show showconf set setconf addconf syncconf genconf genkey genpsk pubkey" -- "${COMP_WORDS[1]}") )
99
return
1010
fi
1111
case "${COMP_WORDS[1]}" in
12-
genkey|genpsk|pubkey|help) return; ;;
12+
genconf|genkey|genpsk|pubkey|help) return; ;;
1313
show|showconf|set|setconf|addconf|syncconf) ;;
1414
*) return;
1515
esac

src/genconf.c

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
// SPDX-License-Identifier: GPL-2.0 OR MIT
2+
#include <errno.h>
3+
#include <inttypes.h>
4+
#include <stdio.h>
5+
#include <stdint.h>
6+
#include <stdbool.h>
7+
#ifndef _WIN32
8+
#include <unistd.h>
9+
#include <sys/socket.h>
10+
#include <netinet/in.h>
11+
#else
12+
#include <winsock2.h>
13+
#include <ws2tcpip.h>
14+
#endif
15+
16+
#include "curve25519.h"
17+
#include "encoding.h"
18+
#include "random.h"
19+
#include "subcommands.h"
20+
21+
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
22+
#define SET_SOCKADDR_LEN(addr) ((addr).sin_len = sizeof(addr))
23+
#define SET_SOCKADDR6_LEN(addr) ((addr).sin6_len = sizeof(addr))
24+
#else
25+
#define SET_SOCKADDR_LEN(addr) do { } while (0)
26+
#define SET_SOCKADDR6_LEN(addr) do { } while (0)
27+
#endif
28+
29+
struct magic_header {
30+
uint32_t first;
31+
uint32_t last;
32+
};
33+
34+
#ifndef _WIN32
35+
typedef int socket_fd_t;
36+
#define INVALID_SOCKET_FD (-1)
37+
#define close_socket close
38+
#else
39+
typedef SOCKET socket_fd_t;
40+
#define INVALID_SOCKET_FD INVALID_SOCKET
41+
#define close_socket closesocket
42+
#endif
43+
44+
static bool random_u32(uint32_t *value)
45+
{
46+
return get_random_bytes((uint8_t *)value, sizeof(*value));
47+
}
48+
49+
static bool random_range_u32(uint32_t *value, uint32_t min, uint32_t max)
50+
{
51+
uint32_t random, range;
52+
uint64_t limit;
53+
54+
if (min > max) {
55+
errno = ERANGE;
56+
return false;
57+
}
58+
range = max - min + 1;
59+
if (!range) {
60+
if (!random_u32(value))
61+
return false;
62+
return true;
63+
}
64+
65+
limit = ((uint64_t)UINT32_MAX + 1) - (((uint64_t)UINT32_MAX + 1) % range);
66+
do {
67+
if (!random_u32(&random))
68+
return false;
69+
} while ((uint64_t)random >= limit);
70+
71+
*value = min + (random % range);
72+
return true;
73+
}
74+
75+
static bool random_magic_header(struct magic_header *header)
76+
{
77+
uint32_t offset;
78+
79+
if (!random_range_u32(&header->first, 5, UINT32_MAX))
80+
return false;
81+
if (!random_range_u32(&offset, 0, 65365))
82+
return false;
83+
84+
header->last = header->first + offset;
85+
if (header->last < header->first)
86+
header->last = UINT32_MAX;
87+
return true;
88+
}
89+
90+
static bool magic_headers_overlap(const struct magic_header *a, const struct magic_header *b)
91+
{
92+
return a->last >= b->first && b->last >= a->first;
93+
}
94+
95+
static bool any_magic_headers_overlap(const struct magic_header headers[static 4])
96+
{
97+
for (size_t i = 0; i < 4; ++i) {
98+
for (size_t j = i + 1; j < 4; ++j) {
99+
if (magic_headers_overlap(&headers[i], &headers[j]))
100+
return true;
101+
}
102+
}
103+
return false;
104+
}
105+
106+
static bool udp_port_available_ipv4(uint16_t port)
107+
{
108+
socket_fd_t fd;
109+
struct sockaddr_in addr = {
110+
.sin_family = AF_INET,
111+
.sin_port = htons(port),
112+
.sin_addr = { 0 }
113+
};
114+
bool available;
115+
116+
SET_SOCKADDR_LEN(addr);
117+
fd = socket(AF_INET, SOCK_DGRAM, 0);
118+
if (fd == INVALID_SOCKET_FD)
119+
return false;
120+
121+
available = bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == 0;
122+
close_socket(fd);
123+
return available;
124+
}
125+
126+
static bool udp_port_available_ipv6(uint16_t port)
127+
{
128+
socket_fd_t fd;
129+
struct sockaddr_in6 addr = {
130+
.sin6_family = AF_INET6,
131+
.sin6_port = htons(port),
132+
.sin6_addr = IN6ADDR_ANY_INIT
133+
};
134+
bool available;
135+
136+
SET_SOCKADDR6_LEN(addr);
137+
fd = socket(AF_INET6, SOCK_DGRAM, 0);
138+
if (fd == INVALID_SOCKET_FD)
139+
return true;
140+
141+
available = bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == 0;
142+
close_socket(fd);
143+
return available;
144+
}
145+
146+
static bool random_available_listen_port(uint32_t *port)
147+
{
148+
for (size_t attempt = 0; attempt < 128; ++attempt) {
149+
if (!random_range_u32(port, 1024, 65535))
150+
return false;
151+
if (udp_port_available_ipv4((uint16_t)*port) && udp_port_available_ipv6((uint16_t)*port))
152+
return true;
153+
}
154+
155+
errno = EADDRINUSE;
156+
return false;
157+
}
158+
159+
int genconf_main(int argc, const char *argv[])
160+
{
161+
uint8_t key[WG_KEY_LEN];
162+
char base64[WG_KEY_LEN_BASE64];
163+
uint32_t listen_port, jc, jmin, jmax, s1, s2, s3, s4;
164+
struct magic_header h[4];
165+
bool found_nonoverlapping_headers = false;
166+
167+
if (argc != 1) {
168+
fprintf(stderr, "Usage: %s %s\n", PROG_NAME, argv[0]);
169+
return 1;
170+
}
171+
172+
if (!get_random_bytes(key, WG_KEY_LEN)) {
173+
perror("getrandom");
174+
return 1;
175+
}
176+
curve25519_clamp_secret(key);
177+
key_to_base64(base64, key);
178+
179+
if (!random_available_listen_port(&listen_port)) {
180+
perror("listen port");
181+
return 1;
182+
}
183+
if (!random_range_u32(&jc, 3, 10) ||
184+
!random_range_u32(&jmin, 0, 1200) ||
185+
!random_range_u32(&jmax, jmin, 1280) ||
186+
!random_range_u32(&s1, 15, 1304) ||
187+
!random_range_u32(&s2, 15, 1360) ||
188+
!random_range_u32(&s3, 15, 1388) ||
189+
!random_range_u32(&s4, 15, 160)) {
190+
perror("getrandom");
191+
return 1;
192+
}
193+
194+
for (size_t attempt = 0; attempt < 128; ++attempt) {
195+
if (!random_magic_header(&h[0]) ||
196+
!random_magic_header(&h[1]) ||
197+
!random_magic_header(&h[2]) ||
198+
!random_magic_header(&h[3])) {
199+
perror("getrandom");
200+
return 1;
201+
}
202+
if (!any_magic_headers_overlap(h)) {
203+
found_nonoverlapping_headers = true;
204+
break;
205+
}
206+
}
207+
if (!found_nonoverlapping_headers) {
208+
fputs("Unable to generate non-overlapping magic headers\n", stderr);
209+
return 1;
210+
}
211+
212+
printf("[Interface]\n");
213+
printf("ListenPort = %" PRIu32 "\n", listen_port);
214+
printf("PrivateKey = %s\n", base64);
215+
printf("Jc = %" PRIu32 "\n", jc);
216+
printf("Jmin = %" PRIu32 "\n", jmin);
217+
printf("Jmax = %" PRIu32 "\n", jmax);
218+
printf("S1 = %" PRIu32 "\n", s1);
219+
printf("S2 = %" PRIu32 "\n", s2);
220+
printf("S3 = %" PRIu32 "\n", s3);
221+
printf("S4 = %" PRIu32 "\n", s4);
222+
printf("H1 = %" PRIu32 "-%" PRIu32 "\n", h[0].first, h[0].last);
223+
printf("H2 = %" PRIu32 "-%" PRIu32 "\n", h[1].first, h[1].last);
224+
printf("H3 = %" PRIu32 "-%" PRIu32 "\n", h[2].first, h[2].last);
225+
printf("H4 = %" PRIu32 "-%" PRIu32 "\n", h[3].first, h[3].last);
226+
return 0;
227+
}

src/genkey.c

Lines changed: 1 addition & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -3,75 +3,17 @@
33
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
44
*/
55

6-
#include <errno.h>
76
#include <stdio.h>
8-
#include <stdbool.h>
97
#include <unistd.h>
108
#include <sys/types.h>
119
#include <sys/stat.h>
1210
#include <string.h>
13-
#include <fcntl.h>
14-
#ifdef __linux__
15-
#include <sys/syscall.h>
16-
#endif
17-
#ifdef __APPLE__
18-
#include <AvailabilityMacros.h>
19-
#ifndef MAC_OS_X_VERSION_10_12
20-
#define MAC_OS_X_VERSION_10_12 101200
21-
#endif
22-
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12
23-
#include <sys/random.h>
24-
#endif
25-
#endif
2611

2712
#include "curve25519.h"
2813
#include "encoding.h"
14+
#include "random.h"
2915
#include "subcommands.h"
3016

31-
#ifndef _WIN32
32-
static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len)
33-
{
34-
ssize_t ret = 0;
35-
size_t i;
36-
int fd;
37-
38-
if (len > 256) {
39-
errno = EOVERFLOW;
40-
return false;
41-
}
42-
43-
#if defined(__OpenBSD__) || (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) || (defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)))
44-
if (!getentropy(out, len))
45-
return true;
46-
#endif
47-
48-
#if defined(__NR_getrandom) && defined(__linux__)
49-
if (syscall(__NR_getrandom, out, len, 0) == (ssize_t)len)
50-
return true;
51-
#endif
52-
53-
fd = open("/dev/urandom", O_RDONLY);
54-
if (fd < 0)
55-
return false;
56-
for (errno = 0, i = 0; i < len; i += ret, ret = 0) {
57-
ret = read(fd, out + i, len - i);
58-
if (ret <= 0) {
59-
ret = errno ? -errno : -EIO;
60-
break;
61-
}
62-
}
63-
close(fd);
64-
errno = -ret;
65-
return i == len;
66-
}
67-
#else
68-
#include <ntsecapi.h>
69-
static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len)
70-
{
71-
return RtlGenRandom(out, len);
72-
}
73-
#endif
74-
7517
int genkey_main(int argc, const char *argv[])
7618
{
7719
uint8_t key[WG_KEY_LEN];

src/man/wg.8

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ but has the benefit of not disrupting current peer sessions. The contents of
102102
\fI<configuration-filename>\fP must be in the format described by
103103
\fICONFIGURATION FILE FORMAT\fP below.
104104
.TP
105+
\fBgenconf\fP
106+
Generates a random base AmneziaWG configuration with an \fIInterface\fP section
107+
and prints it to standard output. The generated output includes a private key,
108+
listen port, junk packet settings, packet junk sizes, and magic headers.
109+
.TP
105110
\fBgenkey\fP
106111
Generates a random \fIprivate\fP key in base64 and prints it to
107112
standard output.

0 commit comments

Comments
 (0)