VirtualBox

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

Last change on this file since 94068 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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