VirtualBox

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

Last change on this file since 48310 was 48001, checked in by vboxsync, 12 years ago

Move proxy sources from Devices/Network/lwip-new/vbox
to NetworkServices/NAT where they belong.

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

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