VirtualBox

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

Last change on this file since 58725 was 56300, checked in by vboxsync, 9 years ago

NetworkServices: Updated (C) year.

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