VirtualBox

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

Last change on this file since 60869 was 56300, checked in by vboxsync, 9 years ago

NetworkServices: Updated (C) year.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.9 KB
Line 
1/* $Id: fwudp.c 56300 2015-06-09 14:36:22Z vboxsync $ */
2/** @file
3 * NAT Network - UDP port-forwarding.
4 */
5
6/*
7 * Copyright (C) 2013-2015 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 nread = recvfrom(fwudp->sock, pollmgr_udpbuf, sizeof(pollmgr_udpbuf), 0,
282 (struct sockaddr *)&ss, &sslen);
283 if (nread < 0) {
284 DPRINTF(("%s: %R[sockerr]\n", __func__, SOCKERRNO()));
285 return POLLIN;
286 }
287
288 /* Check that ring buffer is not full */
289 lim = fwudp->inbuf.unsent;
290 if (lim == 0) {
291 lim = fwudp->inbuf.bufsize - 1; /* guard slot at the end */
292 }
293 else {
294 --lim;
295 }
296
297 beg = fwudp->inbuf.vacant;
298 if (beg == lim) { /* no vacant slot */
299 return POLLIN;
300 }
301
302
303 dgram = &fwudp->inbuf.buf[beg];
304
305
306 status = fwany_ipX_addr_set_src(&dgram->src_addr, (struct sockaddr *)&ss);
307 if (status == PXREMAP_FAILED) {
308 return POLLIN;
309 }
310
311 if (ss.ss_family == AF_INET) {
312 const struct sockaddr_in *peer4 = (const struct sockaddr_in *)&ss;
313 dgram->src_port = htons(peer4->sin_port);
314 }
315 else { /* PF_INET6 */
316 const struct sockaddr_in6 *peer6 = (const struct sockaddr_in6 *)&ss;
317 dgram->src_port = htons(peer6->sin6_port);
318 }
319
320 p = pbuf_alloc(PBUF_RAW, nread, PBUF_RAM);
321 if (p == NULL) {
322 DPRINTF(("%s: pbuf_alloc(%d) failed\n", __func__, (int)nread));
323 return POLLIN;
324 }
325
326 error = pbuf_take(p, pollmgr_udpbuf, nread);
327 if (error != ERR_OK) {
328 DPRINTF(("%s: pbuf_take(%d) failed\n", __func__, (int)nread));
329 pbuf_free(p);
330 return POLLIN;
331 }
332
333 dgram->p = p;
334
335 ++beg;
336 if (beg == fwudp->inbuf.bufsize) {
337 beg = 0;
338 }
339 fwudp->inbuf.vacant = beg;
340
341 proxy_lwip_post(&fwudp->msg_send);
342
343 return POLLIN;
344}
345
346
347/**
348 * Lwip thread callback invoked via fwudp::msg_send
349 */
350void
351fwudp_pcb_send(void *arg)
352{
353 struct fwudp *fwudp = (struct fwudp *)arg;
354 struct fwudp_dgram dgram;
355 struct udp_pcb *pcb;
356 struct udp_pcb **pprev;
357 int isv6;
358 size_t idx;
359
360 idx = fwudp->inbuf.unsent;
361
362 if (idx == fwudp->inbuf.vacant) {
363 /* empty buffer - shouldn't happen! */
364 DPRINTF(("%s: ring buffer empty!\n", __func__));
365 return;
366 }
367
368 dgram = fwudp->inbuf.buf[idx]; /* struct copy */
369#if 1 /* valgrind hint */
370 fwudp->inbuf.buf[idx].p = NULL;
371#endif
372 if (++idx == fwudp->inbuf.bufsize) {
373 idx = 0;
374 }
375 fwudp->inbuf.unsent = idx;
376
377 /* XXX: this is *STUPID* */
378 isv6 = (fwudp->fwspec.sdom == PF_INET6);
379 pprev = &udp_proxy_pcbs;
380 for (pcb = udp_proxy_pcbs; pcb != NULL; pcb = pcb->next) {
381 if (PCB_ISIPV6(pcb) == isv6
382 && pcb->remote_port == fwudp->dst_port
383 && ipX_addr_cmp(isv6, &fwudp->dst_addr, &pcb->remote_ip)
384 && pcb->local_port == dgram.src_port
385 && ipX_addr_cmp(isv6, &dgram.src_addr, &pcb->local_ip))
386 {
387 break;
388 }
389 else {
390 pprev = &pcb->next;
391 }
392 }
393
394 if (pcb != NULL) {
395 *pprev = pcb->next;
396 pcb->next = udp_proxy_pcbs;
397 udp_proxy_pcbs = pcb;
398
399 /*
400 * XXX: check that its ours and not accidentally created by
401 * outbound traffic.
402 *
403 * ???: Otherwise? Expire it and set pcb = NULL; to create a
404 * new one below?
405 */
406 }
407
408 if (pcb == NULL) {
409 pcb = udp_new();
410 if (pcb == NULL) {
411 goto out;
412 }
413
414 ip_set_v6(pcb, isv6);
415
416 /* equivalent of udp_bind */
417 ipX_addr_set(isv6, &pcb->local_ip, &dgram.src_addr);
418 pcb->local_port = dgram.src_port;
419
420 /* equivalent to udp_connect */
421 ipX_addr_set(isv6, &pcb->remote_ip, &fwudp->dst_addr);
422 pcb->remote_port = fwudp->dst_port;
423 pcb->flags |= UDP_FLAGS_CONNECTED;
424
425 udp_recv(pcb, fwudp_pcb_recv, fwudp);
426
427 pcb->next = udp_proxy_pcbs;
428 udp_proxy_pcbs = pcb;
429 udp_proxy_timer_needed();
430 }
431
432 udp_send(pcb, dgram.p);
433
434 out:
435 pbuf_free(dgram.p);
436}
437
438
439/**
440 * udp_recv() callback.
441 */
442void
443fwudp_pcb_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p,
444 ip_addr_t *addr, u16_t port)
445{
446 struct fwudp *fwudp = (struct fwudp *)arg;
447
448 LWIP_UNUSED_ARG(addr);
449 LWIP_UNUSED_ARG(port);
450
451 LWIP_ASSERT1(fwudp != NULL);
452
453 if (p == NULL) {
454 DPRINTF(("%s: pcb %p (fwudp %p); sock %d: expired\n",
455 __func__, (void *)pcb, (void *)fwudp, fwudp->sock));
456 /* NB: fwudp is "global" and not deleted */
457 /* XXX: TODO: delete local reference when we will keep one */
458 udp_remove(pcb);
459 return;
460 }
461 else {
462 fwudp_pcb_forward_outbound(fwudp, pcb, p);
463 }
464}
465
466
467/*
468 * XXX: This is pxudp_pcb_forward_outbound modulo:
469 * - s/pxudp/fwudp/g
470 * - addr/port (unused in either) dropped
471 * - destination is specified since host socket is not connected
472 */
473static void
474fwudp_pcb_forward_outbound(struct fwudp *fwudp, struct udp_pcb *pcb,
475 struct pbuf *p)
476{
477 union {
478 struct sockaddr_in sin;
479 struct sockaddr_in6 sin6;
480 } peer;
481 socklen_t namelen;
482
483 memset(&peer, 0, sizeof(peer)); /* XXX: shut up valgrind */
484
485 if (fwudp->fwspec.sdom == PF_INET) {
486 peer.sin.sin_family = AF_INET;
487#if HAVE_SA_LEN
488 peer.sin.sin_len =
489#endif
490 namelen = sizeof(peer.sin);
491 pxremap_outbound_ip4((ip_addr_t *)&peer.sin.sin_addr, &pcb->local_ip.ip4);
492 peer.sin.sin_port = htons(pcb->local_port);
493 }
494 else {
495 peer.sin6.sin6_family = AF_INET6;
496#if HAVE_SA_LEN
497 peer.sin6.sin6_len =
498#endif
499 namelen = sizeof(peer.sin6);
500
501 pxremap_outbound_ip6((ip6_addr_t *)&peer.sin6.sin6_addr, &pcb->local_ip.ip6);
502 peer.sin6.sin6_port = htons(pcb->local_port);
503 }
504
505 proxy_sendto(fwudp->sock, p, &peer, namelen);
506 pbuf_free(p);
507}
508
509
510/**
511 * Lwip thread callback invoked via fwudp::msg_delete
512 */
513static void
514fwudp_pcb_delete(void *arg)
515{
516 struct fwudp *fwudp = (struct fwudp *)arg;
517 struct udp_pcb *pcb;
518 struct udp_pcb **pprev;
519
520 LWIP_ASSERT1(fwudp->inbuf.unsent == fwudp->inbuf.vacant);
521
522 pprev = &udp_proxy_pcbs;
523 pcb = udp_proxy_pcbs;
524 while (pcb != NULL) {
525 if (pcb->recv_arg != fwudp) {
526 pprev = &pcb->next;
527 pcb = pcb->next;
528 }
529 else {
530 struct udp_pcb *dead = pcb;
531 pcb = pcb->next;
532 *pprev = pcb;
533 memp_free(MEMP_UDP_PCB, dead);
534 }
535 }
536
537 closesocket(fwudp->sock);
538 free(fwudp->inbuf.buf);
539 free(fwudp);
540}
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