VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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