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
open_tun(const char * tun_name)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
close_tun(struct wg_user * wgu)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 *
wg_user_rcvthread(void * aaargh)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
rumpuser_wg_create(const char * tun_name,struct wg_softc * wg,struct wg_user ** wgup)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
rumpuser_wg_send_user(struct wg_user * wgu,struct iovec * iov,size_t iovlen)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
rumpuser_wg_send_peer(struct wg_user * wgu,struct sockaddr * sa,struct iovec * iov,size_t iovlen)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
rumpuser_wg_ioctl(struct wg_user * wgu,u_long cmd,void * data,int af)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
rumpuser_wg_sock_bind(struct wg_user * wgu,const uint16_t port)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
rumpuser_wg_destroy(struct wg_user * wgu)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 *
rumpuser_wg_get_tunname(struct wg_user * wgu)423 rumpuser_wg_get_tunname(struct wg_user *wgu)
424 {
425
426 return wgu->wgu_tun_name;
427 }
428 #endif
429