xref: /freebsd/contrib/wireguard-tools/show.c (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
1 // SPDX-License-Identifier: GPL-2.0 OR MIT
2 /*
3  * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4  */
5 
6 #include <arpa/inet.h>
7 #include <inttypes.h>
8 #include <netinet/in.h>
9 #include <sys/socket.h>
10 #include <net/if.h>
11 #include <stdbool.h>
12 #include <stddef.h>
13 #include <stdint.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <time.h>
19 #include <netdb.h>
20 
21 #include "containers.h"
22 #include "ipc.h"
23 #include "terminal.h"
24 #include "encoding.h"
25 #include "subcommands.h"
26 
27 static int peer_cmp(const void *first, const void *second)
28 {
29 	time_t diff;
30 	const struct wgpeer *a = *(void *const *)first, *b = *(void *const *)second;
31 
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))
33 		return 1;
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))
35 		return -1;
36 	diff = a->last_handshake_time.tv_sec - b->last_handshake_time.tv_sec;
37 	if (!diff)
38 		diff = a->last_handshake_time.tv_nsec - b->last_handshake_time.tv_nsec;
39 	if (diff < 0)
40 		return 1;
41 	if (diff > 0)
42 		return -1;
43 	return 0;
44 }
45 
46 /* This, hilariously, is not the right way to sort a linked list... */
47 static void sort_peers(struct wgdevice *device)
48 {
49 	size_t peer_count = 0, i = 0;
50 	struct wgpeer *peer, **peers;
51 
52 	for_each_wgpeer(device, peer)
53 		++peer_count;
54 	if (!peer_count)
55 		return;
56 	peers = calloc(peer_count, sizeof(*peers));
57 	if (!peers)
58 		return;
59 	for_each_wgpeer(device, peer)
60 		peers[i++] = 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];
65 	}
66 	peers[peer_count - 1]->next_peer = NULL;
67 	free(peers);
68 }
69 
70 static char *key(const uint8_t key[static WG_KEY_LEN])
71 {
72 	static char base64[WG_KEY_LEN_BASE64];
73 
74 	key_to_base64(base64, key);
75 	return base64;
76 }
77 
78 static const char *maybe_key(const uint8_t maybe_key[static WG_KEY_LEN], bool have_it)
79 {
80 	if (!have_it)
81 		return "(none)";
82 	return key(maybe_key);
83 }
84 
85 static const char *masked_key(const uint8_t masked_key[static WG_KEY_LEN])
86 {
87 	const char *var = getenv("WG_HIDE_KEYS");
88 
89 	if (var && !strcmp(var, "never"))
90 		return key(masked_key);
91 	return "(hidden)";
92 }
93 
94 static char *ip(const struct wgallowedip *ip)
95 {
96 	static char buf[INET6_ADDRSTRLEN + 1];
97 
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);
103 	return buf;
104 }
105 
106 static char *endpoint(const struct sockaddr *addr)
107 {
108 	char host[4096 + 1];
109 	char service[512 + 1];
110 	static char buf[sizeof(host) + sizeof(service) + 4];
111 	int ret;
112 	socklen_t addr_len = 0;
113 
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);
119 
120 	ret = getnameinfo(addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST);
121 	if (ret) {
122 		strncpy(buf, gai_strerror(ret), sizeof(buf) - 1);
123 		buf[sizeof(buf) - 1] = '\0';
124 	} else
125 		snprintf(buf, sizeof(buf), (addr->sa_family == AF_INET6 && strchr(host, ':')) ? "[%s]:%s" : "%s:%s", host, service);
126 	return buf;
127 }
128 
129 static size_t pretty_time(char *buf, const size_t len, unsigned long long left)
130 {
131 	size_t offset = 0;
132 	unsigned long long years, days, hours, minutes, seconds;
133 
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);
140 	minutes = left / 60;
141 	seconds = left % 60;
142 
143 	if (years)
144 		offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "year%s" TERMINAL_RESET, offset ? ", " : "", years, years == 1 ? "" : "s");
145 	if (days)
146 		offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN  "day%s" TERMINAL_RESET, offset ? ", " : "", days, days == 1 ? "" : "s");
147 	if (hours)
148 		offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN  "hour%s" TERMINAL_RESET, offset ? ", " : "", hours, hours == 1 ? "" : "s");
149 	if (minutes)
150 		offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "minute%s" TERMINAL_RESET, offset ? ", " : "", minutes, minutes == 1 ? "" : "s");
151 	if (seconds)
152 		offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN  "second%s" TERMINAL_RESET, offset ? ", " : "", seconds, seconds == 1 ? "" : "s");
153 
154 	return offset;
155 }
156 
157 static char *ago(const struct timespec64 *t)
158 {
159 	static char buf[1024];
160 	size_t offset;
161 	time_t now = time(NULL);
162 
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);
167 	else {
168 		offset = pretty_time(buf, sizeof(buf), now - t->tv_sec);
169 		strncpy(buf + offset, " ago", sizeof(buf) - offset - 1);
170 	}
171 	buf[sizeof(buf) - 1] = '\0';
172 
173 	return buf;
174 }
175 
176 static char *every(uint16_t seconds)
177 {
178 	static char buf[1024] = "every ";
179 
180 	pretty_time(buf + strlen("every "), sizeof(buf) - strlen("every ") - 1, seconds);
181 	return buf;
182 }
183 
184 static char *bytes(uint64_t b)
185 {
186 	static char buf[1024];
187 
188 	if (b < 1024ULL)
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));
196 	else
197 		snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "TiB" TERMINAL_RESET, (double)b / (1024 * 1024 * 1024) / 1024);
198 
199 	return buf;
200 }
201 
202 static const char *COMMAND_NAME;
203 static void show_usage(void)
204 {
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);
206 }
207 
208 static void pretty_print(struct wgdevice *device)
209 {
210 	struct wgpeer *peer;
211 	struct wgallowedip *allowedip;
212 
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);
221 	if (device->fwmark)
222 		terminal_printf("  " TERMINAL_BOLD "fwmark" TERMINAL_RESET ": 0x%x\n", device->fwmark);
223 	if (device->first_peer) {
224 		sort_peers(device);
225 		terminal_printf("\n");
226 	}
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");
237 		} else
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));
245 		}
246 		if (peer->persistent_keepalive_interval)
247 			terminal_printf("  " TERMINAL_BOLD "persistent keepalive" TERMINAL_RESET ": %s\n", every(peer->persistent_keepalive_interval));
248 		if (peer->next_peer)
249 			terminal_printf("\n");
250 	}
251 }
252 
253 static void dump_print(struct wgdevice *device, bool with_interface)
254 {
255 	struct wgpeer *peer;
256 	struct wgallowedip *allowedip;
257 
258 	if (with_interface)
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);
263 	if (device->fwmark)
264 		printf("0x%x\n", device->fwmark);
265 	else
266 		printf("off\n");
267 	for_each_wgpeer(device, peer) {
268 		if (with_interface)
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));
274 		else
275 			printf("(none)\t");
276 		if (peer->first_allowedip) {
277 			for_each_wgallowedip(peer, allowedip)
278 				printf("%s/%u%c", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? ',' : '\t');
279 		} else
280 			printf("(none)\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);
285 		else
286 			printf("off\n");
287 	}
288 }
289 
290 static bool ugly_print(struct wgdevice *device, const char *param, bool with_interface)
291 {
292 	struct wgpeer *peer;
293 	struct wgallowedip *allowedip;
294 
295 	if (!strcmp(param, "public-key")) {
296 		if (with_interface)
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")) {
300 		if (with_interface)
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")) {
304 		if (with_interface)
305 			printf("%s\t", device->name);
306 		printf("%u\n", device->listen_port);
307 	} else if (!strcmp(param, "fwmark")) {
308 		if (with_interface)
309 			printf("%s\t", device->name);
310 		if (device->fwmark)
311 			printf("0x%x\n", device->fwmark);
312 		else
313 			printf("off\n");
314 	} else if (!strcmp(param, "endpoints")) {
315 		if (with_interface)
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));
321 			else
322 				printf("(none)\n");
323 		}
324 	} else if (!strcmp(param, "allowed-ips")) {
325 		for_each_wgpeer(device, peer) {
326 			if (with_interface)
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');
332 			} else
333 				printf("(none)\n");
334 		}
335 	} else if (!strcmp(param, "latest-handshakes")) {
336 		for_each_wgpeer(device, peer) {
337 			if (with_interface)
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);
340 		}
341 	} else if (!strcmp(param, "transfer")) {
342 		for_each_wgpeer(device, peer) {
343 			if (with_interface)
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);
346 		}
347 	} else if (!strcmp(param, "persistent-keepalive")) {
348 		for_each_wgpeer(device, peer) {
349 			if (with_interface)
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);
353 			else
354 				printf("%s\toff\n", key(peer->public_key));
355 		}
356 	} else if (!strcmp(param, "preshared-keys")) {
357 		for_each_wgpeer(device, peer) {
358 			if (with_interface)
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));
362 		}
363 	} else if (!strcmp(param, "peers")) {
364 		for_each_wgpeer(device, peer) {
365 			if (with_interface)
366 				printf("%s\t", device->name);
367 			printf("%s\n", key(peer->public_key));
368 		}
369 	} else if (!strcmp(param, "dump"))
370 		dump_print(device, with_interface);
371 	else {
372 		fprintf(stderr, "Invalid parameter: `%s'\n", param);
373 		show_usage();
374 		return false;
375 	}
376 	return true;
377 }
378 
379 int show_main(int argc, const char *argv[])
380 {
381 	int ret = 0;
382 
383 	COMMAND_NAME = argv[0];
384 
385 	if (argc > 3) {
386 		show_usage();
387 		return 1;
388 	}
389 
390 	if (argc == 1 || !strcmp(argv[1], "all")) {
391 		char *interfaces = ipc_list_devices(), *interface;
392 
393 		if (!interfaces) {
394 			perror("Unable to list interfaces");
395 			return 1;
396 		}
397 		ret = !!*interfaces;
398 		interface = interfaces;
399 		for (size_t len = 0; (len = strlen(interface)); interface += len + 1) {
400 			struct wgdevice *device = NULL;
401 
402 			if (ipc_get_device(&device, interface) < 0) {
403 				fprintf(stderr, "Unable to access interface %s: %s\n", interface, strerror(errno));
404 				continue;
405 			}
406 			if (argc == 3) {
407 				if (!ugly_print(device, argv[2], true)) {
408 					ret = 1;
409 					free_wgdevice(device);
410 					break;
411 				}
412 			} else {
413 				pretty_print(device);
414 				if (strlen(interface + len + 1))
415 					printf("\n");
416 			}
417 			free_wgdevice(device);
418 			ret = 0;
419 		}
420 		free(interfaces);
421 	} else if (!strcmp(argv[1], "interfaces")) {
422 		char *interfaces, *interface;
423 
424 		if (argc > 2) {
425 			show_usage();
426 			return 1;
427 		}
428 		interfaces = ipc_list_devices();
429 		if (!interfaces) {
430 			perror("Unable to list interfaces");
431 			return 1;
432 		}
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');
436 		free(interfaces);
437 	} else if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "help")))
438 		show_usage();
439 	else {
440 		struct wgdevice *device = NULL;
441 
442 		if (ipc_get_device(&device, argv[1]) < 0) {
443 			perror("Unable to access interface");
444 			return 1;
445 		}
446 		if (argc == 3) {
447 			if (!ugly_print(device, argv[2], false))
448 				ret = 1;
449 		} else
450 			pretty_print(device);
451 		free_wgdevice(device);
452 	}
453 	return ret;
454 }
455