VirtualBox

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

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

NAT/Net: convert perror/warn/warnx to DPRINTFs.

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