1 // SPDX-License-Identifier: GPL-2.0 OR MIT
3 * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
8 #include <netinet/in.h>
9 #include <sys/socket.h>
21 #include "containers.h"
25 #include "subcommands.h"
27 static int peer_cmp(const void *first, const void *second)
30 const struct wgpeer *a = *(void *const *)first, *b = *(void *const *)second;
32 if (!a->last_handshake_time.tv_sec && !a->last_handshake_time.tv_nsec && (b->last_handshake_time.tv_sec || b->last_handshake_time.tv_nsec))
34 if (!b->last_handshake_time.tv_sec && !b->last_handshake_time.tv_nsec && (a->last_handshake_time.tv_sec || a->last_handshake_time.tv_nsec))
36 diff = a->last_handshake_time.tv_sec - b->last_handshake_time.tv_sec;
38 diff = a->last_handshake_time.tv_nsec - b->last_handshake_time.tv_nsec;
46 /* This, hilariously, is not the right way to sort a linked list... */
47 static void sort_peers(struct wgdevice *device)
49 size_t peer_count = 0, i = 0;
50 struct wgpeer *peer, **peers;
52 for_each_wgpeer(device, peer)
56 peers = calloc(peer_count, sizeof(*peers));
59 for_each_wgpeer(device, peer)
61 qsort(peers, peer_count, sizeof(*peers), peer_cmp);
62 device->first_peer = peers[0];
63 for (i = 1; i < peer_count; ++i) {
64 peers[i - 1]->next_peer = peers[i];
66 peers[peer_count - 1]->next_peer = NULL;
70 static char *key(const uint8_t key[static WG_KEY_LEN])
72 static char base64[WG_KEY_LEN_BASE64];
74 key_to_base64(base64, key);
78 static const char *maybe_key(const uint8_t maybe_key[static WG_KEY_LEN], bool have_it)
82 return key(maybe_key);
85 static const char *masked_key(const uint8_t masked_key[static WG_KEY_LEN])
87 const char *var = getenv("WG_HIDE_KEYS");
89 if (var && !strcmp(var, "never"))
90 return key(masked_key);
94 static char *ip(const struct wgallowedip *ip)
96 static char buf[INET6_ADDRSTRLEN + 1];
98 memset(buf, 0, INET6_ADDRSTRLEN + 1);
99 if (ip->family == AF_INET)
100 inet_ntop(AF_INET, &ip->ip4, buf, INET6_ADDRSTRLEN);
101 else if (ip->family == AF_INET6)
102 inet_ntop(AF_INET6, &ip->ip6, buf, INET6_ADDRSTRLEN);
106 static char *endpoint(const struct sockaddr *addr)
109 char service[512 + 1];
110 static char buf[sizeof(host) + sizeof(service) + 4];
112 socklen_t addr_len = 0;
114 memset(buf, 0, sizeof(buf));
115 if (addr->sa_family == AF_INET)
116 addr_len = sizeof(struct sockaddr_in);
117 else if (addr->sa_family == AF_INET6)
118 addr_len = sizeof(struct sockaddr_in6);
120 ret = getnameinfo(addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST);
122 strncpy(buf, gai_strerror(ret), sizeof(buf) - 1);
123 buf[sizeof(buf) - 1] = '\0';
125 snprintf(buf, sizeof(buf), (addr->sa_family == AF_INET6 && strchr(host, ':')) ? "[%s]:%s" : "%s:%s", host, service);
129 static size_t pretty_time(char *buf, const size_t len, unsigned long long left)
132 unsigned long long years, days, hours, minutes, seconds;
134 years = left / (365 * 24 * 60 * 60);
135 left = left % (365 * 24 * 60 * 60);
136 days = left / (24 * 60 * 60);
137 left = left % (24 * 60 * 60);
138 hours = left / (60 * 60);
139 left = left % (60 * 60);
144 offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "year%s" TERMINAL_RESET, offset ? ", " : "", years, years == 1 ? "" : "s");
146 offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "day%s" TERMINAL_RESET, offset ? ", " : "", days, days == 1 ? "" : "s");
148 offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "hour%s" TERMINAL_RESET, offset ? ", " : "", hours, hours == 1 ? "" : "s");
150 offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "minute%s" TERMINAL_RESET, offset ? ", " : "", minutes, minutes == 1 ? "" : "s");
152 offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "second%s" TERMINAL_RESET, offset ? ", " : "", seconds, seconds == 1 ? "" : "s");
157 static char *ago(const struct timespec64 *t)
159 static char buf[1024];
161 time_t now = time(NULL);
163 if (now == t->tv_sec)
164 strncpy(buf, "Now", sizeof(buf) - 1);
165 else if (now < t->tv_sec)
166 strncpy(buf, "(" TERMINAL_FG_RED "System clock wound backward; connection problems may ensue." TERMINAL_RESET ")", sizeof(buf) - 1);
168 offset = pretty_time(buf, sizeof(buf), now - t->tv_sec);
169 strncpy(buf + offset, " ago", sizeof(buf) - offset - 1);
171 buf[sizeof(buf) - 1] = '\0';
176 static char *every(uint16_t seconds)
178 static char buf[1024] = "every ";
180 pretty_time(buf + strlen("every "), sizeof(buf) - strlen("every ") - 1, seconds);
184 static char *bytes(uint64_t b)
186 static char buf[1024];
189 snprintf(buf, sizeof(buf), "%u " TERMINAL_FG_CYAN "B" TERMINAL_RESET, (unsigned int)b);
190 else if (b < 1024ULL * 1024ULL)
191 snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "KiB" TERMINAL_RESET, (double)b / 1024);
192 else if (b < 1024ULL * 1024ULL * 1024ULL)
193 snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "MiB" TERMINAL_RESET, (double)b / (1024 * 1024));
194 else if (b < 1024ULL * 1024ULL * 1024ULL * 1024ULL)
195 snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "GiB" TERMINAL_RESET, (double)b / (1024 * 1024 * 1024));
197 snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "TiB" TERMINAL_RESET, (double)b / (1024 * 1024 * 1024) / 1024);
202 static const char *COMMAND_NAME;
203 static void show_usage(void)
205 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);
208 static void pretty_print(struct wgdevice *device)
211 struct wgallowedip *allowedip;
213 terminal_printf(TERMINAL_RESET);
214 terminal_printf(TERMINAL_FG_GREEN TERMINAL_BOLD "interface" TERMINAL_RESET ": " TERMINAL_FG_GREEN "%s" TERMINAL_RESET "\n", device->name);
215 if (device->flags & WGDEVICE_HAS_PUBLIC_KEY)
216 terminal_printf(" " TERMINAL_BOLD "public key" TERMINAL_RESET ": %s\n", key(device->public_key));
217 if (device->flags & WGDEVICE_HAS_PRIVATE_KEY)
218 terminal_printf(" " TERMINAL_BOLD "private key" TERMINAL_RESET ": %s\n", masked_key(device->private_key));
219 if (device->listen_port)
220 terminal_printf(" " TERMINAL_BOLD "listening port" TERMINAL_RESET ": %u\n", device->listen_port);
222 terminal_printf(" " TERMINAL_BOLD "fwmark" TERMINAL_RESET ": 0x%x\n", device->fwmark);
223 if (device->first_peer) {
225 terminal_printf("\n");
227 for_each_wgpeer(device, peer) {
228 terminal_printf(TERMINAL_FG_YELLOW TERMINAL_BOLD "peer" TERMINAL_RESET ": " TERMINAL_FG_YELLOW "%s" TERMINAL_RESET "\n", key(peer->public_key));
229 if (peer->flags & WGPEER_HAS_PRESHARED_KEY)
230 terminal_printf(" " TERMINAL_BOLD "preshared key" TERMINAL_RESET ": %s\n", masked_key(peer->preshared_key));
231 if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
232 terminal_printf(" " TERMINAL_BOLD "endpoint" TERMINAL_RESET ": %s\n", endpoint(&peer->endpoint.addr));
233 terminal_printf(" " TERMINAL_BOLD "allowed ips" TERMINAL_RESET ": ");
234 if (peer->first_allowedip) {
235 for_each_wgallowedip(peer, allowedip)
236 terminal_printf("%s" TERMINAL_FG_CYAN "/" TERMINAL_RESET "%u%s", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? ", " : "\n");
238 terminal_printf("(none)\n");
239 if (peer->last_handshake_time.tv_sec)
240 terminal_printf(" " TERMINAL_BOLD "latest handshake" TERMINAL_RESET ": %s\n", ago(&peer->last_handshake_time));
241 if (peer->rx_bytes || peer->tx_bytes) {
242 terminal_printf(" " TERMINAL_BOLD "transfer" TERMINAL_RESET ": ");
243 terminal_printf("%s received, ", bytes(peer->rx_bytes));
244 terminal_printf("%s sent\n", bytes(peer->tx_bytes));
246 if (peer->persistent_keepalive_interval)
247 terminal_printf(" " TERMINAL_BOLD "persistent keepalive" TERMINAL_RESET ": %s\n", every(peer->persistent_keepalive_interval));
249 terminal_printf("\n");
253 static void dump_print(struct wgdevice *device, bool with_interface)
256 struct wgallowedip *allowedip;
259 printf("%s\t", device->name);
260 printf("%s\t", maybe_key(device->private_key, device->flags & WGDEVICE_HAS_PRIVATE_KEY));
261 printf("%s\t", maybe_key(device->public_key, device->flags & WGDEVICE_HAS_PUBLIC_KEY));
262 printf("%u\t", device->listen_port);
264 printf("0x%x\n", device->fwmark);
267 for_each_wgpeer(device, peer) {
269 printf("%s\t", device->name);
270 printf("%s\t", key(peer->public_key));
271 printf("%s\t", maybe_key(peer->preshared_key, peer->flags & WGPEER_HAS_PRESHARED_KEY));
272 if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
273 printf("%s\t", endpoint(&peer->endpoint.addr));
276 if (peer->first_allowedip) {
277 for_each_wgallowedip(peer, allowedip)
278 printf("%s/%u%c", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? ',' : '\t');
281 printf("%llu\t", (unsigned long long)peer->last_handshake_time.tv_sec);
282 printf("%" PRIu64 "\t%" PRIu64 "\t", (uint64_t)peer->rx_bytes, (uint64_t)peer->tx_bytes);
283 if (peer->persistent_keepalive_interval)
284 printf("%u\n", peer->persistent_keepalive_interval);
290 static bool ugly_print(struct wgdevice *device, const char *param, bool with_interface)
293 struct wgallowedip *allowedip;
295 if (!strcmp(param, "public-key")) {
297 printf("%s\t", device->name);
298 printf("%s\n", maybe_key(device->public_key, device->flags & WGDEVICE_HAS_PUBLIC_KEY));
299 } else if (!strcmp(param, "private-key")) {
301 printf("%s\t", device->name);
302 printf("%s\n", maybe_key(device->private_key, device->flags & WGDEVICE_HAS_PRIVATE_KEY));
303 } else if (!strcmp(param, "listen-port")) {
305 printf("%s\t", device->name);
306 printf("%u\n", device->listen_port);
307 } else if (!strcmp(param, "fwmark")) {
309 printf("%s\t", device->name);
311 printf("0x%x\n", device->fwmark);
314 } else if (!strcmp(param, "endpoints")) {
316 printf("%s\t", device->name);
317 for_each_wgpeer(device, peer) {
318 printf("%s\t", key(peer->public_key));
319 if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
320 printf("%s\n", endpoint(&peer->endpoint.addr));
324 } else if (!strcmp(param, "allowed-ips")) {
325 for_each_wgpeer(device, peer) {
327 printf("%s\t", device->name);
328 printf("%s\t", key(peer->public_key));
329 if (peer->first_allowedip) {
330 for_each_wgallowedip(peer, allowedip)
331 printf("%s/%u%c", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? ' ' : '\n');
335 } else if (!strcmp(param, "latest-handshakes")) {
336 for_each_wgpeer(device, peer) {
338 printf("%s\t", device->name);
339 printf("%s\t%llu\n", key(peer->public_key), (unsigned long long)peer->last_handshake_time.tv_sec);
341 } else if (!strcmp(param, "transfer")) {
342 for_each_wgpeer(device, peer) {
344 printf("%s\t", device->name);
345 printf("%s\t%" PRIu64 "\t%" PRIu64 "\n", key(peer->public_key), (uint64_t)peer->rx_bytes, (uint64_t)peer->tx_bytes);
347 } else if (!strcmp(param, "persistent-keepalive")) {
348 for_each_wgpeer(device, peer) {
350 printf("%s\t", device->name);
351 if (peer->persistent_keepalive_interval)
352 printf("%s\t%u\n", key(peer->public_key), peer->persistent_keepalive_interval);
354 printf("%s\toff\n", key(peer->public_key));
356 } else if (!strcmp(param, "preshared-keys")) {
357 for_each_wgpeer(device, peer) {
359 printf("%s\t", device->name);
360 printf("%s\t", key(peer->public_key));
361 printf("%s\n", maybe_key(peer->preshared_key, peer->flags & WGPEER_HAS_PRESHARED_KEY));
363 } else if (!strcmp(param, "peers")) {
364 for_each_wgpeer(device, peer) {
366 printf("%s\t", device->name);
367 printf("%s\n", key(peer->public_key));
369 } else if (!strcmp(param, "dump"))
370 dump_print(device, with_interface);
372 fprintf(stderr, "Invalid parameter: `%s'\n", param);
379 int show_main(int argc, const char *argv[])
383 COMMAND_NAME = argv[0];
390 if (argc == 1 || !strcmp(argv[1], "all")) {
391 char *interfaces = ipc_list_devices(), *interface;
394 perror("Unable to list interfaces");
398 interface = interfaces;
399 for (size_t len = 0; (len = strlen(interface)); interface += len + 1) {
400 struct wgdevice *device = NULL;
402 if (ipc_get_device(&device, interface) < 0) {
403 fprintf(stderr, "Unable to access interface %s: %s\n", interface, strerror(errno));
407 if (!ugly_print(device, argv[2], true)) {
409 free_wgdevice(device);
413 pretty_print(device);
414 if (strlen(interface + len + 1))
417 free_wgdevice(device);
421 } else if (!strcmp(argv[1], "interfaces")) {
422 char *interfaces, *interface;
428 interfaces = ipc_list_devices();
430 perror("Unable to list interfaces");
433 interface = interfaces;
434 for (size_t len = 0; (len = strlen(interface)); interface += len + 1)
435 printf("%s%c", interface, strlen(interface + len + 1) ? ' ' : '\n');
437 } else if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "help")))
440 struct wgdevice *device = NULL;
442 if (ipc_get_device(&device, argv[1]) < 0) {
443 perror("Unable to access interface");
447 if (!ugly_print(device, argv[2], false))
450 pretty_print(device);
451 free_wgdevice(device);