VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/NAT/pxping.c@ 101665

Last change on this file since 101665 was 98103, checked in by vboxsync, 22 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 55.3 KB
Line 
1/* $Id: pxping.c 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * NAT Network - ping proxy, raw sockets version.
4 */
5
6/*
7 * Copyright (C) 2013-2023 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#include <iprt/string.h>
36
37#ifndef RT_OS_WINDOWS
38#include <sys/types.h>
39#include <sys/socket.h>
40#ifdef RT_OS_DARWIN
41# define __APPLE_USE_RFC_3542
42#endif
43#include <netinet/in.h>
44#include <poll.h>
45#include <stdint.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#else
50#include <iprt/stdint.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54#include "winpoll.h"
55#endif
56
57#include "lwip/opt.h"
58
59#include "lwip/sys.h"
60#include "lwip/tcpip.h"
61#include "lwip/inet_chksum.h"
62#include "lwip/ip.h"
63#include "lwip/icmp.h"
64
65#if defined(RT_OS_LINUX) && !defined(__USE_GNU)
66#if __GLIBC_PREREQ(2, 8)
67/*
68 * XXX: This is gross. in6_pktinfo is now hidden behind _GNU_SOURCE
69 * https://sourceware.org/bugzilla/show_bug.cgi?id=6775
70 *
71 * But in older glibc versions, e.g. RHEL5, it is not! I don't want
72 * to deal with _GNU_SOURCE now, so as a kludge check for glibc
73 * version. It seems the __USE_GNU guard was introduced in 2.8.
74 */
75struct in6_pktinfo {
76 struct in6_addr ipi6_addr;
77 unsigned int ipi6_ifindex;
78};
79#endif /* __GLIBC_PREREQ */
80#endif /* RT_OS_LINUX && !__USE_GNU */
81
82
83/* forward */
84struct ping_pcb;
85
86
87/**
88 * Global state for ping proxy collected in one entity to minimize
89 * globals. There's only one instance of this structure.
90 *
91 * Raw ICMP sockets are promiscuous, so it doesn't make sense to have
92 * multiple. If this code ever needs to support multiple netifs, the
93 * netif member should be exiled into "pcb".
94 */
95struct pxping {
96 SOCKET sock4;
97
98#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
99# define DF_WITH_IP_HDRINCL
100 int hdrincl;
101#else
102 int df;
103#endif
104 int ttl;
105 int tos;
106
107 SOCKET sock6;
108#ifdef RT_OS_WINDOWS
109 LPFN_WSARECVMSG pfWSARecvMsg6;
110#endif
111 int hopl;
112
113 struct pollmgr_handler pmhdl4;
114 struct pollmgr_handler pmhdl6;
115
116 struct netif *netif;
117
118 /**
119 * Protect lwIP and pmgr accesses to the list of pcbs.
120 */
121 sys_mutex_t lock;
122
123 /*
124 * We need to find pcbs both from the guest side and from the host
125 * side. If we need to support industrial grade ping throughput,
126 * we will need two pcb hashes. For now, a short linked list
127 * should be enough. Cf. pxping_pcb_for_request() and
128 * pxping_pcb_for_reply().
129 */
130#define PXPING_MAX_PCBS 8
131 size_t npcbs;
132 struct ping_pcb *pcbs;
133
134#define TIMEOUT 5
135 int timer_active;
136 size_t timeout_slot;
137 struct ping_pcb *timeout_list[TIMEOUT];
138};
139
140
141/**
142 * Quasi PCB for ping.
143 */
144struct ping_pcb {
145 ipX_addr_t src;
146 ipX_addr_t dst;
147
148 u8_t is_ipv6;
149 u8_t is_mapped;
150
151 u16_t guest_id;
152 u16_t host_id;
153
154 /**
155 * Desired slot in pxping::timeout_list. See pxping_timer().
156 */
157 size_t timeout_slot;
158
159 /**
160 * Chaining for pxping::timeout_list
161 */
162 struct ping_pcb **pprev_timeout;
163 struct ping_pcb *next_timeout;
164
165 /**
166 * Chaining for pxping::pcbs
167 */
168 struct ping_pcb *next;
169
170 union {
171 struct sockaddr_in sin;
172 struct sockaddr_in6 sin6;
173 } peer;
174};
175
176
177/**
178 * lwIP thread callback message for IPv4 ping.
179 *
180 * We pass raw IP datagram for ip_output_if() so we only need pbuf and
181 * netif (from pxping).
182 */
183struct ping_msg {
184 struct tcpip_msg msg;
185 struct pxping *pxping;
186 struct pbuf *p;
187};
188
189
190/**
191 * lwIP thread callback message for IPv6 ping.
192 *
193 * We cannot obtain raw IPv6 datagram from host without extra trouble,
194 * so we pass ICMPv6 payload in pbuf and also other parameters to
195 * ip6_output_if().
196 */
197struct ping6_msg {
198 struct tcpip_msg msg;
199 struct pxping *pxping;
200 struct pbuf *p;
201 ip6_addr_t src, dst;
202 int hopl, tclass;
203};
204
205
206#ifdef RT_OS_WINDOWS
207static int pxping_init_windows(struct pxping *pxping);
208#endif
209static void pxping_recv4(void *arg, struct pbuf *p);
210static void pxping_recv6(void *arg, struct pbuf *p);
211
212static void pxping_timer(void *arg);
213static void pxping_timer_needed(struct pxping *pxping);
214
215static struct ping_pcb *pxping_pcb_for_request(struct pxping *pxping,
216 int is_ipv6,
217 ipX_addr_t *src, ipX_addr_t *dst,
218 u16_t guest_id);
219static struct ping_pcb *pxping_pcb_for_reply(struct pxping *pxping, int is_ipv6,
220 ipX_addr_t *dst, u16_t host_id);
221
222static FNRTSTRFORMATTYPE pxping_pcb_rtstrfmt;
223static struct ping_pcb *pxping_pcb_allocate(struct pxping *pxping);
224static void pxping_pcb_register(struct pxping *pxping, struct ping_pcb *pcb);
225static void pxping_pcb_deregister(struct pxping *pxping, struct ping_pcb *pcb);
226static void pxping_pcb_delete(struct pxping *pxping, struct ping_pcb *pcb);
227static void pxping_timeout_add(struct pxping *pxping, struct ping_pcb *pcb);
228static void pxping_timeout_del(struct pxping *pxping, struct ping_pcb *pcb);
229
230static int pxping_pmgr_pump(struct pollmgr_handler *handler, SOCKET fd, int revents);
231
232static void pxping_pmgr_icmp4(struct pxping *pxping);
233static void pxping_pmgr_icmp4_echo(struct pxping *pxping,
234 u16_t iplen, struct sockaddr_in *peer);
235static void pxping_pmgr_icmp4_error(struct pxping *pxping,
236 u16_t iplen, struct sockaddr_in *peer);
237static void pxping_pmgr_icmp6(struct pxping *pxping);
238static void pxping_pmgr_icmp6_echo(struct pxping *pxping,
239 ip6_addr_t *src, ip6_addr_t *dst,
240 int hopl, int tclass, u16_t icmplen);
241static void pxping_pmgr_icmp6_error(struct pxping *pxping,
242 ip6_addr_t *src, ip6_addr_t *dst,
243 int hopl, int tclass, u16_t icmplen);
244
245static void pxping_pmgr_forward_inbound(struct pxping *pxping, u16_t iplen);
246static void pxping_pcb_forward_inbound(void *arg);
247
248static void pxping_pmgr_forward_inbound6(struct pxping *pxping,
249 ip6_addr_t *src, ip6_addr_t *dst,
250 u8_t hopl, u8_t tclass,
251 u16_t icmplen);
252static void pxping_pcb_forward_inbound6(void *arg);
253
254/*
255 * NB: This is not documented except in RTFS.
256 *
257 * If ip_output_if() is passed dest == NULL then it treats p as
258 * complete IP packet with payload pointing to the IP header. It does
259 * not build IP header, ignores all header-related arguments, fetches
260 * real destination from the header in the pbuf and outputs pbuf to
261 * the specified netif.
262 */
263#define ip_raw_output_if(p, netif) \
264 (ip_output_if((p), NULL, NULL, 0, 0, 0, (netif)))
265
266
267
268static struct pxping g_pxping;
269
270
271err_t
272pxping_init(struct netif *netif, SOCKET sock4, SOCKET sock6)
273{
274 const int on = 1;
275 int status;
276
277 if (sock4 == INVALID_SOCKET && sock6 == INVALID_SOCKET) {
278 return ERR_VAL;
279 }
280
281 g_pxping.netif = netif;
282 sys_mutex_new(&g_pxping.lock);
283
284 g_pxping.sock4 = sock4;
285 if (g_pxping.sock4 != INVALID_SOCKET) {
286#ifdef DF_WITH_IP_HDRINCL
287 g_pxping.hdrincl = 0;
288#else
289 g_pxping.df = -1;
290#endif
291 g_pxping.ttl = -1;
292 g_pxping.tos = 0;
293
294#ifdef RT_OS_LINUX
295 {
296 const int dont = IP_PMTUDISC_DONT;
297 status = setsockopt(sock4, IPPROTO_IP, IP_MTU_DISCOVER,
298 &dont, sizeof(dont));
299 if (status != 0) {
300 DPRINTF(("IP_MTU_DISCOVER: %R[sockerr]\n", SOCKERRNO()));
301 }
302 }
303#endif /* RT_OS_LINUX */
304
305 g_pxping.pmhdl4.callback = pxping_pmgr_pump;
306 g_pxping.pmhdl4.data = (void *)&g_pxping;
307 g_pxping.pmhdl4.slot = -1;
308 pollmgr_add(&g_pxping.pmhdl4, g_pxping.sock4, POLLIN);
309
310 ping_proxy_accept(pxping_recv4, &g_pxping);
311 }
312
313 g_pxping.sock6 = sock6;
314#ifdef RT_OS_WINDOWS
315 /* we need recvmsg */
316 if (g_pxping.sock6 != INVALID_SOCKET) {
317 status = pxping_init_windows(&g_pxping);
318 if (status == SOCKET_ERROR) {
319 g_pxping.sock6 = INVALID_SOCKET;
320 /* close(sock6); */
321 }
322 }
323#endif
324 if (g_pxping.sock6 != INVALID_SOCKET) {
325 g_pxping.hopl = -1;
326
327#if !defined(IPV6_RECVPKTINFO)
328#define IPV6_RECVPKTINFO (IPV6_PKTINFO)
329#endif
330 status = setsockopt(sock6, IPPROTO_IPV6, IPV6_RECVPKTINFO,
331 (const char *)&on, sizeof(on));
332 if (status < 0) {
333 DPRINTF(("IPV6_RECVPKTINFO: %R[sockerr]\n", SOCKERRNO()));
334 /* XXX: for now this is fatal */
335 }
336
337#if !defined(IPV6_RECVHOPLIMIT)
338#define IPV6_RECVHOPLIMIT (IPV6_HOPLIMIT)
339#endif
340 status = setsockopt(sock6, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
341 (const char *)&on, sizeof(on));
342 if (status < 0) {
343 DPRINTF(("IPV6_RECVHOPLIMIT: %R[sockerr]\n", SOCKERRNO()));
344 }
345
346#ifdef IPV6_RECVTCLASS /* new in RFC 3542, there's no RFC 2292 counterpart */
347 /** @todo IPV6_RECVTCLASS */
348#endif
349
350 g_pxping.pmhdl6.callback = pxping_pmgr_pump;
351 g_pxping.pmhdl6.data = (void *)&g_pxping;
352 g_pxping.pmhdl6.slot = -1;
353 pollmgr_add(&g_pxping.pmhdl6, g_pxping.sock6, POLLIN);
354
355 ping6_proxy_accept(pxping_recv6, &g_pxping);
356 }
357
358 status = RTStrFormatTypeRegister("ping_pcb", pxping_pcb_rtstrfmt, NULL);
359 AssertRC(status);
360
361 return ERR_OK;
362}
363
364
365#ifdef RT_OS_WINDOWS
366static int
367pxping_init_windows(struct pxping *pxping)
368{
369 GUID WSARecvMsgGUID = WSAID_WSARECVMSG;
370 DWORD nread;
371 int status;
372
373 pxping->pfWSARecvMsg6 = NULL;
374 status = WSAIoctl(pxping->sock6,
375 SIO_GET_EXTENSION_FUNCTION_POINTER,
376 &WSARecvMsgGUID, sizeof(WSARecvMsgGUID),
377 &pxping->pfWSARecvMsg6, sizeof(pxping->pfWSARecvMsg6),
378 &nread,
379 NULL, NULL);
380 return status;
381}
382#endif /* RT_OS_WINDOWS */
383
384
385static u32_t
386chksum_delta_16(u16_t oval, u16_t nval)
387{
388 u32_t sum = (u16_t)~oval;
389 sum += nval;
390 return sum;
391}
392
393
394static u32_t
395chksum_update_16(u16_t *oldp, u16_t nval)
396{
397 u32_t sum = chksum_delta_16(*oldp, nval);
398 *oldp = nval;
399 return sum;
400}
401
402
403static u32_t
404chksum_delta_32(u32_t oval, u32_t nval)
405{
406 u32_t sum = ~oval;
407 sum = FOLD_U32T(sum);
408 sum += FOLD_U32T(nval);
409 return sum;
410}
411
412
413static u32_t
414chksum_update_32(u32_t *oldp, u32_t nval)
415{
416 u32_t sum = chksum_delta_32(*oldp, nval);
417 *oldp = nval;
418 return sum;
419}
420
421
422static u32_t
423chksum_delta_ipv6(const ip6_addr_t *oldp, const ip6_addr_t *newp)
424{
425 u32_t sum;
426
427 sum = chksum_delta_32(oldp->addr[0], newp->addr[0]);
428 sum += chksum_delta_32(oldp->addr[1], newp->addr[1]);
429 sum += chksum_delta_32(oldp->addr[2], newp->addr[2]);
430 sum += chksum_delta_32(oldp->addr[3], newp->addr[3]);
431
432 return sum;
433}
434
435
436static u32_t
437chksum_update_ipv6(ip6_addr_t *oldp, const ip6_addr_t *newp)
438{
439 u32_t sum;
440
441 sum = chksum_update_32(&oldp->addr[0], newp->addr[0]);
442 sum += chksum_update_32(&oldp->addr[1], newp->addr[1]);
443 sum += chksum_update_32(&oldp->addr[2], newp->addr[2]);
444 sum += chksum_update_32(&oldp->addr[3], newp->addr[3]);
445
446 return sum;
447}
448
449
450/**
451 * ICMP Echo Request in pbuf "p" is to be proxied.
452 */
453static void
454pxping_recv4(void *arg, struct pbuf *p)
455{
456 struct pxping *pxping = (struct pxping *)arg;
457 struct ping_pcb *pcb;
458#ifdef DF_WITH_IP_HDRINCL
459 struct ip_hdr iph_orig;
460#endif
461 struct icmp_echo_hdr icmph_orig;
462 struct ip_hdr *iph;
463 struct icmp_echo_hdr *icmph;
464 int df, ttl, tos;
465 u32_t sum;
466 u16_t iphlen;
467 int status;
468
469 iphlen = ip_current_header_tot_len();
470 if (iphlen != IP_HLEN) { /* we don't do options */
471 pbuf_free(p);
472 return;
473 }
474
475 iph = (/* UNCONST */ struct ip_hdr *)ip_current_header();
476 icmph = (struct icmp_echo_hdr *)p->payload;
477
478 pcb = pxping_pcb_for_request(pxping, 0,
479 ipX_current_src_addr(),
480 ipX_current_dest_addr(),
481 icmph->id);
482 if (pcb == NULL) {
483 pbuf_free(p);
484 return;
485 }
486
487 DPRINTF(("ping %p: %R[ping_pcb] seq %d len %u ttl %d\n",
488 pcb, pcb,
489 ntohs(icmph->seqno), (unsigned int)p->tot_len,
490 IPH_TTL(iph)));
491
492 ttl = IPH_TTL(iph);
493 if (!pcb->is_mapped) {
494 if (RT_UNLIKELY(ttl == 1)) {
495 status = pbuf_header(p, iphlen); /* back to IP header */
496 if (RT_LIKELY(status == 0)) {
497 icmp_time_exceeded(p, ICMP_TE_TTL);
498 }
499 pbuf_free(p);
500 return;
501 }
502 --ttl;
503 }
504
505 /*
506 * OS X doesn't provide a socket option to control fragmentation.
507 * Solaris doesn't provide IP_DONTFRAG on all releases we support.
508 * In this case we have to use IP_HDRINCL. We don't want to use
509 * it always since it doesn't handle fragmentation (but that's ok
510 * for DF) and Windows doesn't do automatic source address
511 * selection with IP_HDRINCL.
512 */
513 df = (IPH_OFFSET(iph) & PP_HTONS(IP_DF)) != 0;
514
515#ifdef DF_WITH_IP_HDRINCL
516 if (df != pxping->hdrincl) {
517 status = setsockopt(pxping->sock4, IPPROTO_IP, IP_HDRINCL,
518 &df, sizeof(df));
519 if (RT_LIKELY(status == 0)) {
520 pxping->hdrincl = df;
521 }
522 else {
523 DPRINTF(("IP_HDRINCL: %R[sockerr]\n", SOCKERRNO()));
524 }
525 }
526
527 if (pxping->hdrincl) {
528 status = pbuf_header(p, iphlen); /* back to IP header */
529 if (RT_UNLIKELY(status != 0)) {
530 pbuf_free(p);
531 return;
532 }
533
534 /* we will overwrite IP header, save original for ICMP errors */
535 memcpy(&iph_orig, iph, iphlen);
536
537 if (pcb->is_mapped) {
538 ip4_addr_set_u32(&iph->dest, pcb->peer.sin.sin_addr.s_addr);
539 }
540
541 if (g_proxy_options->src4 != NULL) {
542 ip4_addr_set_u32(&iph->src, g_proxy_options->src4->sin_addr.s_addr);
543 }
544 else {
545 /* let the kernel select suitable source address */
546 ip_addr_set_any(&iph->src);
547 }
548
549 IPH_TTL_SET(iph, ttl); /* already decremented */
550 IPH_ID_SET(iph, 0); /* kernel will set one */
551#ifdef RT_OS_DARWIN
552 /* wants ip_offset and ip_len fields in host order */
553 IPH_OFFSET_SET(iph, ntohs(IPH_OFFSET(iph)));
554 IPH_LEN_SET(iph, ntohs(IPH_LEN(iph)));
555 /* wants checksum of everything (sic!), in host order */
556 sum = inet_chksum_pbuf(p);
557 IPH_CHKSUM_SET(iph, sum);
558#else /* !RT_OS_DARWIN */
559 IPH_CHKSUM_SET(iph, 0); /* kernel will recalculate */
560#endif
561 }
562 else /* !pxping->hdrincl */
563#endif /* DF_WITH_IP_HDRINCL */
564 {
565#if !defined(DF_WITH_IP_HDRINCL)
566 /* control DF flag via setsockopt(2) */
567#define USE_DF_OPTION(_Optname) \
568 const int dfopt = _Optname; \
569 const char * const dfoptname = #_Optname; NOREF(dfoptname)
570#if defined(RT_OS_LINUX)
571 USE_DF_OPTION(IP_MTU_DISCOVER);
572 df = df ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
573#elif defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
574 USE_DF_OPTION(IP_DONTFRAG);
575#elif defined(RT_OS_WINDOWS)
576 USE_DF_OPTION(IP_DONTFRAGMENT);
577#endif
578 if (df != pxping->df) {
579 status = setsockopt(pxping->sock4, IPPROTO_IP, dfopt,
580 (char *)&df, sizeof(df));
581 if (RT_LIKELY(status == 0)) {
582 pxping->df = df;
583 }
584 else {
585 DPRINTF(("%s: %R[sockerr]\n", dfoptname, SOCKERRNO()));
586 }
587 }
588#endif /* !DF_WITH_IP_HDRINCL */
589
590 if (ttl != pxping->ttl) {
591 status = setsockopt(pxping->sock4, IPPROTO_IP, IP_TTL,
592 (char *)&ttl, sizeof(ttl));
593 if (RT_LIKELY(status == 0)) {
594 pxping->ttl = ttl;
595 }
596 else {
597 DPRINTF(("IP_TTL: %R[sockerr]\n", SOCKERRNO()));
598 }
599 }
600
601 tos = IPH_TOS(iph);
602 if (tos != pxping->tos) {
603 status = setsockopt(pxping->sock4, IPPROTO_IP, IP_TOS,
604 (char *)&tos, sizeof(tos));
605 if (RT_LIKELY(status == 0)) {
606 pxping->tos = tos;
607 }
608 else {
609 DPRINTF(("IP_TOS: %R[sockerr]\n", SOCKERRNO()));
610 }
611 }
612 }
613
614 /* rewrite ICMP echo header */
615 memcpy(&icmph_orig, icmph, sizeof(*icmph));
616 sum = (u16_t)~icmph->chksum;
617 sum += chksum_update_16(&icmph->id, pcb->host_id);
618 sum = FOLD_U32T(sum);
619 icmph->chksum = ~sum;
620
621 status = proxy_sendto(pxping->sock4, p,
622 &pcb->peer.sin, sizeof(pcb->peer.sin));
623 if (status != 0) {
624 int error = -status;
625 DPRINTF(("%s: sendto: %R[sockerr]\n", __func__, error));
626
627#ifdef DF_WITH_IP_HDRINCL
628 if (pxping->hdrincl) {
629 /* restore original IP header */
630 memcpy(iph, &iph_orig, iphlen);
631 }
632 else
633#endif
634 {
635 status = pbuf_header(p, iphlen); /* back to IP header */
636 if (RT_UNLIKELY(status != 0)) {
637 pbuf_free(p);
638 return;
639 }
640 }
641
642 /* restore original ICMP header */
643 memcpy(icmph, &icmph_orig, sizeof(*icmph));
644
645 /*
646 * Some ICMP errors may be generated by the kernel and we read
647 * them from the socket and forward them normally, hence the
648 * ifdefs below.
649 */
650 switch (error) {
651
652#if !( defined(RT_OS_SOLARIS) \
653 || (defined(RT_OS_LINUX) && !defined(DF_WITH_IP_HDRINCL)) \
654 )
655 case EMSGSIZE:
656 icmp_dest_unreach(p, ICMP_DUR_FRAG);
657 break;
658#endif
659
660 case ENETDOWN:
661 case ENETUNREACH:
662 icmp_dest_unreach(p, ICMP_DUR_NET);
663 break;
664
665 case EHOSTDOWN:
666 case EHOSTUNREACH:
667 icmp_dest_unreach(p, ICMP_DUR_HOST);
668 break;
669 }
670 }
671
672 pbuf_free(p);
673}
674
675
676/**
677 * ICMPv6 Echo Request in pbuf "p" is to be proxied.
678 */
679static void
680pxping_recv6(void *arg, struct pbuf *p)
681{
682 struct pxping *pxping = (struct pxping *)arg;
683 struct ping_pcb *pcb;
684 struct ip6_hdr *iph;
685 struct icmp6_echo_hdr *icmph;
686 int hopl;
687 u16_t iphlen;
688 u16_t id, seq;
689 int status;
690
691 iph = (/* UNCONST */ struct ip6_hdr *)ip6_current_header();
692 iphlen = ip_current_header_tot_len();
693
694 icmph = (struct icmp6_echo_hdr *)p->payload;
695
696 id = icmph->id;
697 seq = icmph->seqno;
698
699 pcb = pxping_pcb_for_request(pxping, 1,
700 ipX_current_src_addr(),
701 ipX_current_dest_addr(),
702 id);
703 if (pcb == NULL) {
704 pbuf_free(p);
705 return;
706 }
707
708 DPRINTF(("ping %p: %R[ping_pcb] seq %d len %u hopl %d\n",
709 pcb, pcb,
710 ntohs(seq), (unsigned int)p->tot_len,
711 IP6H_HOPLIM(iph)));
712
713 hopl = IP6H_HOPLIM(iph);
714 if (!pcb->is_mapped) {
715 if (hopl == 1) {
716 status = pbuf_header(p, iphlen); /* back to IP header */
717 if (RT_LIKELY(status == 0)) {
718 icmp6_time_exceeded(p, ICMP6_TE_HL);
719 }
720 pbuf_free(p);
721 return;
722 }
723 --hopl;
724 }
725
726 /*
727 * Rewrite ICMPv6 echo header. We don't need to recompute the
728 * checksum since, unlike IPv4, checksum includes pseudo-header.
729 * OS computes checksum for us on send() since it needs to select
730 * source address.
731 */
732 icmph->id = pcb->host_id;
733
734 /** @todo use control messages to save a syscall? */
735 if (hopl != pxping->hopl) {
736 status = setsockopt(pxping->sock6, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
737 (char *)&hopl, sizeof(hopl));
738 if (status == 0) {
739 pxping->hopl = hopl;
740 }
741 else {
742 DPRINTF(("IPV6_HOPLIMIT: %R[sockerr]\n", SOCKERRNO()));
743 }
744 }
745
746 status = proxy_sendto(pxping->sock6, p,
747 &pcb->peer.sin6, sizeof(pcb->peer.sin6));
748 if (status != 0) {
749 int error = -status;
750 DPRINTF(("%s: sendto: %R[sockerr]\n", __func__, error));
751
752 status = pbuf_header(p, iphlen); /* back to IP header */
753 if (RT_UNLIKELY(status != 0)) {
754 pbuf_free(p);
755 return;
756 }
757
758 /* restore original ICMP header */
759 icmph->id = pcb->guest_id;
760
761 switch (error) {
762 case EACCES:
763 icmp6_dest_unreach(p, ICMP6_DUR_PROHIBITED);
764 break;
765
766#ifdef ENONET
767 case ENONET:
768#endif
769 case ENETDOWN:
770 case ENETUNREACH:
771 case EHOSTDOWN:
772 case EHOSTUNREACH:
773 icmp6_dest_unreach(p, ICMP6_DUR_NO_ROUTE);
774 break;
775 }
776 }
777
778 pbuf_free(p);
779}
780
781
782/**
783 * Formatter for %R[ping_pcb].
784 */
785static DECLCALLBACK(size_t)
786pxping_pcb_rtstrfmt(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
787 const char *pszType, const void *pvValue,
788 int cchWidth, int cchPrecision, unsigned int fFlags,
789 void *pvUser)
790{
791 const struct ping_pcb *pcb = (const struct ping_pcb *)pvValue;
792 size_t cb = 0;
793
794 NOREF(cchWidth);
795 NOREF(cchPrecision);
796 NOREF(fFlags);
797 NOREF(pvUser);
798
799 AssertReturn(strcmp(pszType, "ping_pcb") == 0, 0);
800
801 if (pcb == NULL) {
802 return RTStrFormat(pfnOutput, pvArgOutput, NULL, NULL, "(null)");
803 }
804
805 /* XXX: %RTnaipv4 takes the value, but %RTnaipv6 takes the pointer */
806 if (pcb->is_ipv6) {
807 cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, NULL,
808 "%RTnaipv6 -> %RTnaipv6", &pcb->src, &pcb->dst);
809 if (pcb->is_mapped) {
810 cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, NULL,
811 " (%RTnaipv6)", &pcb->peer.sin6.sin6_addr);
812 }
813 }
814 else {
815 cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, NULL,
816 "%RTnaipv4 -> %RTnaipv4",
817 ip4_addr_get_u32(ipX_2_ip(&pcb->src)),
818 ip4_addr_get_u32(ipX_2_ip(&pcb->dst)));
819 if (pcb->is_mapped) {
820 cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, NULL,
821 " (%RTnaipv4)", pcb->peer.sin.sin_addr.s_addr);
822 }
823 }
824
825 cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, NULL,
826 " id %04x->%04x", ntohs(pcb->guest_id), ntohs(pcb->host_id));
827
828 return cb;
829}
830
831
832static struct ping_pcb *
833pxping_pcb_allocate(struct pxping *pxping)
834{
835 struct ping_pcb *pcb;
836
837 if (pxping->npcbs >= PXPING_MAX_PCBS) {
838 return NULL;
839 }
840
841 pcb = (struct ping_pcb *)malloc(sizeof(*pcb));
842 if (pcb == NULL) {
843 return NULL;
844 }
845
846 ++pxping->npcbs;
847 return pcb;
848}
849
850
851static void
852pxping_pcb_delete(struct pxping *pxping, struct ping_pcb *pcb)
853{
854 LWIP_ASSERT1(pxping->npcbs > 0);
855 LWIP_ASSERT1(pcb->next == NULL);
856 LWIP_ASSERT1(pcb->pprev_timeout == NULL);
857
858 DPRINTF(("%s: ping %p\n", __func__, (void *)pcb));
859
860 --pxping->npcbs;
861 free(pcb);
862}
863
864
865static void
866pxping_timeout_add(struct pxping *pxping, struct ping_pcb *pcb)
867{
868 struct ping_pcb **chain;
869
870 LWIP_ASSERT1(pcb->pprev_timeout == NULL);
871
872 chain = &pxping->timeout_list[pcb->timeout_slot];
873 if ((pcb->next_timeout = *chain) != NULL) {
874 (*chain)->pprev_timeout = &pcb->next_timeout;
875 }
876 *chain = pcb;
877 pcb->pprev_timeout = chain;
878}
879
880
881static void
882pxping_timeout_del(struct pxping *pxping, struct ping_pcb *pcb)
883{
884 LWIP_UNUSED_ARG(pxping);
885
886 LWIP_ASSERT1(pcb->pprev_timeout != NULL);
887 if (pcb->next_timeout != NULL) {
888 pcb->next_timeout->pprev_timeout = pcb->pprev_timeout;
889 }
890 *pcb->pprev_timeout = pcb->next_timeout;
891 pcb->pprev_timeout = NULL;
892 pcb->next_timeout = NULL;
893}
894
895
896static void
897pxping_pcb_register(struct pxping *pxping, struct ping_pcb *pcb)
898{
899 pcb->next = pxping->pcbs;
900 pxping->pcbs = pcb;
901
902 pxping_timeout_add(pxping, pcb);
903}
904
905
906static void
907pxping_pcb_deregister(struct pxping *pxping, struct ping_pcb *pcb)
908{
909 struct ping_pcb **p;
910
911 for (p = &pxping->pcbs; *p != NULL; p = &(*p)->next) {
912 if (*p == pcb) {
913 *p = pcb->next;
914 pcb->next = NULL;
915 break;
916 }
917 }
918
919 pxping_timeout_del(pxping, pcb);
920}
921
922
923static struct ping_pcb *
924pxping_pcb_for_request(struct pxping *pxping,
925 int is_ipv6, ipX_addr_t *src, ipX_addr_t *dst,
926 u16_t guest_id)
927{
928 struct ping_pcb *pcb;
929
930 /* on lwip thread, so no concurrent updates */
931 for (pcb = pxping->pcbs; pcb != NULL; pcb = pcb->next) {
932 if (pcb->guest_id == guest_id
933 && pcb->is_ipv6 == is_ipv6
934 && ipX_addr_cmp(is_ipv6, &pcb->dst, dst)
935 && ipX_addr_cmp(is_ipv6, &pcb->src, src))
936 {
937 break;
938 }
939 }
940
941 if (pcb == NULL) {
942 int mapped;
943
944 pcb = pxping_pcb_allocate(pxping);
945 if (pcb == NULL) {
946 return NULL;
947 }
948
949 pcb->is_ipv6 = is_ipv6;
950 ipX_addr_copy(is_ipv6, pcb->src, *src);
951 ipX_addr_copy(is_ipv6, pcb->dst, *dst);
952
953 pcb->guest_id = guest_id;
954#ifdef RT_OS_WINDOWS
955# define random() (rand())
956#endif
957 pcb->host_id = random() & 0xffffUL;
958
959 pcb->pprev_timeout = NULL;
960 pcb->next_timeout = NULL;
961
962 if (is_ipv6) {
963 pcb->peer.sin6.sin6_family = AF_INET6;
964#if HAVE_SA_LEN
965 pcb->peer.sin6.sin6_len = sizeof(pcb->peer.sin6);
966#endif
967 pcb->peer.sin6.sin6_port = htons(IPPROTO_ICMPV6);
968 pcb->peer.sin6.sin6_flowinfo = 0;
969 mapped = pxremap_outbound_ip6((ip6_addr_t *)&pcb->peer.sin6.sin6_addr,
970 ipX_2_ip6(&pcb->dst));
971 }
972 else {
973 pcb->peer.sin.sin_family = AF_INET;
974#if HAVE_SA_LEN
975 pcb->peer.sin.sin_len = sizeof(pcb->peer.sin);
976#endif
977 pcb->peer.sin.sin_port = htons(IPPROTO_ICMP);
978 mapped = pxremap_outbound_ip4((ip_addr_t *)&pcb->peer.sin.sin_addr,
979 ipX_2_ip(&pcb->dst));
980 }
981
982 if (mapped == PXREMAP_FAILED) {
983 free(pcb);
984 return NULL;
985 }
986 else {
987 pcb->is_mapped = (mapped == PXREMAP_MAPPED);
988 }
989
990 pcb->timeout_slot = pxping->timeout_slot;
991
992 sys_mutex_lock(&pxping->lock);
993 pxping_pcb_register(pxping, pcb);
994 sys_mutex_unlock(&pxping->lock);
995
996 DPRINTF(("ping %p: %R[ping_pcb] - created\n", pcb, pcb));
997
998 pxping_timer_needed(pxping);
999 }
1000 else {
1001 /* just bump up expiration timeout lazily */
1002 DPRINTF(("ping %p: %R[ping_pcb] - slot %d -> %d\n",
1003 pcb, pcb,
1004 (unsigned int)pcb->timeout_slot,
1005 (unsigned int)pxping->timeout_slot));
1006 pcb->timeout_slot = pxping->timeout_slot;
1007 }
1008
1009 return pcb;
1010}
1011
1012
1013/* GCC 12.2.1 complains that array subscript is partly outside
1014 * of array bounds in expansion of ipX_addr_cmp. */
1015#if RT_GNUC_PREREQ(12, 0)
1016# pragma GCC diagnostic push
1017# pragma GCC diagnostic ignored "-Warray-bounds"
1018#endif
1019/**
1020 * Called on pollmgr thread. Caller must do the locking since caller
1021 * is going to use the returned pcb, which needs to be protected from
1022 * being expired by pxping_timer() on lwip thread.
1023 */
1024static struct ping_pcb *
1025pxping_pcb_for_reply(struct pxping *pxping,
1026 int is_ipv6, ipX_addr_t *dst, u16_t host_id)
1027{
1028 struct ping_pcb *pcb;
1029
1030 for (pcb = pxping->pcbs; pcb != NULL; pcb = pcb->next) {
1031 if (pcb->host_id == host_id
1032 && pcb->is_ipv6 == is_ipv6
1033 /* XXX: allow broadcast pings? */
1034 && ipX_addr_cmp(is_ipv6, &pcb->dst, dst))
1035 {
1036 return pcb;
1037 }
1038 }
1039
1040 return NULL;
1041}
1042#if RT_GNUC_PREREQ(12, 0)
1043# pragma GCC diagnostic pop
1044#endif
1045
1046
1047static void
1048pxping_timer(void *arg)
1049{
1050 struct pxping *pxping = (struct pxping *)arg;
1051 struct ping_pcb **chain, *pcb;
1052
1053 pxping->timer_active = 0;
1054
1055 /*
1056 * New slot points to the list of pcbs to check for expiration.
1057 */
1058 LWIP_ASSERT1(pxping->timeout_slot < TIMEOUT);
1059 if (++pxping->timeout_slot == TIMEOUT) {
1060 pxping->timeout_slot = 0;
1061 }
1062
1063 chain = &pxping->timeout_list[pxping->timeout_slot];
1064 pcb = *chain;
1065
1066 /* protect from pollmgr concurrent reads */
1067 sys_mutex_lock(&pxping->lock);
1068
1069 while (pcb != NULL) {
1070 struct ping_pcb *xpcb = pcb;
1071 pcb = pcb->next_timeout;
1072
1073 if (xpcb->timeout_slot == pxping->timeout_slot) {
1074 /* expired */
1075 pxping_pcb_deregister(pxping, xpcb);
1076 pxping_pcb_delete(pxping, xpcb);
1077 }
1078 else {
1079 /*
1080 * If there was another request, we updated timeout_slot
1081 * but delayed actually moving the pcb until now.
1082 */
1083 pxping_timeout_del(pxping, xpcb); /* from current slot */
1084 pxping_timeout_add(pxping, xpcb); /* to new slot */
1085 }
1086 }
1087
1088 sys_mutex_unlock(&pxping->lock);
1089 pxping_timer_needed(pxping);
1090}
1091
1092
1093static void
1094pxping_timer_needed(struct pxping *pxping)
1095{
1096 if (!pxping->timer_active && pxping->pcbs != NULL) {
1097 pxping->timer_active = 1;
1098 sys_timeout(1 * 1000, pxping_timer, pxping);
1099 }
1100}
1101
1102
1103static int
1104pxping_pmgr_pump(struct pollmgr_handler *handler, SOCKET fd, int revents)
1105{
1106 struct pxping *pxping;
1107
1108 pxping = (struct pxping *)handler->data;
1109 LWIP_ASSERT1(fd == pxping->sock4 || fd == pxping->sock6);
1110
1111 if (revents & ~(POLLIN|POLLERR)) {
1112 DPRINTF0(("%s: unexpected revents 0x%x\n", __func__, revents));
1113 return POLLIN;
1114 }
1115
1116 if (revents & POLLERR) {
1117 int sockerr = -1;
1118 socklen_t optlen = (socklen_t)sizeof(sockerr);
1119 int status;
1120
1121 status = getsockopt(fd, SOL_SOCKET,
1122 SO_ERROR, (char *)&sockerr, &optlen);
1123 if (status < 0) {
1124 DPRINTF(("%s: sock %d: SO_ERROR failed: %R[sockerr]\n",
1125 __func__, fd, SOCKERRNO()));
1126 }
1127 else {
1128 DPRINTF(("%s: sock %d: %R[sockerr]\n",
1129 __func__, fd, sockerr));
1130 }
1131 }
1132
1133 if ((revents & POLLIN) == 0) {
1134 return POLLIN;
1135 }
1136
1137 if (fd == pxping->sock4) {
1138 pxping_pmgr_icmp4(pxping);
1139 }
1140 else /* fd == pxping->sock6 */ {
1141 pxping_pmgr_icmp6(pxping);
1142 }
1143
1144 return POLLIN;
1145}
1146
1147
1148/**
1149 * Process incoming ICMP message for the host.
1150 * NB: we will get a lot of spam here and have to sift through it.
1151 */
1152static void
1153pxping_pmgr_icmp4(struct pxping *pxping)
1154{
1155 struct sockaddr_in sin;
1156 socklen_t salen = sizeof(sin);
1157 ssize_t nread;
1158 struct ip_hdr *iph;
1159 struct icmp_echo_hdr *icmph;
1160 u16_t iplen, ipoff;
1161
1162 memset(&sin, 0, sizeof(sin));
1163
1164 /*
1165 * Reads from raw IPv4 sockets deliver complete IP datagrams with
1166 * IP header included.
1167 */
1168 nread = recvfrom(pxping->sock4, pollmgr_udpbuf, sizeof(pollmgr_udpbuf), 0,
1169 (struct sockaddr *)&sin, &salen);
1170 if (nread < 0) {
1171 DPRINTF(("%s: %R[sockerr]\n", __func__, SOCKERRNO()));
1172 return;
1173 }
1174
1175 if (nread < IP_HLEN) {
1176 DPRINTF2(("%s: read %d bytes, IP header truncated\n",
1177 __func__, (unsigned int)nread));
1178 return;
1179 }
1180
1181 iph = (struct ip_hdr *)pollmgr_udpbuf;
1182
1183 /* match version */
1184 if (IPH_V(iph) != 4) {
1185 DPRINTF2(("%s: unexpected IP version %d\n", __func__, IPH_V(iph)));
1186 return;
1187 }
1188
1189 /* no fragmentation */
1190 ipoff = IPH_OFFSET(iph);
1191#if defined(RT_OS_DARWIN)
1192 /* darwin reports IPH_OFFSET in host byte order */
1193 ipoff = htons(ipoff);
1194 IPH_OFFSET_SET(iph, ipoff);
1195#endif
1196 if ((ipoff & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) {
1197 DPRINTF2(("%s: dropping fragmented datagram (0x%04x)\n",
1198 __func__, ntohs(ipoff)));
1199 return;
1200 }
1201
1202 /* no options */
1203 if (IPH_HL(iph) * 4 != IP_HLEN) {
1204 DPRINTF2(("%s: dropping datagram with options (IP header length %d)\n",
1205 __func__, IPH_HL(iph) * 4));
1206 return;
1207 }
1208
1209 if (IPH_PROTO(iph) != IP_PROTO_ICMP) {
1210 DPRINTF2(("%s: unexpected protocol %d\n", __func__, IPH_PROTO(iph)));
1211 return;
1212 }
1213
1214 iplen = IPH_LEN(iph);
1215#if !defined(RT_OS_DARWIN)
1216 /* darwin reports IPH_LEN in host byte order */
1217 iplen = ntohs(iplen);
1218#endif
1219#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
1220 /* darwin and solaris change IPH_LEN to payload length only */
1221 iplen += IP_HLEN; /* we verified there are no options */
1222 IPH_LEN_SET(iph, htons(iplen));
1223#endif
1224 if (nread < iplen) {
1225 DPRINTF2(("%s: read %d bytes but total length is %d bytes\n",
1226 __func__, (unsigned int)nread, (unsigned int)iplen));
1227 return;
1228 }
1229
1230 if (iplen < IP_HLEN + ICMP_HLEN) {
1231 DPRINTF2(("%s: IP length %d bytes, ICMP header truncated\n",
1232 __func__, iplen));
1233 return;
1234 }
1235
1236 icmph = (struct icmp_echo_hdr *)(pollmgr_udpbuf + IP_HLEN);
1237 if (ICMPH_TYPE(icmph) == ICMP_ER) {
1238 pxping_pmgr_icmp4_echo(pxping, iplen, &sin);
1239 }
1240 else if (ICMPH_TYPE(icmph) == ICMP_DUR || ICMPH_TYPE(icmph) == ICMP_TE) {
1241 pxping_pmgr_icmp4_error(pxping, iplen, &sin);
1242 }
1243#if 1
1244 else {
1245 DPRINTF2(("%s: ignoring ICMP type %d\n", __func__, ICMPH_TYPE(icmph)));
1246 }
1247#endif
1248}
1249
1250
1251/**
1252 * Check if this incoming ICMP echo reply is for one of our pings and
1253 * forward it to the guest.
1254 */
1255static void
1256pxping_pmgr_icmp4_echo(struct pxping *pxping,
1257 u16_t iplen, struct sockaddr_in *peer)
1258{
1259 struct ip_hdr *iph;
1260 struct icmp_echo_hdr *icmph;
1261 u16_t id, seq;
1262 ip_addr_t guest_ip, target_ip;
1263 int mapped;
1264 struct ping_pcb *pcb;
1265 u16_t guest_id;
1266 u16_t oipsum;
1267 u32_t sum;
1268 RT_NOREF(peer);
1269
1270 iph = (struct ip_hdr *)pollmgr_udpbuf;
1271 icmph = (struct icmp_echo_hdr *)(pollmgr_udpbuf + IP_HLEN);
1272
1273 id = icmph->id;
1274 seq = icmph->seqno;
1275
1276 DPRINTF(("<--- PING %RTnaipv4 id 0x%x seq %d\n",
1277 peer->sin_addr.s_addr, ntohs(id), ntohs(seq)));
1278
1279 /*
1280 * Is this a reply to one of our pings?
1281 */
1282
1283 ip_addr_copy(target_ip, iph->src);
1284 mapped = pxremap_inbound_ip4(&target_ip, &target_ip);
1285 if (mapped == PXREMAP_FAILED) {
1286 return;
1287 }
1288 if (mapped == PXREMAP_ASIS && IPH_TTL(iph) == 1) {
1289 DPRINTF2(("%s: dropping packet with ttl 1\n", __func__));
1290 return;
1291 }
1292
1293 sys_mutex_lock(&pxping->lock);
1294 pcb = pxping_pcb_for_reply(pxping, 0, ip_2_ipX(&target_ip), id);
1295 if (pcb == NULL) {
1296 sys_mutex_unlock(&pxping->lock);
1297 DPRINTF2(("%s: no match\n", __func__));
1298 return;
1299 }
1300
1301 DPRINTF2(("%s: pcb %p\n", __func__, (void *)pcb));
1302
1303 /* save info before unlocking since pcb may expire */
1304 ip_addr_copy(guest_ip, *ipX_2_ip(&pcb->src));
1305 guest_id = pcb->guest_id;
1306
1307 sys_mutex_unlock(&pxping->lock);
1308
1309
1310 /*
1311 * Rewrite headers and forward to guest.
1312 */
1313
1314 /* rewrite ICMP echo header */
1315 sum = (u16_t)~icmph->chksum;
1316 sum += chksum_update_16(&icmph->id, guest_id);
1317 sum = FOLD_U32T(sum);
1318 icmph->chksum = ~sum;
1319
1320 /* rewrite IP header */
1321 oipsum = IPH_CHKSUM(iph);
1322 if (oipsum == 0) {
1323 /* Solaris doesn't compute checksum for local replies */
1324 ip_addr_copy(iph->dest, guest_ip);
1325 if (mapped == PXREMAP_MAPPED) {
1326 ip_addr_copy(iph->src, target_ip);
1327 }
1328 else {
1329 IPH_TTL_SET(iph, IPH_TTL(iph) - 1);
1330 }
1331 IPH_CHKSUM_SET(iph, inet_chksum(iph, ntohs(IPH_LEN(iph))));
1332 }
1333 else {
1334 sum = (u16_t)~oipsum;
1335 sum += chksum_update_32((u32_t *)&iph->dest,
1336 ip4_addr_get_u32(&guest_ip));
1337 if (mapped == PXREMAP_MAPPED) {
1338 sum += chksum_update_32((u32_t *)&iph->src,
1339 ip4_addr_get_u32(&target_ip));
1340 }
1341 else {
1342 IPH_TTL_SET(iph, IPH_TTL(iph) - 1);
1343 sum += PP_NTOHS(~0x0100);
1344 }
1345 sum = FOLD_U32T(sum);
1346 IPH_CHKSUM_SET(iph, ~sum);
1347 }
1348
1349 pxping_pmgr_forward_inbound(pxping, iplen);
1350}
1351
1352
1353/**
1354 * Check if this incoming ICMP error (destination unreachable or time
1355 * exceeded) is about one of our pings and forward it to the guest.
1356 */
1357static void
1358pxping_pmgr_icmp4_error(struct pxping *pxping,
1359 u16_t iplen, struct sockaddr_in *peer)
1360{
1361 struct ip_hdr *iph, *oiph;
1362 struct icmp_echo_hdr *icmph, *oicmph;
1363 u16_t oipoff, oiphlen, oiplen;
1364 u16_t id, seq;
1365 ip_addr_t guest_ip, target_ip, error_ip;
1366 int target_mapped, error_mapped;
1367 struct ping_pcb *pcb;
1368 u16_t guest_id;
1369 u32_t sum;
1370 RT_NOREF(peer);
1371
1372 iph = (struct ip_hdr *)pollmgr_udpbuf;
1373 icmph = (struct icmp_echo_hdr *)(pollmgr_udpbuf + IP_HLEN);
1374
1375 /*
1376 * Inner IP datagram is not checked by the kernel and may be
1377 * anything, possibly malicious.
1378 */
1379
1380 oipoff = IP_HLEN + ICMP_HLEN;
1381 oiplen = iplen - oipoff; /* NB: truncated length, not IPH_LEN(oiph) */
1382 if (oiplen < IP_HLEN) {
1383 DPRINTF2(("%s: original datagram truncated to %d bytes\n",
1384 __func__, oiplen));
1385 }
1386
1387 /* IP header of the original message */
1388 oiph = (struct ip_hdr *)(pollmgr_udpbuf + oipoff);
1389
1390 /* match version */
1391 if (IPH_V(oiph) != 4) {
1392 DPRINTF2(("%s: unexpected IP version %d\n", __func__, IPH_V(oiph)));
1393 return;
1394 }
1395
1396 /* can't match fragments except the first one */
1397 if ((IPH_OFFSET(oiph) & PP_HTONS(IP_OFFMASK)) != 0) {
1398 DPRINTF2(("%s: ignoring fragment with offset %d\n",
1399 __func__, ntohs(IPH_OFFSET(oiph) & PP_HTONS(IP_OFFMASK))));
1400 return;
1401 }
1402
1403 if (IPH_PROTO(oiph) != IP_PROTO_ICMP) {
1404#if 0
1405 /* don't spam with every "destination unreachable" in the system */
1406 DPRINTF2(("%s: ignoring protocol %d\n", __func__, IPH_PROTO(oiph)));
1407#endif
1408 return;
1409 }
1410
1411 oiphlen = IPH_HL(oiph) * 4;
1412 if (oiplen < oiphlen + ICMP_HLEN) {
1413 DPRINTF2(("%s: original datagram truncated to %d bytes\n",
1414 __func__, oiplen));
1415 return;
1416 }
1417
1418 oicmph = (struct icmp_echo_hdr *)(pollmgr_udpbuf + oipoff + oiphlen);
1419 if (ICMPH_TYPE(oicmph) != ICMP_ECHO) {
1420 DPRINTF2(("%s: ignoring ICMP error for original ICMP type %d\n",
1421 __func__, ICMPH_TYPE(oicmph)));
1422 return;
1423 }
1424
1425 id = oicmph->id;
1426 seq = oicmph->seqno;
1427
1428 DPRINTF2(("%s: ping %RTnaipv4 id 0x%x seq %d",
1429 __func__, ip4_addr_get_u32(&oiph->dest), ntohs(id), ntohs(seq)));
1430 if (ICMPH_TYPE(icmph) == ICMP_DUR) {
1431 DPRINTF2((" unreachable (code %d)\n", ICMPH_CODE(icmph)));
1432 }
1433 else {
1434 DPRINTF2((" time exceeded\n"));
1435 }
1436
1437
1438 /*
1439 * Is the inner (failed) datagram one of our pings?
1440 */
1441
1442 ip_addr_copy(target_ip, oiph->dest); /* inner (failed) */
1443 target_mapped = pxremap_inbound_ip4(&target_ip, &target_ip);
1444 if (target_mapped == PXREMAP_FAILED) {
1445 return;
1446 }
1447
1448 sys_mutex_lock(&pxping->lock);
1449 pcb = pxping_pcb_for_reply(pxping, 0, ip_2_ipX(&target_ip), id);
1450 if (pcb == NULL) {
1451 sys_mutex_unlock(&pxping->lock);
1452 DPRINTF2(("%s: no match\n", __func__));
1453 return;
1454 }
1455
1456 DPRINTF2(("%s: pcb %p\n", __func__, (void *)pcb));
1457
1458 /* save info before unlocking since pcb may expire */
1459 ip_addr_copy(guest_ip, *ipX_2_ip(&pcb->src));
1460 guest_id = pcb->guest_id;
1461
1462 sys_mutex_unlock(&pxping->lock);
1463
1464
1465 /*
1466 * Rewrite both inner and outer headers and forward to guest.
1467 * Note that the checksum of the outer ICMP error message is
1468 * preserved by the changes we do to inner headers.
1469 */
1470
1471 ip_addr_copy(error_ip, iph->src); /* node that reports the error */
1472 error_mapped = pxremap_inbound_ip4(&error_ip, &error_ip);
1473 if (error_mapped == PXREMAP_FAILED) {
1474 return;
1475 }
1476 if (error_mapped == PXREMAP_ASIS && IPH_TTL(iph) == 1) {
1477 DPRINTF2(("%s: dropping packet with ttl 1\n", __func__));
1478 return;
1479 }
1480
1481 /* rewrite inner ICMP echo header */
1482 sum = (u16_t)~oicmph->chksum;
1483 sum += chksum_update_16(&oicmph->id, guest_id);
1484 sum = FOLD_U32T(sum);
1485 oicmph->chksum = ~sum;
1486
1487 /* rewrite inner IP header */
1488#if defined(RT_OS_DARWIN)
1489 /* darwin converts inner length to host byte order too */
1490 IPH_LEN_SET(oiph, htons(IPH_LEN(oiph)));
1491#endif
1492 sum = (u16_t)~IPH_CHKSUM(oiph);
1493 sum += chksum_update_32((u32_t *)&oiph->src, ip4_addr_get_u32(&guest_ip));
1494 if (target_mapped == PXREMAP_MAPPED) {
1495 sum += chksum_update_32((u32_t *)&oiph->dest, ip4_addr_get_u32(&target_ip));
1496 }
1497 sum = FOLD_U32T(sum);
1498 IPH_CHKSUM_SET(oiph, ~sum);
1499
1500 /* rewrite outer IP header */
1501 sum = (u16_t)~IPH_CHKSUM(iph);
1502 sum += chksum_update_32((u32_t *)&iph->dest, ip4_addr_get_u32(&guest_ip));
1503 if (error_mapped == PXREMAP_MAPPED) {
1504 sum += chksum_update_32((u32_t *)&iph->src, ip4_addr_get_u32(&error_ip));
1505 }
1506 else {
1507 IPH_TTL_SET(iph, IPH_TTL(iph) - 1);
1508 sum += PP_NTOHS(~0x0100);
1509 }
1510 sum = FOLD_U32T(sum);
1511 IPH_CHKSUM_SET(iph, ~sum);
1512
1513 pxping_pmgr_forward_inbound(pxping, iplen);
1514}
1515
1516
1517/**
1518 * Process incoming ICMPv6 message for the host.
1519 * NB: we will get a lot of spam here and have to sift through it.
1520 */
1521static void
1522pxping_pmgr_icmp6(struct pxping *pxping)
1523{
1524#ifndef RT_OS_WINDOWS
1525 struct msghdr mh;
1526 ssize_t nread;
1527#else
1528 WSAMSG mh;
1529 DWORD nread;
1530#endif
1531 IOVEC iov[1];
1532 static u8_t cmsgbuf[128];
1533 struct cmsghdr *cmh;
1534 struct sockaddr_in6 sin6;
1535 /* socklen_t salen = sizeof(sin6); - unused */
1536 struct icmp6_echo_hdr *icmph;
1537 struct in6_pktinfo *pktinfo;
1538 int hopl, tclass;
1539#ifdef RT_OS_WINDOWS
1540 int status;
1541#endif
1542
1543 /*
1544 * Reads from raw IPv6 sockets deliver only the payload. Full
1545 * headers are available via recvmsg(2)/cmsg(3).
1546 */
1547 IOVEC_SET_BASE(iov[0], pollmgr_udpbuf);
1548 IOVEC_SET_LEN(iov[0], sizeof(pollmgr_udpbuf));
1549
1550 memset(&mh, 0, sizeof(mh));
1551#ifndef RT_OS_WINDOWS
1552 mh.msg_name = &sin6;
1553 mh.msg_namelen = sizeof(sin6);
1554 mh.msg_iov = iov;
1555 mh.msg_iovlen = 1;
1556 mh.msg_control = cmsgbuf;
1557 mh.msg_controllen = sizeof(cmsgbuf);
1558 mh.msg_flags = 0;
1559
1560 nread = recvmsg(pxping->sock6, &mh, 0);
1561 if (nread < 0) {
1562 DPRINTF(("%s: %R[sockerr]\n", __func__, SOCKERRNO()));
1563 return;
1564 }
1565#else /* RT_OS_WINDOWS */
1566 mh.name = (LPSOCKADDR)&sin6;
1567 mh.namelen = sizeof(sin6);
1568 mh.lpBuffers = iov;
1569 mh.dwBufferCount = 1;
1570 mh.Control.buf = cmsgbuf;
1571 mh.Control.len = sizeof(cmsgbuf);
1572 mh.dwFlags = 0;
1573
1574 status = (*pxping->pfWSARecvMsg6)(pxping->sock6, &mh, &nread, NULL, NULL);
1575 if (status == SOCKET_ERROR) {
1576 DPRINTF2(("%s: error %d\n", __func__, WSAGetLastError()));
1577 return;
1578 }
1579#endif
1580
1581 icmph = (struct icmp6_echo_hdr *)pollmgr_udpbuf;
1582
1583 DPRINTF2(("%s: %RTnaipv6 ICMPv6: ", __func__, &sin6.sin6_addr));
1584
1585 if (icmph->type == ICMP6_TYPE_EREP) {
1586 DPRINTF2(("echo reply %04x %u\n",
1587 (unsigned int)icmph->id, (unsigned int)icmph->seqno));
1588 }
1589 else { /* XXX */
1590 if (icmph->type == ICMP6_TYPE_EREQ) {
1591 DPRINTF2(("echo request %04x %u\n",
1592 (unsigned int)icmph->id, (unsigned int)icmph->seqno));
1593 }
1594 else if (icmph->type == ICMP6_TYPE_DUR) {
1595 DPRINTF2(("destination unreachable\n"));
1596 }
1597 else if (icmph->type == ICMP6_TYPE_PTB) {
1598 DPRINTF2(("packet too big\n"));
1599 }
1600 else if (icmph->type == ICMP6_TYPE_TE) {
1601 DPRINTF2(("time exceeded\n"));
1602 }
1603 else if (icmph->type == ICMP6_TYPE_PP) {
1604 DPRINTF2(("parameter problem\n"));
1605 }
1606 else {
1607 DPRINTF2(("type %d len %u\n", icmph->type, (unsigned int)nread));
1608 }
1609
1610 if (icmph->type >= ICMP6_TYPE_EREQ) {
1611 return; /* informational message */
1612 }
1613 }
1614
1615 pktinfo = NULL;
1616 hopl = -1;
1617 tclass = -1;
1618 for (cmh = CMSG_FIRSTHDR(&mh); cmh != NULL; cmh = CMSG_NXTHDR(&mh, cmh)) {
1619 if (cmh->cmsg_len == 0)
1620 break;
1621
1622 if (cmh->cmsg_level == IPPROTO_IPV6
1623 && cmh->cmsg_type == IPV6_HOPLIMIT
1624 && cmh->cmsg_len == CMSG_LEN(sizeof(int)))
1625 {
1626 hopl = *(int *)CMSG_DATA(cmh);
1627 DPRINTF2(("hoplimit = %d\n", hopl));
1628 }
1629
1630 if (cmh->cmsg_level == IPPROTO_IPV6
1631 && cmh->cmsg_type == IPV6_PKTINFO
1632 && cmh->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
1633 {
1634 pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmh);
1635 DPRINTF2(("pktinfo found\n"));
1636 }
1637 }
1638
1639 if (pktinfo == NULL) {
1640 /*
1641 * ip6_output_if() doesn't do checksum for us so we need to
1642 * manually recompute it - for this we must know the
1643 * destination address of the pseudo-header that we will
1644 * rewrite with guest's address. (TODO: yeah, yeah, we can
1645 * compute it from scratch...)
1646 */
1647 DPRINTF2(("%s: unable to get pktinfo\n", __func__));
1648 return;
1649 }
1650
1651 if (hopl < 0) {
1652 hopl = LWIP_ICMP6_HL;
1653 }
1654
1655 if (icmph->type == ICMP6_TYPE_EREP) {
1656 pxping_pmgr_icmp6_echo(pxping,
1657 (ip6_addr_t *)&sin6.sin6_addr,
1658 (ip6_addr_t *)&pktinfo->ipi6_addr,
1659 hopl, tclass, (u16_t)nread);
1660 }
1661 else if (icmph->type < ICMP6_TYPE_EREQ) {
1662 pxping_pmgr_icmp6_error(pxping,
1663 (ip6_addr_t *)&sin6.sin6_addr,
1664 (ip6_addr_t *)&pktinfo->ipi6_addr,
1665 hopl, tclass, (u16_t)nread);
1666 }
1667}
1668
1669
1670/**
1671 * Check if this incoming ICMPv6 echo reply is for one of our pings
1672 * and forward it to the guest.
1673 */
1674static void
1675pxping_pmgr_icmp6_echo(struct pxping *pxping,
1676 ip6_addr_t *src, ip6_addr_t *dst,
1677 int hopl, int tclass, u16_t icmplen)
1678{
1679 struct icmp6_echo_hdr *icmph;
1680 ip6_addr_t guest_ip, target_ip;
1681 int mapped;
1682 struct ping_pcb *pcb;
1683 u16_t id, guest_id;
1684 u32_t sum;
1685
1686 ip6_addr_copy(target_ip, *src);
1687 mapped = pxremap_inbound_ip6(&target_ip, &target_ip);
1688 if (mapped == PXREMAP_FAILED) {
1689 return;
1690 }
1691 else if (mapped == PXREMAP_ASIS) {
1692 if (hopl == 1) {
1693 DPRINTF2(("%s: dropping packet with ttl 1\n", __func__));
1694 return;
1695 }
1696 --hopl;
1697 }
1698
1699 icmph = (struct icmp6_echo_hdr *)pollmgr_udpbuf;
1700 id = icmph->id;
1701
1702 sys_mutex_lock(&pxping->lock);
1703 pcb = pxping_pcb_for_reply(pxping, 1, ip6_2_ipX(&target_ip), id);
1704 if (pcb == NULL) {
1705 sys_mutex_unlock(&pxping->lock);
1706 DPRINTF2(("%s: no match\n", __func__));
1707 return;
1708 }
1709
1710 DPRINTF2(("%s: pcb %p\n", __func__, (void *)pcb));
1711
1712 /* save info before unlocking since pcb may expire */
1713 ip6_addr_copy(guest_ip, *ipX_2_ip6(&pcb->src));
1714 guest_id = pcb->guest_id;
1715
1716 sys_mutex_unlock(&pxping->lock);
1717
1718 /* rewrite ICMPv6 echo header */
1719 sum = (u16_t)~icmph->chksum;
1720 sum += chksum_update_16(&icmph->id, guest_id);
1721 sum += chksum_delta_ipv6(dst, &guest_ip); /* pseudo */
1722 if (mapped) {
1723 sum += chksum_delta_ipv6(src, &target_ip); /* pseudo */
1724 }
1725 sum = FOLD_U32T(sum);
1726 icmph->chksum = ~sum;
1727
1728 pxping_pmgr_forward_inbound6(pxping,
1729 &target_ip, /* echo reply src */
1730 &guest_ip, /* echo reply dst */
1731 hopl, tclass, icmplen);
1732}
1733
1734
1735/**
1736 * Check if this incoming ICMPv6 error is about one of our pings and
1737 * forward it to the guest.
1738 */
1739static void
1740pxping_pmgr_icmp6_error(struct pxping *pxping,
1741 ip6_addr_t *src, ip6_addr_t *dst,
1742 int hopl, int tclass, u16_t icmplen)
1743{
1744 struct icmp6_hdr *icmph;
1745 u8_t *bufptr;
1746 size_t buflen, hlen;
1747 int proto;
1748 struct ip6_hdr *oiph;
1749 struct icmp6_echo_hdr *oicmph;
1750 struct ping_pcb *pcb;
1751 ip6_addr_t guest_ip, target_ip, error_ip;
1752 int target_mapped, error_mapped;
1753 u16_t guest_id;
1754 u32_t sum;
1755
1756 icmph = (struct icmp6_hdr *)pollmgr_udpbuf;
1757
1758 /*
1759 * Inner IP datagram is not checked by the kernel and may be
1760 * anything, possibly malicious.
1761 */
1762 oiph = NULL;
1763 oicmph = NULL;
1764
1765 bufptr = pollmgr_udpbuf;
1766 buflen = icmplen;
1767
1768 hlen = sizeof(*icmph);
1769 proto = IP6_NEXTH_ENCAPS; /* i.e. IPv6, lwIP's name is unfortuate */
1770 for (;;) {
1771 if (hlen > buflen) {
1772 DPRINTF2(("truncated datagram inside ICMPv6 error message is too short\n"));
1773 return;
1774 }
1775 buflen -= hlen;
1776 bufptr += hlen;
1777
1778 if (proto == IP6_NEXTH_ENCAPS && oiph == NULL) { /* outermost IPv6 */
1779 oiph = (struct ip6_hdr *)bufptr;
1780 if (IP6H_V(oiph) != 6) {
1781 DPRINTF2(("%s: unexpected IP version %d\n", __func__, IP6H_V(oiph)));
1782 return;
1783 }
1784
1785 proto = IP6H_NEXTH(oiph);
1786 hlen = IP6_HLEN;
1787 }
1788 else if (proto == IP6_NEXTH_ICMP6) {
1789 oicmph = (struct icmp6_echo_hdr *)bufptr;
1790 break;
1791 }
1792 else if (proto == IP6_NEXTH_ROUTING
1793 || proto == IP6_NEXTH_HOPBYHOP
1794 || proto == IP6_NEXTH_DESTOPTS)
1795 {
1796 proto = bufptr[0];
1797 hlen = (bufptr[1] + 1) * 8;
1798 }
1799 else {
1800 DPRINTF2(("%s: stopping at protocol %d\n", __func__, proto));
1801 break;
1802 }
1803 }
1804
1805 if (oiph == NULL || oicmph == NULL) {
1806 return;
1807 }
1808
1809 if (buflen < sizeof(*oicmph)) {
1810 DPRINTF2(("%s: original ICMPv6 is truncated too short\n", __func__));
1811 return;
1812 }
1813
1814 if (oicmph->type != ICMP6_TYPE_EREQ) {
1815 DPRINTF2(("%s: ignoring original ICMPv6 type %d\n", __func__, oicmph->type));
1816 return;
1817 }
1818
1819 ip6_addr_copy(target_ip, oiph->dest); /* inner (failed) */
1820 target_mapped = pxremap_inbound_ip6(&target_ip, &target_ip);
1821 if (target_mapped == PXREMAP_FAILED) {
1822 return;
1823 }
1824
1825 sys_mutex_lock(&pxping->lock);
1826 pcb = pxping_pcb_for_reply(pxping, 1, ip6_2_ipX(&target_ip), oicmph->id);
1827 if (pcb == NULL) {
1828 sys_mutex_unlock(&pxping->lock);
1829 DPRINTF2(("%s: no match\n", __func__));
1830 return;
1831 }
1832
1833 DPRINTF2(("%s: pcb %p\n", __func__, (void *)pcb));
1834
1835 /* save info before unlocking since pcb may expire */
1836 ip6_addr_copy(guest_ip, *ipX_2_ip6(&pcb->src));
1837 guest_id = pcb->guest_id;
1838
1839 sys_mutex_unlock(&pxping->lock);
1840
1841
1842 /*
1843 * Rewrite inner and outer headers and forward to guest. Note
1844 * that IPv6 has no IP header checksum, but uses pseudo-header for
1845 * ICMPv6, so we update both in one go, adjusting ICMPv6 checksum
1846 * as we rewrite IP header.
1847 */
1848
1849 ip6_addr_copy(error_ip, *src); /* node that reports the error */
1850 error_mapped = pxremap_inbound_ip6(&error_ip, &error_ip);
1851 if (error_mapped == PXREMAP_FAILED) {
1852 return;
1853 }
1854 if (error_mapped == PXREMAP_ASIS && hopl == 1) {
1855 DPRINTF2(("%s: dropping packet with ttl 1\n", __func__));
1856 return;
1857 }
1858
1859 /* rewrite inner ICMPv6 echo header and inner IPv6 header */
1860 sum = (u16_t)~oicmph->chksum;
1861 sum += chksum_update_16(&oicmph->id, guest_id);
1862 sum += chksum_update_ipv6((ip6_addr_t *)&oiph->src, &guest_ip);
1863 if (target_mapped) {
1864 sum += chksum_delta_ipv6((ip6_addr_t *)&oiph->dest, &target_ip);
1865 }
1866 sum = FOLD_U32T(sum);
1867 oicmph->chksum = ~sum;
1868
1869 /* rewrite outer ICMPv6 error header */
1870 sum = (u16_t)~icmph->chksum;
1871 sum += chksum_delta_ipv6(dst, &guest_ip); /* pseudo */
1872 if (error_mapped) {
1873 sum += chksum_delta_ipv6(src, &error_ip); /* pseudo */
1874 }
1875 sum = FOLD_U32T(sum);
1876 icmph->chksum = ~sum;
1877
1878 pxping_pmgr_forward_inbound6(pxping,
1879 &error_ip, /* error src */
1880 &guest_ip, /* error dst */
1881 hopl, tclass, icmplen);
1882}
1883
1884
1885/**
1886 * Hand off ICMP datagram to the lwip thread where it will be
1887 * forwarded to the guest.
1888 *
1889 * We no longer need ping_pcb. The pcb may get expired on the lwip
1890 * thread, but we have already patched necessary information into the
1891 * datagram.
1892 */
1893static void
1894pxping_pmgr_forward_inbound(struct pxping *pxping, u16_t iplen)
1895{
1896 struct pbuf *p;
1897 struct ping_msg *msg;
1898 err_t error;
1899
1900 p = pbuf_alloc(PBUF_LINK, iplen, PBUF_RAM);
1901 if (p == NULL) {
1902 DPRINTF(("%s: pbuf_alloc(%d) failed\n",
1903 __func__, (unsigned int)iplen));
1904 return;
1905 }
1906
1907 error = pbuf_take(p, pollmgr_udpbuf, iplen);
1908 if (error != ERR_OK) {
1909 DPRINTF(("%s: pbuf_take(%d) failed\n",
1910 __func__, (unsigned int)iplen));
1911 pbuf_free(p);
1912 return;
1913 }
1914
1915 msg = (struct ping_msg *)malloc(sizeof(*msg));
1916 if (msg == NULL) {
1917 pbuf_free(p);
1918 return;
1919 }
1920
1921 msg->msg.type = TCPIP_MSG_CALLBACK_STATIC;
1922 msg->msg.sem = NULL;
1923 msg->msg.msg.cb.function = pxping_pcb_forward_inbound;
1924 msg->msg.msg.cb.ctx = (void *)msg;
1925
1926 msg->pxping = pxping;
1927 msg->p = p;
1928
1929 proxy_lwip_post(&msg->msg);
1930}
1931
1932
1933static void
1934pxping_pcb_forward_inbound(void *arg)
1935{
1936 struct ping_msg *msg = (struct ping_msg *)arg;
1937 err_t error;
1938
1939 LWIP_ASSERT1(msg != NULL);
1940 LWIP_ASSERT1(msg->pxping != NULL);
1941 LWIP_ASSERT1(msg->p != NULL);
1942
1943 error = ip_raw_output_if(msg->p, msg->pxping->netif);
1944 if (error != ERR_OK) {
1945 DPRINTF(("%s: ip_output_if: %s\n",
1946 __func__, proxy_lwip_strerr(error)));
1947 }
1948 pbuf_free(msg->p);
1949 free(msg);
1950}
1951
1952
1953static void
1954pxping_pmgr_forward_inbound6(struct pxping *pxping,
1955 ip6_addr_t *src, ip6_addr_t *dst,
1956 u8_t hopl, u8_t tclass,
1957 u16_t icmplen)
1958{
1959 struct pbuf *p;
1960 struct ping6_msg *msg;
1961
1962 err_t error;
1963
1964 p = pbuf_alloc(PBUF_IP, icmplen, PBUF_RAM);
1965 if (p == NULL) {
1966 DPRINTF(("%s: pbuf_alloc(%d) failed\n",
1967 __func__, (unsigned int)icmplen));
1968 return;
1969 }
1970
1971 error = pbuf_take(p, pollmgr_udpbuf, icmplen);
1972 if (error != ERR_OK) {
1973 DPRINTF(("%s: pbuf_take(%d) failed\n",
1974 __func__, (unsigned int)icmplen));
1975 pbuf_free(p);
1976 return;
1977 }
1978
1979 msg = (struct ping6_msg *)malloc(sizeof(*msg));
1980 if (msg == NULL) {
1981 pbuf_free(p);
1982 return;
1983 }
1984
1985 msg->msg.type = TCPIP_MSG_CALLBACK_STATIC;
1986 msg->msg.sem = NULL;
1987 msg->msg.msg.cb.function = pxping_pcb_forward_inbound6;
1988 msg->msg.msg.cb.ctx = (void *)msg;
1989
1990 msg->pxping = pxping;
1991 msg->p = p;
1992 ip6_addr_copy(msg->src, *src);
1993 ip6_addr_copy(msg->dst, *dst);
1994 msg->hopl = hopl;
1995 msg->tclass = tclass;
1996
1997 proxy_lwip_post(&msg->msg);
1998}
1999
2000
2001static void
2002pxping_pcb_forward_inbound6(void *arg)
2003{
2004 struct ping6_msg *msg = (struct ping6_msg *)arg;
2005 err_t error;
2006
2007 LWIP_ASSERT1(msg != NULL);
2008 LWIP_ASSERT1(msg->pxping != NULL);
2009 LWIP_ASSERT1(msg->p != NULL);
2010
2011 error = ip6_output_if(msg->p,
2012 &msg->src, &msg->dst, msg->hopl, msg->tclass,
2013 IP6_NEXTH_ICMP6, msg->pxping->netif);
2014 if (error != ERR_OK) {
2015 DPRINTF(("%s: ip6_output_if: %s\n",
2016 __func__, proxy_lwip_strerr(error)));
2017 }
2018 pbuf_free(msg->p);
2019 free(msg);
2020}
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