VirtualBox

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

Last change on this file since 51300 was 50022, checked in by vboxsync, 11 years ago

Don't call pbuf_free() in proxy_sendto(), leave it up to the caller
that may still need the pbuf, e.g. to generate an ICMP error.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.3 KB
Line 
1/* -*- indent-tabs-mode: nil; -*- */
2#include "winutils.h"
3#include "proxy.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 return;
114}
115
116
117void
118fwudp_add(struct fwspec *fwspec)
119{
120 struct fwudp *fwudp;
121
122 fwudp = fwudp_create(fwspec);
123 if (fwudp == NULL) {
124 DPRINTF0(("%s: failed to add rule for UDP ...\n", __func__));
125 return;
126 }
127
128 DPRINTF0(("%s\n", __func__));
129 /* fwudp_create has put fwudp on the linked list */
130}
131
132
133void
134fwudp_del(struct fwspec *fwspec)
135{
136 struct fwudp *fwudp;
137 struct fwudp **pprev;
138
139 for (pprev = &fwudp_list; (fwudp = *pprev) != NULL; pprev = &fwudp->next) {
140 if (fwspec_equal(&fwudp->fwspec, fwspec)) {
141 *pprev = fwudp->next;
142 fwudp->next = NULL;
143 break;
144 }
145 }
146
147 if (fwudp == NULL) {
148 DPRINTF0(("%s: not found\n", __func__));
149 return;
150 }
151
152 DPRINTF0(("%s\n", __func__));
153
154 pollmgr_del_slot(fwudp->pmhdl.slot);
155 fwudp->pmhdl.slot = -1;
156
157 /* let pending msg_send be processed before we delete fwudp */
158 proxy_lwip_post(&fwudp->msg_delete);
159}
160
161
162struct fwudp *
163fwudp_create(struct fwspec *fwspec)
164{
165 struct fwudp *fwudp;
166 SOCKET sock;
167 int status;
168
169 sock = proxy_bound_socket(fwspec->sdom, fwspec->stype, &fwspec->src.sa);
170 if (sock == INVALID_SOCKET) {
171 perror("socket");
172 return NULL;
173 }
174
175 fwudp = (struct fwudp *)malloc(sizeof(*fwudp));
176 if (fwudp == NULL) {
177 closesocket(sock);
178 return NULL;
179 }
180
181 fwudp->pmhdl.callback = fwudp_pmgr_pump;
182 fwudp->pmhdl.data = (void *)fwudp;
183 fwudp->pmhdl.slot = -1;
184
185 fwudp->sock = sock;
186 fwudp->fwspec = *fwspec; /* struct copy */
187
188 /* XXX */
189 if (fwspec->sdom == PF_INET) {
190 struct sockaddr_in *dst4 = &fwspec->dst.sin;
191 memcpy(&fwudp->dst_addr.ip4, &dst4->sin_addr, sizeof(ip_addr_t));
192 fwudp->dst_port = htons(dst4->sin_port);
193 }
194 else { /* PF_INET6 */
195 struct sockaddr_in6 *dst6 = &fwspec->dst.sin6;
196 memcpy(&fwudp->dst_addr.ip6, &dst6->sin6_addr, sizeof(ip6_addr_t));
197 fwudp->dst_port = htons(dst6->sin6_port);
198 }
199
200 fwudp->inbuf.bufsize = 256; /* elements */
201 fwudp->inbuf.buf
202 = (struct fwudp_dgram *)calloc(fwudp->inbuf.bufsize,
203 sizeof(struct fwudp_dgram));
204 if (fwudp->inbuf.buf == NULL) {
205 closesocket(sock);
206 free(fwudp);
207 return (NULL);
208 }
209 fwudp->inbuf.vacant = 0;
210 fwudp->inbuf.unsent = 0;
211
212#define CALLBACK_MSG(MSG, FUNC) \
213 do { \
214 fwudp->MSG.type = TCPIP_MSG_CALLBACK_STATIC; \
215 fwudp->MSG.sem = NULL; \
216 fwudp->MSG.msg.cb.function = FUNC; \
217 fwudp->MSG.msg.cb.ctx = (void *)fwudp; \
218 } while (0)
219
220 CALLBACK_MSG(msg_send, fwudp_pcb_send);
221 CALLBACK_MSG(msg_delete, fwudp_pcb_delete);
222
223#undef CALLBACK_MSG
224
225 status = pollmgr_add(&fwudp->pmhdl, fwudp->sock, POLLIN);
226 if (status < 0) {
227 closesocket(sock);
228 free(fwudp->inbuf.buf);
229 free(fwudp);
230 return NULL;
231 }
232
233 fwudp->next = fwudp_list;
234 fwudp_list = fwudp;
235
236 return fwudp;
237}
238
239
240/**
241 * Poll manager callaback for fwudp::sock
242 */
243int
244fwudp_pmgr_pump(struct pollmgr_handler *handler, SOCKET fd, int revents)
245{
246 struct fwudp *fwudp;
247 struct sockaddr_storage ss;
248 socklen_t sslen = sizeof(ss);
249 size_t beg, lim;
250 struct fwudp_dgram *dgram;
251 struct pbuf *p;
252 ssize_t nread;
253 int status;
254 err_t error;
255
256 fwudp = (struct fwudp *)handler->data;
257
258 LWIP_ASSERT1(fwudp != NULL);
259 LWIP_ASSERT1(fd == fwudp->sock);
260 LWIP_ASSERT1(revents == POLLIN);
261 LWIP_UNUSED_ARG(fd);
262 LWIP_UNUSED_ARG(revents);
263
264 nread = recvfrom(fwudp->sock, pollmgr_udpbuf, sizeof(pollmgr_udpbuf), 0,
265 (struct sockaddr *)&ss, &sslen);
266 if (nread < 0) {
267 perror(__func__);
268 return POLLIN;
269 }
270
271 /* Check that ring buffer is not full */
272 lim = fwudp->inbuf.unsent;
273 if (lim == 0) {
274 lim = fwudp->inbuf.bufsize - 1; /* guard slot at the end */
275 }
276 else {
277 --lim;
278 }
279
280 beg = fwudp->inbuf.vacant;
281 if (beg == lim) { /* no vacant slot */
282 return POLLIN;
283 }
284
285
286 dgram = &fwudp->inbuf.buf[beg];
287
288
289 status = fwany_ipX_addr_set_src(&dgram->src_addr, (struct sockaddr *)&ss);
290 if (status == PXREMAP_FAILED) {
291 return POLLIN;
292 }
293
294 if (ss.ss_family == AF_INET) {
295 const struct sockaddr_in *peer4 = (const struct sockaddr_in *)&ss;
296 dgram->src_port = htons(peer4->sin_port);
297 }
298 else { /* PF_INET6 */
299 const struct sockaddr_in6 *peer6 = (const struct sockaddr_in6 *)&ss;
300 dgram->src_port = htons(peer6->sin6_port);
301 }
302
303 p = pbuf_alloc(PBUF_RAW, nread, PBUF_RAM);
304 if (p == NULL) {
305 DPRINTF(("%s: pbuf_alloc(%d) failed\n", __func__, (int)nread));
306 return POLLIN;
307 }
308
309 error = pbuf_take(p, pollmgr_udpbuf, nread);
310 if (error != ERR_OK) {
311 DPRINTF(("%s: pbuf_take(%d) failed\n", __func__, (int)nread));
312 pbuf_free(p);
313 return POLLIN;
314 }
315
316 dgram->p = p;
317
318 ++beg;
319 if (beg == fwudp->inbuf.bufsize) {
320 beg = 0;
321 }
322 fwudp->inbuf.vacant = beg;
323
324 proxy_lwip_post(&fwudp->msg_send);
325
326 return POLLIN;
327}
328
329
330/**
331 * Lwip thread callback invoked via fwudp::msg_send
332 */
333void
334fwudp_pcb_send(void *arg)
335{
336 struct fwudp *fwudp = (struct fwudp *)arg;
337 struct fwudp_dgram dgram;
338 struct udp_pcb *pcb;
339 struct udp_pcb **pprev;
340 int isv6;
341 size_t idx;
342
343 idx = fwudp->inbuf.unsent;
344
345 if (idx == fwudp->inbuf.vacant) {
346 /* empty buffer - shouldn't happen! */
347 DPRINTF(("%s: ring buffer empty!\n", __func__));
348 return;
349 }
350
351 dgram = fwudp->inbuf.buf[idx]; /* struct copy */
352#if 1 /* valgrind hint */
353 fwudp->inbuf.buf[idx].p = NULL;
354#endif
355 if (++idx == fwudp->inbuf.bufsize) {
356 idx = 0;
357 }
358 fwudp->inbuf.unsent = idx;
359
360 /* XXX: this is *STUPID* */
361 isv6 = (fwudp->fwspec.sdom == PF_INET6);
362 pprev = &udp_proxy_pcbs;
363 for (pcb = udp_proxy_pcbs; pcb != NULL; pcb = pcb->next) {
364 if (PCB_ISIPV6(pcb) == isv6
365 && pcb->remote_port == fwudp->dst_port
366 && ipX_addr_cmp(isv6, &fwudp->dst_addr, &pcb->remote_ip)
367 && pcb->local_port == dgram.src_port
368 && ipX_addr_cmp(isv6, &dgram.src_addr, &pcb->local_ip))
369 {
370 break;
371 }
372 else {
373 pprev = &pcb->next;
374 }
375 }
376
377 if (pcb != NULL) {
378 *pprev = pcb->next;
379 pcb->next = udp_proxy_pcbs;
380 udp_proxy_pcbs = pcb;
381
382 /*
383 * XXX: check that its ours and not accidentally created by
384 * outbound traffic.
385 *
386 * ???: Otherwise? Expire it and set pcb = NULL; to create a
387 * new one below?
388 */
389 }
390
391 if (pcb == NULL) {
392 pcb = udp_new();
393 if (pcb == NULL) {
394 goto out;
395 }
396
397 ip_set_v6(pcb, isv6);
398
399 /* equivalent of udp_bind */
400 ipX_addr_set(isv6, &pcb->local_ip, &dgram.src_addr);
401 pcb->local_port = dgram.src_port;
402
403 /* equivalent to udp_connect */
404 ipX_addr_set(isv6, &pcb->remote_ip, &fwudp->dst_addr);
405 pcb->remote_port = fwudp->dst_port;
406 pcb->flags |= UDP_FLAGS_CONNECTED;
407
408 udp_recv(pcb, fwudp_pcb_recv, fwudp);
409
410 pcb->next = udp_proxy_pcbs;
411 udp_proxy_pcbs = pcb;
412 udp_proxy_timer_needed();
413 }
414
415 udp_send(pcb, dgram.p);
416
417 out:
418 pbuf_free(dgram.p);
419}
420
421
422/**
423 * udp_recv() callback.
424 */
425void
426fwudp_pcb_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p,
427 ip_addr_t *addr, u16_t port)
428{
429 struct fwudp *fwudp = (struct fwudp *)arg;
430
431 LWIP_UNUSED_ARG(addr);
432 LWIP_UNUSED_ARG(port);
433
434 LWIP_ASSERT1(fwudp != NULL);
435
436 if (p == NULL) {
437 DPRINTF(("%s: pcb %p (fwudp %p); sock %d: expired\n",
438 __func__, (void *)pcb, (void *)fwudp, fwudp->sock));
439 /* NB: fwudp is "global" and not deleted */
440 /* XXX: TODO: delete local reference when we will keep one */
441 udp_remove(pcb);
442 return;
443 }
444 else {
445 fwudp_pcb_forward_outbound(fwudp, pcb, p);
446 }
447}
448
449
450/*
451 * XXX: This is pxudp_pcb_forward_outbound modulo:
452 * - s/pxudp/fwudp/g
453 * - addr/port (unused in either) dropped
454 * - destination is specified since host socket is not connected
455 */
456static void
457fwudp_pcb_forward_outbound(struct fwudp *fwudp, struct udp_pcb *pcb,
458 struct pbuf *p)
459{
460 union {
461 struct sockaddr_in sin;
462 struct sockaddr_in6 sin6;
463 } peer;
464 socklen_t namelen;
465
466 memset(&peer, 0, sizeof(peer)); /* XXX: shut up valgrind */
467
468 if (fwudp->fwspec.sdom == PF_INET) {
469 peer.sin.sin_family = AF_INET;
470#if HAVE_SA_LEN
471 peer.sin.sin_len =
472#endif
473 namelen = sizeof(peer.sin);
474 pxremap_outbound_ip4((ip_addr_t *)&peer.sin.sin_addr, &pcb->local_ip.ip4);
475 peer.sin.sin_port = htons(pcb->local_port);
476 }
477 else {
478 peer.sin6.sin6_family = AF_INET6;
479#if HAVE_SA_LEN
480 peer.sin6.sin6_len =
481#endif
482 namelen = sizeof(peer.sin6);
483
484 pxremap_outbound_ip6((ip6_addr_t *)&peer.sin6.sin6_addr, &pcb->local_ip.ip6);
485 peer.sin6.sin6_port = htons(pcb->local_port);
486 }
487
488 proxy_sendto(fwudp->sock, p, &peer, namelen);
489 pbuf_free(p);
490}
491
492
493/**
494 * Lwip thread callback invoked via fwudp::msg_delete
495 */
496static void
497fwudp_pcb_delete(void *arg)
498{
499 struct fwudp *fwudp = (struct fwudp *)arg;
500 struct udp_pcb *pcb;
501 struct udp_pcb **pprev;
502
503 LWIP_ASSERT1(fwudp->inbuf.unsent == fwudp->inbuf.vacant);
504
505 pprev = &udp_proxy_pcbs;
506 pcb = udp_proxy_pcbs;
507 while (pcb != NULL) {
508 if (pcb->recv_arg != fwudp) {
509 pprev = &pcb->next;
510 pcb = pcb->next;
511 }
512 else {
513 struct udp_pcb *dead = pcb;
514 pcb = pcb->next;
515 *pprev = pcb;
516 memp_free(MEMP_UDP_PCB, dead);
517 }
518 }
519
520 closesocket(fwudp->sock);
521 free(fwudp->inbuf.buf);
522 free(fwudp);
523}
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