VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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