1 /* $NetBSD: wg_user.c,v 1.3 2020/08/27 02:51:15 riastradh Exp $ */ 2 3 /* 4 * Copyright (C) Ryota Ozaki <ozaki.ryota@gmail.com> 5 * All rights reserved. 6 * 7 * Based on wg_user.c by Antti Kantee. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 __KERNEL_RCSID(0, "$NetBSD: wg_user.c,v 1.3 2020/08/27 02:51:15 riastradh Exp $"); 33 34 #ifndef _KERNEL 35 #include <sys/types.h> 36 #include <sys/ioctl.h> 37 #include <sys/uio.h> 38 #include <sys/socket.h> 39 #include <sys/param.h> 40 41 #include <net/if.h> 42 #include <net/if_tun.h> 43 44 #include <netinet/in.h> 45 46 #include <assert.h> 47 #include <errno.h> 48 #include <fcntl.h> 49 #include <inttypes.h> 50 #include <poll.h> 51 #include <pthread.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <unistd.h> 56 57 #include <rump/rumpuser_component.h> 58 59 #include "wg_user.h" 60 61 struct wg_user { 62 struct wg_softc *wgu_sc; 63 int wgu_devnum; 64 char wgu_tun_name[IFNAMSIZ]; 65 66 int wgu_fd; 67 int wgu_sock4; 68 int wgu_sock6; 69 int wgu_pipe[2]; 70 pthread_t wgu_rcvthr; 71 72 int wgu_dying; 73 74 char wgu_rcvbuf[9018]; /* jumbo frame max len */ 75 }; 76 77 static int 78 open_tun(const char *tun_name) 79 { 80 char tun_path[MAXPATHLEN]; 81 int n, fd, error; 82 83 n = snprintf(tun_path, sizeof(tun_path), "/dev/%s", tun_name); 84 if (n == MAXPATHLEN) 85 return E2BIG; 86 87 fd = open(tun_path, O_RDWR); 88 if (fd == -1) { 89 fprintf(stderr, "%s: can't open %s: %s\n", 90 __func__, tun_name, strerror(errno)); 91 } 92 93 int i = 1; 94 error = ioctl(fd, TUNSLMODE, &i); 95 if (error == -1) { 96 close(fd); 97 fd = -1; 98 } 99 100 return fd; 101 } 102 103 static void 104 close_tun(struct wg_user *wgu) 105 { 106 int s; 107 struct ifreq ifr = {}; 108 109 close(wgu->wgu_fd); 110 111 s = socket(AF_INET, SOCK_DGRAM, 0); 112 if (s == -1) 113 return; /* XXX */ 114 strcpy(ifr.ifr_name, wgu->wgu_tun_name); 115 (void)ioctl(s, SIOCIFDESTROY, &ifr); 116 close(s); 117 } 118 119 static void * 120 wg_user_rcvthread(void *aaargh) 121 { 122 struct wg_user *wgu = aaargh; 123 struct pollfd pfd[4]; 124 ssize_t nn = 0; 125 int prv; 126 127 rumpuser_component_kthread(); 128 129 pfd[0].fd = wgu->wgu_fd; 130 pfd[0].events = POLLIN; 131 pfd[1].fd = wgu->wgu_pipe[0]; 132 pfd[1].events = POLLIN; 133 pfd[2].fd = wgu->wgu_sock4; 134 pfd[2].events = POLLIN; 135 pfd[3].fd = wgu->wgu_sock6; 136 pfd[3].events = POLLIN; 137 138 while (!wgu->wgu_dying) { 139 struct iovec iov[2]; 140 141 prv = poll(pfd, 4, -1); 142 if (prv == 0) 143 continue; 144 if (prv == -1) { 145 /* XXX */ 146 fprintf(stderr, "%s: poll error: %d\n", 147 wgu->wgu_tun_name, errno); 148 sleep(1); 149 continue; 150 } 151 if (pfd[1].revents & POLLIN) 152 continue; 153 154 /* Receive user packets from tun */ 155 if (pfd[0].revents & POLLIN) { 156 nn = read(wgu->wgu_fd, wgu->wgu_rcvbuf, sizeof(wgu->wgu_rcvbuf)); 157 if (nn == -1 && errno == EAGAIN) 158 continue; 159 160 if (nn < 1) { 161 /* XXX */ 162 fprintf(stderr, "%s: receive failed\n", 163 wgu->wgu_tun_name); 164 sleep(1); 165 continue; 166 } 167 168 iov[0].iov_base = wgu->wgu_rcvbuf; 169 iov[0].iov_len = ((struct sockaddr *)wgu->wgu_rcvbuf)->sa_len; 170 171 iov[1].iov_base = (char *)wgu->wgu_rcvbuf + iov[0].iov_len; 172 iov[1].iov_len = nn - iov[0].iov_len; 173 174 rumpuser_component_schedule(NULL); 175 rumpkern_wg_recv_user(wgu->wgu_sc, iov, 2); 176 rumpuser_component_unschedule(); 177 } 178 179 /* Receive wg UDP/IPv4 packets from a peer */ 180 if (pfd[2].revents & POLLIN) { 181 struct sockaddr_in sin; 182 socklen_t len = sizeof(sin); 183 nn = recvfrom(wgu->wgu_sock4, wgu->wgu_rcvbuf, 184 sizeof(wgu->wgu_rcvbuf), 0, (struct sockaddr *)&sin, 185 &len); 186 if (nn == -1 && errno == EAGAIN) 187 continue; 188 if (len != sizeof(sin)) 189 continue; 190 iov[0].iov_base = &sin; 191 iov[0].iov_len = sin.sin_len; 192 193 iov[1].iov_base = wgu->wgu_rcvbuf; 194 iov[1].iov_len = nn; 195 196 rumpuser_component_schedule(NULL); 197 rumpkern_wg_recv_peer(wgu->wgu_sc, iov, 2); 198 rumpuser_component_unschedule(); 199 } 200 201 /* Receive wg UDP/IPv6 packets from a peer */ 202 if (pfd[3].revents & POLLIN) { 203 struct sockaddr_in6 sin6; 204 socklen_t len = sizeof(sin6); 205 nn = recvfrom(wgu->wgu_sock6, wgu->wgu_rcvbuf, 206 sizeof(wgu->wgu_rcvbuf), 0, (struct sockaddr *)&sin6, 207 &len); 208 if (nn == -1 && errno == EAGAIN) 209 continue; 210 if (len != sizeof(sin6)) 211 continue; 212 iov[0].iov_base = &sin6; 213 iov[0].iov_len = sin6.sin6_len; 214 215 iov[1].iov_base = wgu->wgu_rcvbuf; 216 iov[1].iov_len = nn; 217 218 rumpuser_component_schedule(NULL); 219 rumpkern_wg_recv_peer(wgu->wgu_sc, iov, 2); 220 rumpuser_component_unschedule(); 221 } 222 } 223 224 assert(wgu->wgu_dying); 225 226 rumpuser_component_kthread_release(); 227 return NULL; 228 } 229 230 int 231 rumpuser_wg_create(const char *tun_name, struct wg_softc *wg, 232 struct wg_user **wgup) 233 { 234 struct wg_user *wgu = NULL; 235 void *cookie; 236 int rv; 237 238 cookie = rumpuser_component_unschedule(); 239 240 wgu = malloc(sizeof(*wgu)); 241 if (wgu == NULL) { 242 rv = errno; 243 goto oerr1; 244 } 245 246 if (strlcpy(wgu->wgu_tun_name, tun_name, sizeof(wgu->wgu_tun_name)) 247 >= sizeof(wgu->wgu_tun_name)) { 248 rv = EINVAL; 249 goto oerr2; 250 } 251 wgu->wgu_sc = wg; 252 253 wgu->wgu_fd = open_tun(tun_name); 254 if (wgu->wgu_fd == -1) { 255 rv = errno; 256 goto oerr2; 257 } 258 259 if (pipe(wgu->wgu_pipe) == -1) { 260 rv = errno; 261 goto oerr3; 262 } 263 264 wgu->wgu_sock4 = socket(AF_INET, SOCK_DGRAM, 0); 265 wgu->wgu_sock6 = socket(AF_INET6, SOCK_DGRAM, 0); 266 if (wgu->wgu_sock4 == -1 || wgu->wgu_sock6 == -1) { 267 rv = errno; 268 goto oerr4; 269 } 270 271 rv = pthread_create(&wgu->wgu_rcvthr, NULL, wg_user_rcvthread, wgu); 272 if (rv != 0) 273 goto oerr5; 274 275 rumpuser_component_schedule(cookie); 276 *wgup = wgu; 277 return 0; 278 279 oerr5: 280 if (wgu->wgu_sock4 != -1) 281 close(wgu->wgu_sock4); 282 if (wgu->wgu_sock6 != -1) 283 close(wgu->wgu_sock6); 284 oerr4: 285 close(wgu->wgu_pipe[0]); 286 close(wgu->wgu_pipe[1]); 287 oerr3: 288 close_tun(wgu); 289 oerr2: 290 free(wgu); 291 oerr1: 292 rumpuser_component_schedule(cookie); 293 return rumpuser_component_errtrans(rv); 294 } 295 296 /* 297 * Send decrypted packets to users via a tun. 298 */ 299 void 300 rumpuser_wg_send_user(struct wg_user *wgu, struct iovec *iov, size_t iovlen) 301 { 302 void *cookie = rumpuser_component_unschedule(); 303 ssize_t idontcare __attribute__((__unused__)); 304 305 /* 306 * no need to check for return value; packets may be dropped 307 * 308 * ... sorry, I spoke too soon. We need to check it because 309 * apparently gcc reinvented const poisoning and it's very 310 * hard to say "thanks, I know I'm not using the result, 311 * but please STFU and let's get on with something useful". 312 * So let's trick gcc into letting us share the compiler 313 * experience. 314 */ 315 idontcare = writev(wgu->wgu_fd, iov, iovlen); 316 317 rumpuser_component_schedule(cookie); 318 } 319 320 /* 321 * Send wg messages to a peer. 322 */ 323 int 324 rumpuser_wg_send_peer(struct wg_user *wgu, struct sockaddr *sa, 325 struct iovec *iov, size_t iovlen) 326 { 327 void *cookie = rumpuser_component_unschedule(); 328 int s, error = 0; 329 size_t i; 330 ssize_t sent; 331 332 if (sa->sa_family == AF_INET) 333 s = wgu->wgu_sock4; 334 else 335 s = wgu->wgu_sock6; 336 337 for (i = 0; i < iovlen; i++) { 338 sent = sendto(s, iov[i].iov_base, iov[i].iov_len, 0, sa, 339 sa->sa_len); 340 if (sent == -1 || (size_t)sent != iov[i].iov_len) { 341 error = errno; 342 break; 343 } 344 } 345 346 rumpuser_component_schedule(cookie); 347 348 return error; 349 } 350 351 int 352 rumpuser_wg_ioctl(struct wg_user *wgu, u_long cmd, void *data, int af) 353 { 354 void *cookie = rumpuser_component_unschedule(); 355 int s, error; 356 357 s = socket(af, SOCK_DGRAM, 0); 358 if (s == -1) 359 return errno; 360 error = ioctl(s, cmd, data); 361 close(s); 362 363 rumpuser_component_schedule(cookie); 364 365 return error == -1 ? errno : 0; 366 } 367 368 int 369 rumpuser_wg_sock_bind(struct wg_user *wgu, const uint16_t port) 370 { 371 int error; 372 struct sockaddr_in sin; 373 struct sockaddr_in6 sin6; 374 375 memset(&sin, 0, sizeof(sin)); 376 sin.sin_family = AF_INET; 377 sin.sin_len = sizeof(sin); 378 sin.sin_addr.s_addr = INADDR_ANY; 379 sin.sin_port = htons(port); 380 381 error = bind(wgu->wgu_sock4, (struct sockaddr *)&sin, sizeof(sin)); 382 if (error == -1) 383 return errno; 384 385 memset(&sin6, 0, sizeof(sin6)); 386 sin6.sin6_family = AF_INET6; 387 sin6.sin6_len = sizeof(sin6); 388 sin6.sin6_addr = in6addr_any; 389 sin6.sin6_port = htons(port); 390 391 error = bind(wgu->wgu_sock6, (struct sockaddr *)&sin6, sizeof(sin6)); 392 if (error == -1) 393 return errno; 394 395 return 0; 396 } 397 398 void 399 rumpuser_wg_destroy(struct wg_user *wgu) 400 { 401 void *cookie = rumpuser_component_unschedule(); 402 403 wgu->wgu_dying = 1; 404 if (write(wgu->wgu_pipe[1], 405 &wgu->wgu_dying, sizeof(wgu->wgu_dying)) == -1) { 406 /* 407 * this is here mostly to avoid a compiler warning 408 * about ignoring the return value of write() 409 */ 410 fprintf(stderr, "%s: failed to signal thread\n", 411 wgu->wgu_tun_name); 412 } 413 pthread_join(wgu->wgu_rcvthr, NULL); 414 close_tun(wgu); 415 close(wgu->wgu_pipe[0]); 416 close(wgu->wgu_pipe[1]); 417 free(wgu); 418 419 rumpuser_component_schedule(cookie); 420 } 421 422 char * 423 rumpuser_wg_get_tunname(struct wg_user *wgu) 424 { 425 426 return wgu->wgu_tun_name; 427 } 428 #endif 429