// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. * * Example only. Do not run in production. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include enum { MAX_PEERS = 65536, PORT = 49918 }; static struct { uint8_t base64_key[45]; bool have_seen; } peers[MAX_PEERS]; static unsigned int total_peers; static const char *cmd(const char *line, ...) { static char buf[2048]; char full_cmd[2048] = { 0 }; size_t len; FILE *f; va_list args; va_start(args, line); vsnprintf(full_cmd, 2047, line, args); va_end(args); f = popen(full_cmd, "r"); if (!f) { perror("popen"); exit(errno); } if (!fgets(buf, 2048, f)) { pclose(f); return NULL; } pclose(f); len = strlen(buf); if (!len) return NULL; if (buf[len - 1] == '\n') buf[len - 1] = '\0'; return buf; } static void read_peers(const char *interface) { char full_cmd[2048] = { 0 }; size_t len; FILE *f; snprintf(full_cmd, 2047, "wg show %s peers", interface); f = popen(full_cmd, "r"); if (!f) { perror("popen"); exit(errno); } for (;;) { if (!fgets(peers[total_peers].base64_key, 45, f)) break; len = strlen(peers[total_peers].base64_key); if (len != 44 && len != 45) continue; if (peers[total_peers].base64_key[len - 1] == '\n') peers[total_peers].base64_key[len - 1] = '\0'; ++total_peers; } pclose(f); } static void unbase64(uint8_t dstkey[32], const char *srckey) { uint8_t buf[33]; if (b64_pton(srckey, buf, 33) != 32) { fprintf(stderr, "Could not parse base64 key: %s\n", srckey); exit(EINVAL); } memcpy(dstkey, buf, 32); } static void apply_bpf(int sock, uint16_t port, uint32_t ip) { struct sock_filter filter[] = { BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 12 /* src ip */), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ip, 0, 5), BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20 /* src port */), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, PORT, 0, 3), BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 22 /* dst port */), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, port, 0, 1), BPF_STMT(BPF_RET + BPF_K, -1), BPF_STMT(BPF_RET + BPF_K, 0) }; struct sock_fprog filter_prog = { .len = sizeof(filter) / sizeof(filter[0]), .filter = filter }; if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog)) < 0) { perror("setsockopt(bpf)"); exit(errno); } } int main(int argc, char *argv[]) { struct sockaddr_in addr = { .sin_family = AF_INET }; struct { struct udphdr udp; uint8_t my_pubkey[32]; uint8_t their_pubkey[32]; } __attribute__((packed)) packet = { .udp = { .len = htons(sizeof(packet)), .dest = htons(PORT) } }; struct { struct iphdr iphdr; struct udphdr udp; uint32_t ip; uint16_t port; } __attribute__((packed)) reply; ssize_t len; int sock, i; bool repeat; struct hostent *ent; const char *server = argv[1], *interface = argv[2]; if (argc < 3) { fprintf(stderr, "Usage: %s SERVER WIREGUARD_INTERFACE\nExample:\n %s demo.wireguard.com wg0\n", argv[0], argv[0]); return EINVAL; } if (getuid() != 0) { fprintf(stderr, "Must be root!\n"); return EPERM; } ent = gethostbyname2(server, AF_INET); if (!ent) { herror("gethostbyname2"); return h_errno; } addr.sin_addr = *(struct in_addr *)ent->h_addr; read_peers(interface); cmd("ip link set %s up", interface); unbase64(packet.my_pubkey, cmd("wg show %s public-key", interface)); packet.udp.source = htons(atoi(cmd("wg show %s listen-port", interface))); /* We use raw sockets so that the WireGuard interface can actually own the real socket. */ sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP); if (sock < 0) { perror("socket"); return errno; } apply_bpf(sock, ntohs(packet.udp.source), ntohl(addr.sin_addr.s_addr)); check_again: repeat = false; for (i = 0; i < total_peers; ++i) { if (peers[i].have_seen) continue; printf("[+] Requesting IP and port of %s: ", peers[i].base64_key); unbase64(packet.their_pubkey, peers[i].base64_key); if (sendto(sock, &packet, sizeof(packet), 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) { putchar('\n'); perror("sendto"); return errno; } len = recv(sock, &reply, sizeof(reply), 0); if (len < 0) { putchar('\n'); perror("recv"); return errno; } if (len != sizeof(reply)) { printf("server does not yet have it\n"); repeat = true; } else { printf("%s:%d\n", inet_ntoa(*(struct in_addr *)&reply.ip), ntohs(reply.port)); peers[i].have_seen = true; cmd("wg set %s peer %s persistent-keepalive 25 endpoint %s:%d", interface, peers[i].base64_key, inet_ntoa(*(struct in_addr *)&reply.ip), ntohs(reply.port)); } } if (repeat) { sleep(2); goto check_again; } close(sock); return 0; }