VirtualBox

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

Last change on this file since 48767 was 48438, checked in by vboxsync, 11 years ago

Check for PXREMAP_FAILED now that it can actually happen.

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