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