VirtualBox

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

Last change on this file since 53544 was 52257, checked in by vboxsync, 10 years ago

NAT Network: Check if IP_MTU_DISCOVER defined instead of RT_OS_LINUX
for consistency with other tests.

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