VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/NAT/fwudp.c@ 93115

Last change on this file since 93115 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 13.1 KB
Line 
1/* $Id: fwudp.c 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * NAT Network - UDP port-forwarding.
4 */
5
6/*
7 * Copyright (C) 2013-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_NAT_SERVICE
19
20#include "winutils.h"
21#include "proxy.h"
22#include "proxy_pollmgr.h"
23#include "portfwd.h"
24#include "pxremap.h"
25
26#ifndef RT_OS_WINDOWS
27#include <sys/types.h>
28#include <sys/socket.h>
29#include <stdio.h>
30#include <string.h>
31#include <poll.h>
32
33#include <err.h> /* BSD'ism */
34#else
35#include <stdio.h>
36#include <string.h>
37#include "winpoll.h"
38#endif
39
40#include "lwip/opt.h"
41#include "lwip/memp.h" /* XXX: for bulk delete of pcbs */
42
43#include "lwip/sys.h"
44#include "lwip/tcpip.h"
45#include "lwip/udp.h"
46
47struct fwudp_dgram {
48 struct pbuf *p;
49 ipX_addr_t src_addr;
50 u16_t src_port;
51};
52
53/**
54 * UDP port-forwarding.
55 *
56 * Unlike pxudp that uses 1:1 mapping between pcb and socket, for
57 * port-forwarded UDP the setup is bit more elaborated.
58 *
59 * For fwtcp things are simple since incoming TCP connection get a new
60 * socket that we just hand off to pxtcp. Thus fwtcp only handles
61 * connection initiation.
62 *
63 * For fwudp all proxied UDP conversations share the same socket, so
64 * single fwudp multiplexes to several UDP pcbs.
65 *
66 * XXX: TODO: Currently pcbs point back directly to fwudp. It might
67 * make sense to introduce a per-pcb structure that points to fwudp
68 * and carries additional information, like pre-mapped peer address.
69 */
70struct fwudp {
71 /**
72 * Our poll manager handler.
73 */
74 struct pollmgr_handler pmhdl;
75
76 /**
77 * Forwarding specification.
78 */
79 struct fwspec fwspec;
80
81 /**
82 * XXX: lwip-format copy of destination
83 */
84 ipX_addr_t dst_addr;
85 u16_t dst_port;
86
87 /**
88 * Listening socket.
89 */
90 SOCKET sock;
91
92 /**
93 * Ring-buffer for inbound datagrams.
94 */
95 struct {
96 struct fwudp_dgram *buf;
97 size_t bufsize;
98 volatile size_t vacant;
99 volatile size_t unsent;
100 } inbuf;
101
102 struct tcpip_msg msg_send;
103 struct tcpip_msg msg_delete;
104
105 struct fwudp *next;
106};
107
108
109struct fwudp *fwudp_create(struct fwspec *);
110
111/* poll manager callback for fwudp socket */
112static int fwudp_pmgr_pump(struct pollmgr_handler *, SOCKET, int);
113
114/* lwip thread callbacks called via proxy_lwip_post() */
115static void fwudp_pcb_send(void *);
116static void fwudp_pcb_delete(void *);
117
118static void fwudp_pcb_recv(void *, struct udp_pcb *, struct pbuf *, ip_addr_t *, u16_t);
119static void fwudp_pcb_forward_outbound(struct fwudp *, struct udp_pcb *, struct pbuf *);
120
121
122/**
123 * Linked list of active fwtcp forwarders.
124 */
125struct fwudp *fwudp_list = NULL;
126
127
128void
129fwudp_init(void)
130{
131 return;
132}
133
134
135void
136fwudp_add(struct fwspec *fwspec)
137{
138 struct fwudp *fwudp;
139
140 fwudp = fwudp_create(fwspec);
141 if (fwudp == NULL) {
142 DPRINTF0(("%s: failed to add rule for UDP ...\n", __func__));
143 return;
144 }
145
146 DPRINTF0(("%s\n", __func__));
147 /* fwudp_create has put fwudp on the linked list */
148}
149
150
151void
152fwudp_del(struct fwspec *fwspec)
153{
154 struct fwudp *fwudp;
155 struct fwudp **pprev;
156
157 for (pprev = &fwudp_list; (fwudp = *pprev) != NULL; pprev = &fwudp->next) {
158 if (fwspec_equal(&fwudp->fwspec, fwspec)) {
159 *pprev = fwudp->next;
160 fwudp->next = NULL;
161 break;
162 }
163 }
164
165 if (fwudp == NULL) {
166 DPRINTF0(("%s: not found\n", __func__));
167 return;
168 }
169
170 DPRINTF0(("%s\n", __func__));
171
172 pollmgr_del_slot(fwudp->pmhdl.slot);
173 fwudp->pmhdl.slot = -1;
174
175 /* let pending msg_send be processed before we delete fwudp */
176 proxy_lwip_post(&fwudp->msg_delete);
177}
178
179
180struct fwudp *
181fwudp_create(struct fwspec *fwspec)
182{
183 struct fwudp *fwudp;
184 SOCKET sock;
185 int status;
186
187 sock = proxy_bound_socket(fwspec->sdom, fwspec->stype, &fwspec->src.sa);
188 if (sock == INVALID_SOCKET) {
189 return NULL;
190 }
191
192 fwudp = (struct fwudp *)malloc(sizeof(*fwudp));
193 if (fwudp == NULL) {
194 closesocket(sock);
195 return NULL;
196 }
197
198 fwudp->pmhdl.callback = fwudp_pmgr_pump;
199 fwudp->pmhdl.data = (void *)fwudp;
200 fwudp->pmhdl.slot = -1;
201
202 fwudp->sock = sock;
203 fwudp->fwspec = *fwspec; /* struct copy */
204
205 /* XXX */
206 if (fwspec->sdom == PF_INET) {
207 struct sockaddr_in *dst4 = &fwspec->dst.sin;
208 memcpy(&fwudp->dst_addr.ip4, &dst4->sin_addr, sizeof(ip_addr_t));
209 fwudp->dst_port = htons(dst4->sin_port);
210 }
211 else { /* PF_INET6 */
212 struct sockaddr_in6 *dst6 = &fwspec->dst.sin6;
213 memcpy(&fwudp->dst_addr.ip6, &dst6->sin6_addr, sizeof(ip6_addr_t));
214 fwudp->dst_port = htons(dst6->sin6_port);
215 }
216
217 fwudp->inbuf.bufsize = 256; /* elements */
218 fwudp->inbuf.buf
219 = (struct fwudp_dgram *)calloc(fwudp->inbuf.bufsize,
220 sizeof(struct fwudp_dgram));
221 if (fwudp->inbuf.buf == NULL) {
222 closesocket(sock);
223 free(fwudp);
224 return (NULL);
225 }
226 fwudp->inbuf.vacant = 0;
227 fwudp->inbuf.unsent = 0;
228
229#define CALLBACK_MSG(MSG, FUNC) \
230 do { \
231 fwudp->MSG.type = TCPIP_MSG_CALLBACK_STATIC; \
232 fwudp->MSG.sem = NULL; \
233 fwudp->MSG.msg.cb.function = FUNC; \
234 fwudp->MSG.msg.cb.ctx = (void *)fwudp; \
235 } while (0)
236
237 CALLBACK_MSG(msg_send, fwudp_pcb_send);
238 CALLBACK_MSG(msg_delete, fwudp_pcb_delete);
239
240#undef CALLBACK_MSG
241
242 status = pollmgr_add(&fwudp->pmhdl, fwudp->sock, POLLIN);
243 if (status < 0) {
244 closesocket(sock);
245 free(fwudp->inbuf.buf);
246 free(fwudp);
247 return NULL;
248 }
249
250 fwudp->next = fwudp_list;
251 fwudp_list = fwudp;
252
253 return fwudp;
254}
255
256
257/**
258 * Poll manager callaback for fwudp::sock
259 */
260int
261fwudp_pmgr_pump(struct pollmgr_handler *handler, SOCKET fd, int revents)
262{
263 struct fwudp *fwudp;
264 struct sockaddr_storage ss;
265 socklen_t sslen = sizeof(ss);
266 size_t beg, lim;
267 struct fwudp_dgram *dgram;
268 struct pbuf *p;
269 ssize_t nread;
270 int status;
271 err_t error;
272
273 fwudp = (struct fwudp *)handler->data;
274
275 LWIP_ASSERT1(fwudp != NULL);
276 LWIP_ASSERT1(fd == fwudp->sock);
277 LWIP_ASSERT1(revents == POLLIN);
278 LWIP_UNUSED_ARG(fd);
279 LWIP_UNUSED_ARG(revents);
280
281#ifdef RT_OS_WINDOWS
282 nread = recvfrom(fwudp->sock, (char *)pollmgr_udpbuf, sizeof(pollmgr_udpbuf), 0,
283 (struct sockaddr *)&ss, &sslen);
284#else
285 nread = recvfrom(fwudp->sock, pollmgr_udpbuf, sizeof(pollmgr_udpbuf), 0,
286 (struct sockaddr *)&ss, &sslen);
287#endif
288 if (nread < 0) {
289 DPRINTF(("%s: %R[sockerr]\n", __func__, SOCKERRNO()));
290 return POLLIN;
291 }
292
293 /* Check that ring buffer is not full */
294 lim = fwudp->inbuf.unsent;
295 if (lim == 0) {
296 lim = fwudp->inbuf.bufsize - 1; /* guard slot at the end */
297 }
298 else {
299 --lim;
300 }
301
302 beg = fwudp->inbuf.vacant;
303 if (beg == lim) { /* no vacant slot */
304 return POLLIN;
305 }
306
307
308 dgram = &fwudp->inbuf.buf[beg];
309
310
311 status = fwany_ipX_addr_set_src(&dgram->src_addr, (struct sockaddr *)&ss);
312 if (status == PXREMAP_FAILED) {
313 return POLLIN;
314 }
315
316 if (ss.ss_family == AF_INET) {
317 const struct sockaddr_in *peer4 = (const struct sockaddr_in *)&ss;
318 dgram->src_port = htons(peer4->sin_port);
319 }
320 else { /* PF_INET6 */
321 const struct sockaddr_in6 *peer6 = (const struct sockaddr_in6 *)&ss;
322 dgram->src_port = htons(peer6->sin6_port);
323 }
324
325 p = pbuf_alloc(PBUF_RAW, nread, PBUF_RAM);
326 if (p == NULL) {
327 DPRINTF(("%s: pbuf_alloc(%d) failed\n", __func__, (int)nread));
328 return POLLIN;
329 }
330
331 error = pbuf_take(p, pollmgr_udpbuf, nread);
332 if (error != ERR_OK) {
333 DPRINTF(("%s: pbuf_take(%d) failed\n", __func__, (int)nread));
334 pbuf_free(p);
335 return POLLIN;
336 }
337
338 dgram->p = p;
339
340 ++beg;
341 if (beg == fwudp->inbuf.bufsize) {
342 beg = 0;
343 }
344 fwudp->inbuf.vacant = beg;
345
346 proxy_lwip_post(&fwudp->msg_send);
347
348 return POLLIN;
349}
350
351
352/**
353 * Lwip thread callback invoked via fwudp::msg_send
354 */
355void
356fwudp_pcb_send(void *arg)
357{
358 struct fwudp *fwudp = (struct fwudp *)arg;
359 struct fwudp_dgram dgram;
360 struct udp_pcb *pcb;
361 struct udp_pcb **pprev;
362 int isv6;
363 size_t idx;
364
365 idx = fwudp->inbuf.unsent;
366
367 if (idx == fwudp->inbuf.vacant) {
368 /* empty buffer - shouldn't happen! */
369 DPRINTF(("%s: ring buffer empty!\n", __func__));
370 return;
371 }
372
373 dgram = fwudp->inbuf.buf[idx]; /* struct copy */
374#if 1 /* valgrind hint */
375 fwudp->inbuf.buf[idx].p = NULL;
376#endif
377 if (++idx == fwudp->inbuf.bufsize) {
378 idx = 0;
379 }
380 fwudp->inbuf.unsent = idx;
381
382 /* XXX: this is *STUPID* */
383 isv6 = (fwudp->fwspec.sdom == PF_INET6);
384 pprev = &udp_proxy_pcbs;
385 for (pcb = udp_proxy_pcbs; pcb != NULL; pcb = pcb->next) {
386 if (PCB_ISIPV6(pcb) == isv6
387 && pcb->remote_port == fwudp->dst_port
388 && ipX_addr_cmp(isv6, &fwudp->dst_addr, &pcb->remote_ip)
389 && pcb->local_port == dgram.src_port
390 && ipX_addr_cmp(isv6, &dgram.src_addr, &pcb->local_ip))
391 {
392 break;
393 }
394 else {
395 pprev = &pcb->next;
396 }
397 }
398
399 if (pcb != NULL) {
400 *pprev = pcb->next;
401 pcb->next = udp_proxy_pcbs;
402 udp_proxy_pcbs = pcb;
403
404 /*
405 * XXX: check that its ours and not accidentally created by
406 * outbound traffic.
407 *
408 * ???: Otherwise? Expire it and set pcb = NULL; to create a
409 * new one below?
410 */
411 }
412
413 if (pcb == NULL) {
414 pcb = udp_new();
415 if (pcb == NULL) {
416 goto out;
417 }
418
419 ip_set_v6(pcb, isv6);
420
421 /* equivalent of udp_bind */
422 ipX_addr_set(isv6, &pcb->local_ip, &dgram.src_addr);
423 pcb->local_port = dgram.src_port;
424
425 /* equivalent to udp_connect */
426 ipX_addr_set(isv6, &pcb->remote_ip, &fwudp->dst_addr);
427 pcb->remote_port = fwudp->dst_port;
428 pcb->flags |= UDP_FLAGS_CONNECTED;
429
430 udp_recv(pcb, fwudp_pcb_recv, fwudp);
431
432 pcb->next = udp_proxy_pcbs;
433 udp_proxy_pcbs = pcb;
434 udp_proxy_timer_needed();
435 }
436
437 udp_send(pcb, dgram.p);
438
439 out:
440 pbuf_free(dgram.p);
441}
442
443
444/**
445 * udp_recv() callback.
446 */
447void
448fwudp_pcb_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p,
449 ip_addr_t *addr, u16_t port)
450{
451 struct fwudp *fwudp = (struct fwudp *)arg;
452
453 LWIP_UNUSED_ARG(addr);
454 LWIP_UNUSED_ARG(port);
455
456 LWIP_ASSERT1(fwudp != NULL);
457
458 if (p == NULL) {
459 DPRINTF(("%s: pcb %p (fwudp %p); sock %d: expired\n",
460 __func__, (void *)pcb, (void *)fwudp, fwudp->sock));
461 /* NB: fwudp is "global" and not deleted */
462 /* XXX: TODO: delete local reference when we will keep one */
463 udp_remove(pcb);
464 return;
465 }
466 else {
467 fwudp_pcb_forward_outbound(fwudp, pcb, p);
468 }
469}
470
471
472/*
473 * XXX: This is pxudp_pcb_forward_outbound modulo:
474 * - s/pxudp/fwudp/g
475 * - addr/port (unused in either) dropped
476 * - destination is specified since host socket is not connected
477 */
478static void
479fwudp_pcb_forward_outbound(struct fwudp *fwudp, struct udp_pcb *pcb,
480 struct pbuf *p)
481{
482 union {
483 struct sockaddr_in sin;
484 struct sockaddr_in6 sin6;
485 } peer;
486 socklen_t namelen;
487
488 memset(&peer, 0, sizeof(peer)); /* XXX: shut up valgrind */
489
490 if (fwudp->fwspec.sdom == PF_INET) {
491 peer.sin.sin_family = AF_INET;
492#if HAVE_SA_LEN
493 peer.sin.sin_len =
494#endif
495 namelen = sizeof(peer.sin);
496 pxremap_outbound_ip4((ip_addr_t *)&peer.sin.sin_addr, &pcb->local_ip.ip4);
497 peer.sin.sin_port = htons(pcb->local_port);
498 }
499 else {
500 peer.sin6.sin6_family = AF_INET6;
501#if HAVE_SA_LEN
502 peer.sin6.sin6_len =
503#endif
504 namelen = sizeof(peer.sin6);
505
506 pxremap_outbound_ip6((ip6_addr_t *)&peer.sin6.sin6_addr, &pcb->local_ip.ip6);
507 peer.sin6.sin6_port = htons(pcb->local_port);
508 }
509
510 proxy_sendto(fwudp->sock, p, &peer, namelen);
511 pbuf_free(p);
512}
513
514
515/**
516 * Lwip thread callback invoked via fwudp::msg_delete
517 */
518static void
519fwudp_pcb_delete(void *arg)
520{
521 struct fwudp *fwudp = (struct fwudp *)arg;
522 struct udp_pcb *pcb;
523 struct udp_pcb **pprev;
524
525 LWIP_ASSERT1(fwudp->inbuf.unsent == fwudp->inbuf.vacant);
526
527 pprev = &udp_proxy_pcbs;
528 pcb = udp_proxy_pcbs;
529 while (pcb != NULL) {
530 if (pcb->recv_arg != fwudp) {
531 pprev = &pcb->next;
532 pcb = pcb->next;
533 }
534 else {
535 struct udp_pcb *dead = pcb;
536 pcb = pcb->next;
537 *pprev = pcb;
538 memp_free(MEMP_UDP_PCB, dead);
539 }
540 }
541
542 closesocket(fwudp->sock);
543 free(fwudp->inbuf.buf);
544 free(fwudp);
545}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette