VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/NAT/pxudp.c@ 51581

最後變更 在這個檔案從51581是 51581,由 vboxsync 提交於 10 年 前

NAT/Net: Start untangling errno vs. winsock mess. Don't refer errno
directly, while here convert some perror() calls to DPRINTFs.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Id Revision
檔案大小: 16.0 KB
 
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 "pxremap.h"
8
9#ifndef RT_OS_WINDOWS
10#include <sys/types.h>
11#include <sys/socket.h>
12#include <stdlib.h>
13#include <stdint.h>
14#include <stdio.h>
15#include <poll.h>
16
17#include <err.h> /* BSD'ism */
18#else
19#include <stdlib.h>
20#include <iprt/stdint.h>
21#include <stdio.h>
22#include "winpoll.h"
23#endif
24
25#include "lwip/opt.h"
26
27#include "lwip/sys.h"
28#include "lwip/tcpip.h"
29#include "lwip/udp.h"
30
31struct pxudp {
32 /**
33 * Our poll manager handler.
34 */
35 struct pollmgr_handler pmhdl;
36
37 /**
38 * lwIP ("internal") side of the proxied connection.
39 */
40 struct udp_pcb *pcb;
41
42 /**
43 * Host ("external") side of the proxied connection.
44 */
45 SOCKET sock;
46
47 /**
48 * For some protocols (notably: DNS) we know we are getting just
49 * one reply, so we don't want the pcb and the socket to sit there
50 * waiting to be g/c'ed by timeout. This field counts request and
51 * replies for them.
52 */
53 int count;
54
55 /**
56 * Mailbox for inbound pbufs.
57 *
58 * XXX: since we have single producer and single consumer we can
59 * use lockless ringbuf like for pxtcp.
60 */
61 sys_mbox_t inmbox;
62
63 /**
64 * lwIP thread's strong reference to us.
65 */
66 struct pollmgr_refptr *rp;
67
68 /*
69 * We use static messages to void malloc/free overhead.
70 */
71 struct tcpip_msg msg_delete; /* delete pxudp */
72 struct tcpip_msg msg_inbound; /* trigger send of inbound data */
73};
74
75
76static struct pxudp *pxudp_allocate(void);
77static void pxudp_drain_inmbox(struct pxudp *);
78static void pxudp_free(struct pxudp *);
79
80static struct udp_pcb *pxudp_pcb_dissociate(struct pxudp *);
81
82/* poll manager callbacks for pxudp related channels */
83static int pxudp_pmgr_chan_add(struct pollmgr_handler *, SOCKET, int);
84static int pxudp_pmgr_chan_del(struct pollmgr_handler *, SOCKET, int);
85
86/* helper functions for sending/receiving pxudp over poll manager channels */
87static ssize_t pxudp_chan_send(enum pollmgr_slot_t, struct pxudp *);
88static ssize_t pxudp_chan_send_weak(enum pollmgr_slot_t, struct pxudp *);
89static struct pxudp *pxudp_chan_recv(struct pollmgr_handler *, SOCKET, int);
90static struct pxudp *pxudp_chan_recv_strong(struct pollmgr_handler *, SOCKET, int);
91
92/* poll manager callbacks for individual sockets */
93static int pxudp_pmgr_pump(struct pollmgr_handler *, SOCKET, int);
94
95/* convenience function for poll manager callback */
96static int pxudp_schedule_delete(struct pxudp *);
97
98/* lwip thread callbacks called via proxy_lwip_post() */
99static void pxudp_pcb_delete_pxudp(void *);
100
101/* udp pcb callbacks &c */
102static void pxudp_pcb_accept(void *, struct udp_pcb *, struct pbuf *, ip_addr_t *, u16_t);
103static void pxudp_pcb_recv(void *, struct udp_pcb *, struct pbuf *, ip_addr_t *, u16_t);
104static void pxudp_pcb_forward_outbound(struct pxudp *, struct pbuf *, ip_addr_t *, u16_t);
105static void pxudp_pcb_expired(struct pxudp *);
106static void pxudp_pcb_write_inbound(void *);
107static void pxudp_pcb_forward_inbound(struct pxudp *);
108
109/* poll manager handlers for pxudp channels */
110static struct pollmgr_handler pxudp_pmgr_chan_add_hdl;
111static struct pollmgr_handler pxudp_pmgr_chan_del_hdl;
112
113
114void
115pxudp_init(void)
116{
117 /*
118 * Create channels.
119 */
120 pxudp_pmgr_chan_add_hdl.callback = pxudp_pmgr_chan_add;
121 pxudp_pmgr_chan_add_hdl.data = NULL;
122 pxudp_pmgr_chan_add_hdl.slot = -1;
123 pollmgr_add_chan(POLLMGR_CHAN_PXUDP_ADD, &pxudp_pmgr_chan_add_hdl);
124
125 pxudp_pmgr_chan_del_hdl.callback = pxudp_pmgr_chan_del;
126 pxudp_pmgr_chan_del_hdl.data = NULL;
127 pxudp_pmgr_chan_del_hdl.slot = -1;
128 pollmgr_add_chan(POLLMGR_CHAN_PXUDP_DEL, &pxudp_pmgr_chan_del_hdl);
129
130 udp_proxy_accept(pxudp_pcb_accept);
131}
132
133
134/**
135 * Syntactic sugar for sending pxudp pointer over poll manager
136 * channel. Used by lwip thread functions.
137 */
138static ssize_t
139pxudp_chan_send(enum pollmgr_slot_t chan, struct pxudp *pxudp)
140{
141 return pollmgr_chan_send(chan, &pxudp, sizeof(pxudp));
142}
143
144
145/**
146 * Syntactic sugar for sending weak reference to pxudp over poll
147 * manager channel. Used by lwip thread functions.
148 */
149static ssize_t
150pxudp_chan_send_weak(enum pollmgr_slot_t chan, struct pxudp *pxudp)
151{
152 pollmgr_refptr_weak_ref(pxudp->rp);
153 return pollmgr_chan_send(chan, &pxudp->rp, sizeof(pxudp->rp));
154}
155
156
157/**
158 * Counterpart of pxudp_chan_send().
159 */
160static struct pxudp *
161pxudp_chan_recv(struct pollmgr_handler *handler, SOCKET fd, int revents)
162{
163 struct pxudp *pxudp;
164
165 pxudp = (struct pxudp *)pollmgr_chan_recv_ptr(handler, fd, revents);
166 return pxudp;
167}
168
169
170/**
171 * Counterpart of pxudp_chan_send_weak().
172 */
173struct pxudp *
174pxudp_chan_recv_strong(struct pollmgr_handler *handler, SOCKET fd, int revents)
175{
176 struct pollmgr_refptr *rp;
177 struct pollmgr_handler *base;
178 struct pxudp *pxudp;
179
180 rp = (struct pollmgr_refptr *)pollmgr_chan_recv_ptr(handler, fd, revents);
181 base = (struct pollmgr_handler *)pollmgr_refptr_get(rp);
182 pxudp = (struct pxudp *)base;
183
184 return pxudp;
185}
186
187
188/**
189 * POLLMGR_CHAN_PXUDP_ADD handler.
190 *
191 * Get new pxudp from lwip thread and start polling its socket.
192 */
193static int
194pxudp_pmgr_chan_add(struct pollmgr_handler *handler, SOCKET fd, int revents)
195{
196 struct pxudp *pxudp;
197 int status;
198
199 pxudp = pxudp_chan_recv(handler, fd, revents);
200 DPRINTF(("pxudp_add: new pxudp %p; pcb %p\n",
201 (void *)pxudp, (void *)pxudp->pcb));
202
203 LWIP_ASSERT1(pxudp != NULL);
204 LWIP_ASSERT1(pxudp->pmhdl.callback != NULL);
205 LWIP_ASSERT1(pxudp->pmhdl.data = (void *)pxudp);
206 LWIP_ASSERT1(pxudp->pmhdl.slot < 0);
207
208
209 status = pollmgr_add(&pxudp->pmhdl, pxudp->sock, POLLIN);
210 if (status < 0) {
211 pxudp_schedule_delete(pxudp);
212 }
213
214 return POLLIN;
215}
216
217
218/**
219 * POLLMGR_CHAN_PXUDP_DEL handler.
220 */
221static int
222pxudp_pmgr_chan_del(struct pollmgr_handler *handler, SOCKET fd, int revents)
223{
224 struct pxudp *pxudp;
225
226 pxudp = pxudp_chan_recv_strong(handler, fd, revents);
227 if (pxudp == NULL) {
228 return POLLIN;
229 }
230
231 DPRINTF(("pxudp_del: pxudp %p; socket %d\n", (void *)pxudp, pxudp->sock));
232
233 pollmgr_del_slot(pxudp->pmhdl.slot);
234
235 /*
236 * Go back to lwip thread to delete after any pending callbacks
237 * for unprocessed inbound traffic are drained.
238 */
239 pxudp_schedule_delete(pxudp);
240
241 return POLLIN;
242}
243
244
245static struct pxudp *
246pxudp_allocate(void)
247{
248 struct pxudp *pxudp;
249 err_t error;
250
251 pxudp = (struct pxudp *)malloc(sizeof(*pxudp));
252 if (pxudp == NULL) {
253 return NULL;
254 }
255
256 pxudp->pmhdl.callback = NULL;
257 pxudp->pmhdl.data = (void *)pxudp;
258 pxudp->pmhdl.slot = -1;
259
260 pxudp->pcb = NULL;
261 pxudp->sock = INVALID_SOCKET;
262 pxudp->count = 0;
263
264 pxudp->rp = pollmgr_refptr_create(&pxudp->pmhdl);
265 if (pxudp->rp == NULL) {
266 free(pxudp);
267 return NULL;
268 }
269
270 error = sys_mbox_new(&pxudp->inmbox, 16);
271 if (error != ERR_OK) {
272 pollmgr_refptr_unref(pxudp->rp);
273 free(pxudp);
274 return NULL;
275 }
276
277#define CALLBACK_MSG(MSG, FUNC) \
278 do { \
279 pxudp->MSG.type = TCPIP_MSG_CALLBACK_STATIC; \
280 pxudp->MSG.sem = NULL; \
281 pxudp->MSG.msg.cb.function = FUNC; \
282 pxudp->MSG.msg.cb.ctx = (void *)pxudp; \
283 } while (0)
284
285 CALLBACK_MSG(msg_delete, pxudp_pcb_delete_pxudp);
286 CALLBACK_MSG(msg_inbound, pxudp_pcb_write_inbound);
287
288 return pxudp;
289}
290
291
292static void
293pxudp_drain_inmbox(struct pxudp *pxudp)
294{
295 void *ptr;
296
297 if (!sys_mbox_valid(&pxudp->inmbox)) {
298 return;
299 }
300
301 while (sys_mbox_tryfetch(&pxudp->inmbox, &ptr) != SYS_MBOX_EMPTY) {
302 struct pbuf *p = (struct pbuf *)ptr;
303 pbuf_free(p);
304 }
305
306 sys_mbox_free(&pxudp->inmbox);
307 sys_mbox_set_invalid(&pxudp->inmbox);
308}
309
310
311static void
312pxudp_free(struct pxudp *pxudp)
313{
314 pxudp_drain_inmbox(pxudp);
315 free(pxudp);
316}
317
318
319/**
320 * Dissociate pxudp and its udp_pcb.
321 *
322 * Unlike its TCP cousin returns the pcb since UDP pcbs need to be
323 * actively deleted, so save callers the trouble of saving a copy
324 * before calling us.
325 */
326static struct udp_pcb *
327pxudp_pcb_dissociate(struct pxudp *pxudp)
328{
329 struct udp_pcb *pcb;
330
331 if (pxudp == NULL || pxudp->pcb == NULL) {
332 return NULL;
333 }
334
335 pcb = pxudp->pcb;
336
337 udp_recv(pxudp->pcb, NULL, NULL);
338 pxudp->pcb = NULL;
339
340 return pcb;
341}
342
343
344/**
345 * Lwip thread callback invoked via pxudp::msg_delete
346 *
347 * Since we use static messages to communicate to the lwip thread, we
348 * cannot delete pxudp without making sure there are no unprocessed
349 * messages in the lwip thread mailbox.
350 *
351 * The easiest way to ensure that is to send this "delete" message as
352 * the last one and when it's processed we know there are no more and
353 * it's safe to delete pxudp.
354 *
355 * Channel callback should use pxudp_schedule_delete() convenience
356 * function defined below.
357 */
358static void
359pxudp_pcb_delete_pxudp(void *arg)
360{
361 struct pxudp *pxudp = (struct pxudp *)arg;
362 struct udp_pcb *pcb;
363
364 LWIP_ASSERT1(pxudp != NULL);
365
366 if (pxudp->sock != INVALID_SOCKET) {
367 closesocket(pxudp->sock);
368 pxudp->sock = INVALID_SOCKET;
369 }
370
371 pcb = pxudp_pcb_dissociate(pxudp);
372 if (pcb != NULL) {
373 udp_remove(pcb);
374 }
375
376 pollmgr_refptr_unref(pxudp->rp);
377 pxudp_free(pxudp);
378}
379
380
381/**
382 * Poll manager callback should use this convenience wrapper to
383 * schedule pxudp deletion on the lwip thread and to deregister from
384 * the poll manager.
385 */
386static int
387pxudp_schedule_delete(struct pxudp *pxudp)
388{
389 /*
390 * If pollmgr_refptr_get() is called by any channel before
391 * scheduled deletion happens, let them know we are gone.
392 */
393 pxudp->pmhdl.slot = -1;
394
395 /*
396 * Schedule deletion. Since poll manager thread may be pre-empted
397 * right after we send the message, the deletion may actually
398 * happen on the lwip thread before we return from this function,
399 * so it's not safe to refer to pxudp after this call.
400 */
401 proxy_lwip_post(&pxudp->msg_delete);
402
403 /* tell poll manager to deregister us */
404 return -1;
405}
406
407
408/**
409 * New proxied UDP conversation created.
410 * Global callback for udp_proxy_accept().
411 */
412static void
413pxudp_pcb_accept(void *arg, struct udp_pcb *newpcb, struct pbuf *p,
414 ip_addr_t *addr, u16_t port)
415{
416 struct pxudp *pxudp;
417 ipX_addr_t dst_addr;
418 int mapping;
419 int sdom;
420 SOCKET sock;
421
422 LWIP_ASSERT1(newpcb != NULL);
423 LWIP_ASSERT1(p != NULL);
424 LWIP_UNUSED_ARG(arg);
425
426 pxudp = pxudp_allocate();
427 if (pxudp == NULL) {
428 DPRINTF(("pxudp_allocate: failed\n"));
429 udp_remove(newpcb);
430 pbuf_free(p);
431 return;
432 }
433
434 sdom = PCB_ISIPV6(newpcb) ? PF_INET6 : PF_INET;
435 mapping = pxremap_outbound_ipX(PCB_ISIPV6(newpcb), &dst_addr, &newpcb->local_ip);
436
437#if 0 /* XXX: DNS IPv6->IPv4 remapping hack */
438 if (mapping == PXREMAP_MAPPED
439 && newpcb->local_port == 53
440 && PCB_ISIPV6(newpcb))
441 {
442 /*
443 * "Remap" DNS over IPv6 to IPv4 since Ubuntu dnsmasq does not
444 * listen on IPv6.
445 */
446 sdom = PF_INET;
447 ipX_addr_set_loopback(0, &dst_addr);
448 }
449#endif /* DNS IPv6->IPv4 remapping hack */
450
451 sock = proxy_connected_socket(sdom, SOCK_DGRAM,
452 &dst_addr, newpcb->local_port);
453 if (sock == INVALID_SOCKET) {
454 udp_remove(newpcb);
455 pbuf_free(p);
456 return;
457 }
458
459 pxudp->sock = sock;
460 pxudp->pcb = newpcb;
461 udp_recv(newpcb, pxudp_pcb_recv, pxudp);
462
463 pxudp->pmhdl.callback = pxudp_pmgr_pump;
464 pxudp_chan_send(POLLMGR_CHAN_PXUDP_ADD, pxudp);
465
466 /* dispatch directly instead of calling pxudp_pcb_recv() */
467 pxudp_pcb_forward_outbound(pxudp, p, addr, port);
468}
469
470
471/**
472 * udp_recv() callback.
473 */
474static void
475pxudp_pcb_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p,
476 ip_addr_t *addr, u16_t port)
477{
478 struct pxudp *pxudp = (struct pxudp *)arg;
479
480 LWIP_ASSERT1(pxudp != NULL);
481 LWIP_ASSERT1(pcb == pxudp->pcb);
482 LWIP_UNUSED_ARG(pcb);
483
484 if (p != NULL) {
485 pxudp_pcb_forward_outbound(pxudp, p, addr, port);
486 }
487 else {
488 pxudp_pcb_expired(pxudp);
489 }
490}
491
492
493static void
494pxudp_pcb_forward_outbound(struct pxudp *pxudp, struct pbuf *p,
495 ip_addr_t *addr, u16_t port)
496{
497 LWIP_UNUSED_ARG(addr);
498 LWIP_UNUSED_ARG(port);
499
500 if (pxudp->pcb->local_port == 53) {
501 ++pxudp->count;
502 }
503
504 proxy_sendto(pxudp->sock, p, NULL, 0);
505 pbuf_free(p);
506}
507
508
509/**
510 * Proxy udp_pcbs are expired by timer, which is signaled by passing
511 * NULL pbuf to the udp_recv() callback. At that point the pcb is
512 * removed from the list of proxy udp pcbs so no new datagrams will be
513 * delivered.
514 */
515static void
516pxudp_pcb_expired(struct pxudp *pxudp)
517{
518 struct udp_pcb *pcb;
519
520 DPRINTF2(("%s: pxudp %p, pcb %p, sock %d: expired\n",
521 __func__, (void *)pxudp, (void *)pxudp->pcb, pxudp->sock));
522
523 pcb = pxudp_pcb_dissociate(pxudp);
524 if (pcb != NULL) {
525 udp_remove(pcb);
526 }
527
528 pxudp_chan_send_weak(POLLMGR_CHAN_PXUDP_DEL, pxudp);
529}
530
531
532/**
533 */
534static int
535pxudp_pmgr_pump(struct pollmgr_handler *handler, SOCKET fd, int revents)
536{
537 struct pxudp *pxudp;
538 struct pbuf *p;
539 ssize_t nread;
540 err_t error;
541
542 pxudp = (struct pxudp *)handler->data;
543 LWIP_ASSERT1(handler == &pxudp->pmhdl);
544 LWIP_ASSERT1(fd == pxudp->sock);
545 LWIP_UNUSED_ARG(fd);
546
547
548 if (revents & ~(POLLIN|POLLERR)) {
549 DPRINTF(("%s: unexpected revents 0x%x\n", __func__, revents));
550 return pxudp_schedule_delete(pxudp);
551 }
552
553 /*
554 * XXX: AFAICS, there's no way to match the error with the
555 * outgoing datagram that triggered it, since we do non-blocking
556 * sends from lwip thread.
557 */
558 if (revents & POLLERR) {
559 int sockerr = -1;
560 socklen_t optlen = (socklen_t)sizeof(sockerr);
561 int status;
562
563 status = getsockopt(pxudp->sock, SOL_SOCKET,
564 SO_ERROR, (char *)&sockerr, &optlen);
565 if (status < 0) {
566 DPRINTF(("%s: sock %d: SO_ERROR failed:%R[sockerr]\n",
567 __func__, pxudp->sock, SOCKERRNO()));
568 }
569 else {
570 DPRINTF(("%s: sock %d: %R[sockerr]\n",
571 __func__, pxudp->sock, sockerr));
572 }
573 }
574
575 if ((revents & POLLIN) == 0) {
576 return POLLIN;
577 }
578
579 nread = recv(pxudp->sock, pollmgr_udpbuf, sizeof(pollmgr_udpbuf), 0);
580 if (nread == SOCKET_ERROR) {
581 perror(__func__);
582 return POLLIN;
583 }
584
585 p = pbuf_alloc(PBUF_RAW, (u16_t)nread, PBUF_RAM);
586 if (p == NULL) {
587 DPRINTF(("%s: pbuf_alloc(%d) failed\n", __func__, (int)nread));
588 return POLLIN;
589 }
590
591 error = pbuf_take(p, pollmgr_udpbuf, (u16_t)nread);
592 if (error != ERR_OK) {
593 DPRINTF(("%s: pbuf_take(%d) failed\n", __func__, (int)nread));
594 pbuf_free(p);
595 return POLLIN;
596 }
597
598 error = sys_mbox_trypost(&pxudp->inmbox, p);
599 if (error != ERR_OK) {
600 pbuf_free(p);
601 return POLLIN;
602 }
603
604 proxy_lwip_post(&pxudp->msg_inbound);
605
606 return POLLIN;
607}
608
609
610/**
611 * Callback from poll manager to trigger sending to guest.
612 */
613static void
614pxudp_pcb_write_inbound(void *ctx)
615{
616 struct pxudp *pxudp = (struct pxudp *)ctx;
617 LWIP_ASSERT1(pxudp != NULL);
618
619 if (pxudp->pcb == NULL) {
620 return;
621 }
622
623 pxudp_pcb_forward_inbound(pxudp);
624}
625
626
627static void
628pxudp_pcb_forward_inbound(struct pxudp *pxudp)
629{
630 struct pbuf *p;
631 u32_t timo;
632 err_t error;
633
634 if (!sys_mbox_valid(&pxudp->inmbox)) {
635 return;
636 }
637
638 timo = sys_mbox_tryfetch(&pxudp->inmbox, (void **)&p);
639 if (timo == SYS_MBOX_EMPTY) {
640 return;
641 }
642
643 error = udp_send(pxudp->pcb, p);
644 if (error != ERR_OK) {
645 DPRINTF(("%s: udp_send(pcb %p) err %d\n",
646 __func__, (void *)pxudp, error));
647 }
648
649 pbuf_free(p);
650
651 /*
652 * If we enabled counting in pxudp_pcb_forward_outbound() check
653 * that we have (all) the reply(s).
654 */
655 if (pxudp->count > 0) {
656 --pxudp->count;
657 if (pxudp->count == 0) {
658 pxudp_pcb_expired(pxudp);
659 }
660 }
661}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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