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