VirtualBox

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

Last change on this file since 49566 was 49555, checked in by vboxsync, 11 years ago

pxping_timeout_del: mark pxping argument as unused.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.2 KB
Line 
1/* -*- indent-tabs-mode: nil; -*- */
2
3#include "winutils.h"
4#include "proxy.h"
5#include "proxy_pollmgr.h"
6#include "pxremap.h"
7
8#ifndef RT_OS_WINDOWS
9#include <sys/types.h>
10#include <sys/socket.h>
11#include <netinet/in.h>
12#include <arpa/inet.h> /* XXX: inet_ntop */
13#include <poll.h>
14#include <stdint.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#else
19#include <iprt/stdint.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include "winpoll.h"
24#endif
25
26#include "lwip/opt.h"
27
28#include "lwip/sys.h"
29#include "lwip/tcpip.h"
30#include "lwip/inet_chksum.h"
31#include "lwip/ip.h"
32#include "lwip/icmp.h"
33
34#if 1 /* XXX: force debug for now */
35#undef DPRINTF0
36#undef DPRINTF
37#undef DPRINTF1
38#undef DPRINTF2
39#define DPRINTF0(args) do { printf args; } while (0)
40#define DPRINTF(args) do { printf args; } while (0)
41#define DPRINTF1(args) do { printf args; } while (0)
42#define DPRINTF2(args) do { printf args; } while (0)
43#endif
44
45
46/* forward */
47struct ping_pcb;
48
49
50/**
51 * Global state for ping proxy collected in one entity to minimize
52 * globals. There's only one instance of this structure.
53 *
54 * Raw ICMP sockets are promiscuous, so it doesn't make sense to have
55 * multiple. If this code ever needs to support multiple netifs, the
56 * netif member should be exiled into "pcb".
57 */
58struct pxping {
59 SOCKET sock4;
60 int ttl;
61 int tos;
62
63 SOCKET sock6;
64
65 struct pollmgr_handler pmhdl4;
66 struct pollmgr_handler pmhdl6;
67
68 struct netif *netif;
69
70 /**
71 * Protect lwIP and pmgr accesses to the list of pcbs.
72 */
73 sys_mutex_t lock;
74
75 /*
76 * We need to find pcbs both from the guest side and from the host
77 * side. If we need to support industrial grade ping throughput,
78 * we will need two pcb hashes. For now, a short linked list
79 * should be enough. Cf. pxping_pcb_for_request() and
80 * pxping_pcb_for_reply().
81 */
82#define PXPING_MAX_PCBS 8
83 size_t npcbs;
84 struct ping_pcb *pcbs;
85
86#define TIMEOUT 5
87 int timer_active;
88 size_t timeout_slot;
89 struct ping_pcb *timeout_list[TIMEOUT];
90};
91
92
93/**
94 * Quasi PCB for ping.
95 */
96struct ping_pcb {
97 ipX_addr_t src;
98 ipX_addr_t dst;
99
100 u8_t is_ipv6;
101 u8_t is_mapped;
102
103 u16_t guest_id;
104 u16_t host_id;
105
106 /**
107 * Desired slot in pxping::timeout_list. See pxping_timer().
108 */
109 size_t timeout_slot;
110
111 /**
112 * Chaining for pxping::timeout_list
113 */
114 struct ping_pcb **pprev_timeout;
115 struct ping_pcb *next_timeout;
116
117 /**
118 * Chaining for pxping::pcbs
119 */
120 struct ping_pcb *next;
121
122 union {
123 struct sockaddr_in sin;
124 struct sockaddr_in6 sin6;
125 } peer;
126};
127
128
129/**
130 * lwIP thread callback message.
131 */
132struct ping_msg {
133 struct tcpip_msg msg;
134 struct pxping *pxping;
135 struct pbuf *p;
136};
137
138
139static void pxping_recv4(void *arg, struct pbuf *p);
140
141static void pxping_timer(void *arg);
142static void pxping_timer_needed(struct pxping *pxping);
143
144static struct ping_pcb *pxping_pcb_for_request(struct pxping *pxping,
145 int is_ipv6,
146 ipX_addr_t *src, ipX_addr_t *dst,
147 u16_t guest_id);
148static struct ping_pcb *pxping_pcb_for_reply(struct pxping *pxping, int is_ipv6,
149 ipX_addr_t *dst, u16_t host_id);
150
151static struct ping_pcb *pxping_pcb_allocate(struct pxping *pxping);
152static void pxping_pcb_register(struct pxping *pxping, struct ping_pcb *pcb);
153static void pxping_pcb_deregister(struct pxping *pxping, struct ping_pcb *pcb);
154static void pxping_pcb_delete(struct pxping *pxping, struct ping_pcb *pcb);
155static void pxping_timeout_add(struct pxping *pxping, struct ping_pcb *pcb);
156static void pxping_timeout_del(struct pxping *pxping, struct ping_pcb *pcb);
157static void pxping_pcb_debug_print(struct ping_pcb *pcb);
158
159static struct ping_msg *pxping_msg_allocate(struct pxping *pxping, struct pbuf *p);
160
161static int pxping_pmgr_pump(struct pollmgr_handler *handler, SOCKET fd, int revents);
162static void pxping_pmgr_icmp4(struct pxping *pxping,
163 size_t nread, struct sockaddr_in *peer);
164static void pxping_pmgr_icmp4_echo(struct pxping *pxping,
165 u16_t iplen, struct sockaddr_in *peer);
166static void pxping_pmgr_icmp4_error(struct pxping *pxping,
167 u16_t iplen, struct sockaddr_in *peer);
168static void pxping_pmgr_forward_inbound(struct pxping *pxping, u16_t iplen);
169static void pxping_pcb_forward_inbound(void *arg);
170
171/*
172 * NB: This is not documented except in RTFS.
173 *
174 * If ip_output_if() is passed dest == NULL then it treats p as
175 * complete IP packet with payload pointing to the IP header. It does
176 * not build IP header, ignores all header-related arguments, fetches
177 * real destination from the header in the pbuf and outputs pbuf to
178 * the specified netif.
179 */
180#define ip_raw_output_if(p, netif) \
181 (ip_output_if((p), NULL, NULL, 0, 0, 0, (netif)))
182
183
184
185static struct pxping g_pxping;
186
187
188err_t
189pxping_init(struct netif *netif, SOCKET sock)
190{
191 if (sock == INVALID_SOCKET) {
192 return ERR_VAL;
193 }
194
195 g_pxping.netif = netif;
196
197 g_pxping.sock4 = sock;
198 g_pxping.ttl = -1;
199 g_pxping.tos = 0;
200
201 sys_mutex_new(&g_pxping.lock);
202
203 g_pxping.pmhdl4.callback = pxping_pmgr_pump;
204 g_pxping.pmhdl4.data = (void *)&g_pxping;
205 g_pxping.pmhdl4.slot = -1;
206
207 pollmgr_add(&g_pxping.pmhdl4, g_pxping.sock4, POLLIN);
208
209 ping_proxy_accept(pxping_recv4, &g_pxping);
210 return ERR_OK;
211}
212
213
214static u32_t
215update16_with_chksum(u16_t *oldp, u16_t h)
216{
217 u32_t sum = (u16_t)~*oldp;
218 sum += h;
219
220 *oldp = h;
221 return sum;
222}
223
224
225static u32_t
226update32_with_chksum(u32_t *oldp, u32_t u)
227{
228 u32_t sum = ~*oldp;
229 sum = FOLD_U32T(sum);
230 sum += FOLD_U32T(u);
231
232 *oldp = u;
233 return sum;
234}
235
236
237/**
238 * ICMP Echo Request in pbuf "p" is to be proxied.
239 */
240static void
241pxping_recv4(void *arg, struct pbuf *p)
242{
243 struct pxping *pxping = (struct pxping *)arg;
244 struct ping_pcb *pcb;
245 struct ip_hdr *iph;
246 struct icmp_echo_hdr *icmph;
247 int ttl, tos;
248 u32_t sum;
249 u16_t iphlen;
250 u16_t id, seq;
251 int status;
252
253 iph = (/* UNCONST */ struct ip_hdr *)ip_current_header();
254 iphlen = ip_current_header_tot_len();
255
256 icmph = (struct icmp_echo_hdr *)p->payload;
257
258 id = icmph->id;
259 seq = icmph->seqno;
260
261 pcb = pxping_pcb_for_request(pxping, 0,
262 ip_2_ipX(ip_current_src_addr()),
263 ip_2_ipX(ip_current_dest_addr()),
264 id);
265 if (pcb == NULL) {
266 pbuf_free(p);
267 return;
268 }
269
270 pxping_pcb_debug_print(pcb); /* XXX */
271 printf(" seq %d len %u ttl %d\n",
272 ntohs(seq), (unsigned int)p->tot_len,
273 IPH_TTL(iph));
274
275 ttl = IPH_TTL(iph);
276 if (!pcb->is_mapped) {
277 if (ttl == 1) {
278 pbuf_header(p, iphlen); /* back to IP header */
279 icmp_time_exceeded(p, ICMP_TE_TTL);
280 return;
281 }
282 --ttl;
283 }
284
285 /* rewrite ICMP echo header */
286 sum = (u16_t)~icmph->chksum;
287 sum += update16_with_chksum(&icmph->id, pcb->host_id);
288 sum = FOLD_U32T(sum);
289 icmph->chksum = ~sum;
290
291 if (ttl != pxping->ttl) {
292 status = setsockopt(pxping->sock4, IPPROTO_IP, IP_TTL,
293 (char *)&ttl, sizeof(ttl));
294 if (status == 0) {
295 pxping->ttl = ttl;
296 }
297 else {
298 perror("IP_TTL");
299 }
300 }
301
302 tos = IPH_TOS(iph);
303 if (tos != pxping->tos) {
304 status = setsockopt(pxping->sock4, IPPROTO_IP, IP_TOS,
305 (char *)&tos, sizeof(tos));
306 if (status == 0) {
307 pxping->tos = tos;
308 }
309 else {
310 perror("IP_TOS");
311 }
312 }
313
314 proxy_sendto(pxping->sock4, p,
315 &pcb->peer.sin, sizeof(pcb->peer.sin));
316
317 pbuf_free(p);
318}
319
320
321static void
322pxping_pcb_debug_print(struct ping_pcb *pcb)
323{
324 printf("ping %p: %d.%d.%d.%d -> %d.%d.%d.%d id %04x->%04x",
325 (void *)pcb,
326 ip4_addr1(&pcb->src), ip4_addr2(&pcb->src),
327 ip4_addr3(&pcb->src), ip4_addr4(&pcb->src),
328 ip4_addr1(&pcb->dst), ip4_addr2(&pcb->dst),
329 ip4_addr3(&pcb->dst), ip4_addr4(&pcb->dst),
330 ntohs(pcb->guest_id),
331 ntohs(pcb->host_id));
332}
333
334
335static struct ping_pcb *
336pxping_pcb_allocate(struct pxping *pxping)
337{
338 struct ping_pcb *pcb;
339
340 if (pxping->npcbs >= PXPING_MAX_PCBS) {
341 return NULL;
342 }
343
344 pcb = (struct ping_pcb *)malloc(sizeof(*pcb));
345 if (pcb == NULL) {
346 return NULL;
347 }
348
349 ++pxping->npcbs;
350 return pcb;
351}
352
353
354static void
355pxping_pcb_delete(struct pxping *pxping, struct ping_pcb *pcb)
356{
357 LWIP_ASSERT1(pxping->npcbs > 0);
358 LWIP_ASSERT1(pxping->next == NULL);
359 LWIP_ASSERT1(pxping->pprev_timeout == NULL);
360
361 printf("%s: ping %p\n", __func__, (void *)pcb);
362
363 --pxping->npcbs;
364 free(pcb);
365}
366
367
368static void
369pxping_timeout_add(struct pxping *pxping, struct ping_pcb *pcb)
370{
371 struct ping_pcb **chain;
372
373 LWIP_ASSERT1(pcb->pprev_timeout == NULL);
374
375 chain = &pxping->timeout_list[pcb->timeout_slot];
376 if ((pcb->next_timeout = *chain) != NULL) {
377 (*chain)->pprev_timeout = &pcb->next_timeout;
378 }
379 *chain = pcb;
380 pcb->pprev_timeout = chain;
381}
382
383
384static void
385pxping_timeout_del(struct pxping *pxping, struct ping_pcb *pcb)
386{
387 LWIP_UNUSED_ARG(pxping);
388
389 LWIP_ASSERT1(pcb->pprev_timeout != NULL);
390 if (pcb->next_timeout != NULL) {
391 pcb->next_timeout->pprev_timeout = pcb->pprev_timeout;
392 }
393 *pcb->pprev_timeout = pcb->next_timeout;
394 pcb->pprev_timeout = NULL;
395 pcb->next_timeout = NULL;
396}
397
398
399static void
400pxping_pcb_register(struct pxping *pxping, struct ping_pcb *pcb)
401{
402 pcb->next = pxping->pcbs;
403 pxping->pcbs = pcb;
404
405 pxping_timeout_add(pxping, pcb);
406}
407
408
409static void
410pxping_pcb_deregister(struct pxping *pxping, struct ping_pcb *pcb)
411{
412 struct ping_pcb **p;
413
414 for (p = &pxping->pcbs; *p != NULL; p = &(*p)->next) {
415 if (*p == pcb) {
416 *p = pcb->next;
417 break;
418 }
419 }
420
421 pxping_timeout_del(pxping, pcb);
422}
423
424
425static struct ping_pcb *
426pxping_pcb_for_request(struct pxping *pxping,
427 int is_ipv6, ipX_addr_t *src, ipX_addr_t *dst,
428 u16_t guest_id)
429{
430 struct ping_pcb *pcb;
431
432 /* on lwip thread, so no concurrent updates */
433 for (pcb = pxping->pcbs; pcb != NULL; pcb = pcb->next) {
434 if (pcb->guest_id == guest_id
435 && pcb->is_ipv6 == is_ipv6
436 && ipX_addr_cmp(is_ipv6, &pcb->dst, dst)
437 && ipX_addr_cmp(is_ipv6, &pcb->src, src))
438 {
439 break;
440 }
441 }
442
443 if (pcb == NULL) {
444 pcb = pxping_pcb_allocate(pxping);
445 if (pcb == NULL) {
446 return NULL;
447 }
448
449 pcb->is_ipv6 = is_ipv6;
450 ipX_addr_copy(is_ipv6, pcb->src, *src);
451 ipX_addr_copy(is_ipv6, pcb->dst, *dst);
452
453 pcb->guest_id = guest_id;
454#ifdef RT_OS_WINDOWS
455# define random() (rand())
456#endif
457 pcb->host_id = random() & 0xffffUL;
458
459 pcb->pprev_timeout = NULL;
460 pcb->next_timeout = NULL;
461
462 if (is_ipv6) {
463 }
464 else {
465 int mapped;
466
467 pcb->peer.sin.sin_family = AF_INET;
468#if HAVE_SA_LEN
469 pcb->peer.sin.sin_len = sizeof(pcb->peer.sin);
470#endif
471 mapped = pxremap_outbound_ip4((ip_addr_t *)&pcb->peer.sin.sin_addr,
472 ipX_2_ip(&pcb->dst));
473 if (mapped == PXREMAP_FAILED) {
474 free(pcb);
475 return NULL;
476 }
477 else {
478 pcb->is_mapped = (mapped == PXREMAP_MAPPED);
479 }
480 pcb->peer.sin.sin_port = htons(IP_PROTO_ICMP);
481 }
482
483 pcb->timeout_slot = pxping->timeout_slot;
484
485 sys_mutex_lock(&pxping->lock);
486 pxping_pcb_register(pxping, pcb);
487 sys_mutex_unlock(&pxping->lock);
488
489 pxping_pcb_debug_print(pcb); /* XXX */
490 printf(" - created\n");
491
492 pxping_timer_needed(pxping);
493 }
494 else {
495 /* just bump up expiration timeout lazily */
496 pxping_pcb_debug_print(pcb); /* XXX */
497 printf(" - slot %d -> %d\n",
498 (unsigned int)pcb->timeout_slot,
499 (unsigned int)pxping->timeout_slot);
500 pcb->timeout_slot = pxping->timeout_slot;
501 }
502
503 return pcb;
504}
505
506
507/**
508 * Called on pollmgr thread. Caller must do the locking since caller
509 * is going to use the returned pcb, which needs to be protected from
510 * being expired by pxping_timer() on lwip thread.
511 */
512static struct ping_pcb *
513pxping_pcb_for_reply(struct pxping *pxping,
514 int is_ipv6, ipX_addr_t *dst, u16_t host_id)
515{
516 struct ping_pcb *pcb;
517
518 for (pcb = pxping->pcbs; pcb != NULL; pcb = pcb->next) {
519 if (pcb->host_id == host_id
520 && pcb->is_ipv6 == is_ipv6
521 /* XXX: allow broadcast pings? */
522 && ipX_addr_cmp(is_ipv6, &pcb->dst, dst))
523 {
524 return pcb;
525 }
526 }
527
528 return NULL;
529}
530
531
532static void
533pxping_timer(void *arg)
534{
535 struct pxping *pxping = (struct pxping *)arg;
536 struct ping_pcb **chain, *pcb;
537
538 pxping->timer_active = 0;
539
540 /*
541 * New slot points to the list of pcbs to check for expiration.
542 */
543 LWIP_ASSERT1(pxping->timeout_slot < TIMEOUT);
544 if (++pxping->timeout_slot == TIMEOUT) {
545 pxping->timeout_slot = 0;
546 }
547
548 chain = &pxping->timeout_list[pxping->timeout_slot];
549 pcb = *chain;
550
551 /* protect from pollmgr concurrent reads */
552 sys_mutex_lock(&pxping->lock);
553
554 while (pcb != NULL) {
555 struct ping_pcb *xpcb = pcb;
556 pcb = pcb->next_timeout;
557
558 if (xpcb->timeout_slot == pxping->timeout_slot) {
559 /* expired */
560 printf("... ");
561 pxping_pcb_debug_print(xpcb);
562 printf(" - expired\n");
563
564 pxping_pcb_deregister(pxping, xpcb);
565 pxping_pcb_delete(pxping, xpcb);
566 }
567 else {
568 /*
569 * If there was another request, we updated timeout_slot
570 * but delayed actually moving the pcb until now.
571 */
572 printf("... ");
573 pxping_pcb_debug_print(xpcb);
574 printf(" - alive slot %d -> %d\n",
575 (unsigned int)pxping->timeout_slot,
576 (unsigned int)xpcb->timeout_slot);
577
578 pxping_timeout_del(pxping, xpcb); /* from current slot */
579 pxping_timeout_add(pxping, xpcb); /* to new slot */
580 }
581 }
582
583 sys_mutex_unlock(&pxping->lock);
584 pxping_timer_needed(pxping);
585}
586
587
588static void
589pxping_timer_needed(struct pxping *pxping)
590{
591 if (!pxping->timer_active && pxping->pcbs != NULL) {
592 pxping->timer_active = 1;
593 sys_timeout(1 * 1000, pxping_timer, pxping);
594 }
595}
596
597
598static struct ping_msg *
599pxping_msg_allocate(struct pxping *pxping, struct pbuf *p)
600{
601 struct ping_msg *msg;
602
603 msg = (struct ping_msg *)malloc(sizeof(*msg));
604 if (msg == NULL) {
605 return NULL;
606 }
607
608 msg->msg.type = TCPIP_MSG_CALLBACK_STATIC;
609 msg->msg.sem = NULL;
610 msg->msg.msg.cb.function = pxping_pcb_forward_inbound;
611 msg->msg.msg.cb.ctx = (void *)msg;
612
613 msg->pxping = pxping;
614 msg->p = p;
615
616 return msg;
617}
618
619
620static int
621pxping_pmgr_pump(struct pollmgr_handler *handler, SOCKET fd, int revents)
622{
623 struct pxping *pxping;
624 struct sockaddr_storage ss;
625 socklen_t sslen = sizeof(ss);
626 ssize_t nread;
627
628 pxping = (struct pxping *)handler->data;
629
630 if (revents & ~(POLLIN|POLLERR)) {
631 DPRINTF0(("%s: unexpected revents 0x%x\n", __func__, revents));
632 return POLLIN;
633 }
634
635 if (revents & POLLERR) {
636 int sockerr = -1;
637 socklen_t optlen = (socklen_t)sizeof(sockerr);
638 int status;
639
640 status = getsockopt(fd, SOL_SOCKET,
641 SO_ERROR, (char *)&sockerr, &optlen);
642 if (status < 0) {
643 DPRINTF(("%s: sock %d: SO_ERROR failed with errno %d\n",
644 __func__, fd, errno));
645 }
646 else {
647 DPRINTF(("%s: sock %d: errno %d\n",
648 __func__, fd, sockerr));
649 }
650 }
651
652 if ((revents & POLLIN) == 0) {
653 return POLLIN;
654 }
655
656 memset(&ss, 0, sizeof(ss));
657 nread = recvfrom(fd, pollmgr_udpbuf, sizeof(pollmgr_udpbuf), 0,
658 (struct sockaddr *)&ss, &sslen);
659 if (nread < 0) {
660 perror(__func__);
661 return POLLIN;
662 }
663
664 pxping_pmgr_icmp4(pxping, nread, (struct sockaddr_in *)&ss);
665
666 return POLLIN;
667}
668
669
670/**
671 * Process incoming ICMP message for the host.
672 * NB: we will get a lot of spam here and have to sift through it.
673 */
674static void
675pxping_pmgr_icmp4(struct pxping *pxping,
676 size_t nread, struct sockaddr_in *peer)
677{
678 struct ip_hdr *iph;
679 struct icmp_echo_hdr *icmph;
680 u16_t iplen;
681
682 if (nread < IP_HLEN) {
683 DPRINTF2(("%s: read %d bytes, IP header truncated\n",
684 __func__, (unsigned int)nread));
685 return;
686 }
687
688 iph = (struct ip_hdr *)pollmgr_udpbuf;
689
690 /* match version */
691 if (IPH_V(iph) != 4) {
692 DPRINTF2(("%s: unexpected IP version %d\n", __func__, IPH_V(iph)));
693 return;
694 }
695
696 /* no fragmentation */
697 if ((IPH_OFFSET(iph) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) {
698 DPRINTF2(("%s: dropping fragmented datagram\n", __func__));
699 return;
700 }
701
702 /* no options */
703 if (IPH_HL(iph) * 4 != IP_HLEN) {
704 DPRINTF2(("%s: dropping datagram with options (IP header length %d)\n",
705 __func__, IPH_HL(iph) * 4));
706 return;
707 }
708
709 if (IPH_PROTO(iph) != IP_PROTO_ICMP) {
710 DPRINTF2(("%s: unexpected protocol %d\n", __func__, IPH_PROTO(iph)));
711 return;
712 }
713
714 if (IPH_TTL(iph) == 1) {
715 DPRINTF2(("%s: dropping packet with ttl 1\n", __func__));
716 return;
717 }
718
719 iplen = IPH_LEN(iph);
720#if !defined(RT_OS_DARWIN)
721 /* darwin reports IPH_LEN in host byte order */
722 iplen = ntohs(iplen);
723#endif
724#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
725 /* darwin and solaris change IPH_LEN to payload length only */
726 iplen += IP_HLEN; /* we verified there are no options */
727 IPH_LEN(iph) = htons(iplen);
728#endif
729 if (nread < iplen) {
730 DPRINTF2(("%s: read %d bytes but total length is %d bytes\n",
731 __func__, (unsigned int)nread, (unsigned int)iplen));
732 return;
733 }
734
735 if (iplen < IP_HLEN + ICMP_HLEN) {
736 DPRINTF2(("%s: IP length %d bytes, ICMP header truncated\n",
737 __func__, iplen));
738 return;
739 }
740
741 icmph = (struct icmp_echo_hdr *)(pollmgr_udpbuf + IP_HLEN);
742 if (ICMPH_TYPE(icmph) == ICMP_ER) {
743 pxping_pmgr_icmp4_echo(pxping, iplen, peer);
744 }
745 else if (ICMPH_TYPE(icmph) == ICMP_DUR || ICMPH_TYPE(icmph) == ICMP_TE) {
746 pxping_pmgr_icmp4_error(pxping, iplen, peer);
747 }
748#if 1
749 else {
750 DPRINTF2(("%s: ignoring ICMP type %d\n", __func__, ICMPH_TYPE(icmph)));
751 }
752#endif
753}
754
755
756/**
757 * Check if this incoming ICMP echo reply is for one of our pings and
758 * forward it to the guest.
759 */
760static void
761pxping_pmgr_icmp4_echo(struct pxping *pxping,
762 u16_t iplen, struct sockaddr_in *peer)
763{
764 struct ip_hdr *iph;
765 struct icmp_echo_hdr *icmph;
766 u16_t id, seq;
767 int mapped;
768 struct ping_pcb *pcb;
769 ip_addr_t guest_ip, target_ip, unmapped_target_ip;
770 u16_t guest_id;
771 u32_t sum;
772
773 iph = (struct ip_hdr *)pollmgr_udpbuf;
774 icmph = (struct icmp_echo_hdr *)(pollmgr_udpbuf + IP_HLEN);
775
776 id = icmph->id;
777 seq = icmph->seqno;
778
779 {
780 char addrbuf[sizeof "255.255.255.255"];
781 const char *addrstr;
782
783 addrstr = inet_ntop(AF_INET, &peer->sin_addr, addrbuf, sizeof(addrbuf));
784 DPRINTF(("<--- PING %s id 0x%x seq %d\n",
785 addrstr, ntohs(id), ntohs(seq)));
786 }
787
788 ip_addr_copy(target_ip, iph->src);
789 mapped = pxremap_inbound_ip4(&unmapped_target_ip, &target_ip);
790 if (mapped == PXREMAP_FAILED) {
791 return;
792 }
793
794 sys_mutex_lock(&pxping->lock);
795 pcb = pxping_pcb_for_reply(pxping, 0, ip_2_ipX(&unmapped_target_ip), id);
796 if (pcb == NULL) {
797 sys_mutex_unlock(&pxping->lock);
798 DPRINTF2(("%s: no match\n", __func__));
799 return;
800 }
801
802 DPRINTF2(("%s: pcb %p\n", __func__, (void *)pcb));
803
804 /* save info before unlocking since pcb may expire */
805 ip_addr_copy(guest_ip, *ipX_2_ip(&pcb->src));
806 guest_id = pcb->guest_id;
807
808 sys_mutex_unlock(&pxping->lock);
809
810 /* rewrite inner ICMP echo header */
811 sum = (u16_t)~icmph->chksum;
812 sum += update16_with_chksum(&icmph->id, guest_id);
813 sum = FOLD_U32T(sum);
814 icmph->chksum = ~sum;
815
816 /* rewrite outer IP header */
817 sum = (u16_t)~IPH_CHKSUM(iph);
818 sum += update32_with_chksum((u32_t *)&iph->dest,
819 ip4_addr_get_u32(&guest_ip));
820 if (mapped == PXREMAP_MAPPED) {
821 sum += update32_with_chksum((u32_t *)&iph->src,
822 ip4_addr_get_u32(&unmapped_target_ip));
823 }
824 else {
825 IPH_TTL_SET(iph, IPH_TTL(iph) - 1);
826 sum += PP_NTOHS(~0x0100);
827 }
828 sum = FOLD_U32T(sum);
829 IPH_CHKSUM_SET(iph, ~sum);
830
831 pxping_pmgr_forward_inbound(pxping, iplen);
832}
833
834
835/**
836 * Check if this incoming ICMP error (destination unreachable or time
837 * exceeded) is about one of our pings and forward it to the guest.
838 */
839static void
840pxping_pmgr_icmp4_error(struct pxping *pxping,
841 u16_t iplen, struct sockaddr_in *peer)
842{
843 struct ip_hdr *iph, *oiph;
844 struct icmp_echo_hdr *icmph, *oicmph;
845 u16_t oipoff, oiphlen, oiplen;
846 u16_t id, seq;
847 struct ping_pcb *pcb;
848 ip_addr_t pcb_src, pcb_dst;
849 u16_t guest_id;
850 u32_t sum;
851
852 iph = (struct ip_hdr *)pollmgr_udpbuf;
853 icmph = (struct icmp_echo_hdr *)(pollmgr_udpbuf + IP_HLEN);
854
855 oipoff = IP_HLEN + ICMP_HLEN;
856 oiplen = iplen - oipoff; /* NB: truncated length, not IPH_LEN(oiph) */
857 if (oiplen < IP_HLEN) {
858 DPRINTF2(("%s: original datagram truncated to %d bytes\n",
859 __func__, oiplen));
860 }
861
862 /* IP header of the original message */
863 oiph = (struct ip_hdr *)(pollmgr_udpbuf + oipoff);
864
865 /* match version */
866 if (IPH_V(oiph) != 4) {
867 DPRINTF2(("%s: unexpected IP version %d\n", __func__, IPH_V(oiph)));
868 return;
869 }
870
871 /* can't match fragments except the first one */
872 if ((IPH_OFFSET(oiph) & PP_HTONS(IP_OFFMASK)) != 0) {
873 DPRINTF2(("%s: ignoring fragment with offset %d\n",
874 __func__, ntohs(IPH_OFFSET(oiph) & PP_HTONS(IP_OFFMASK))));
875 return;
876 }
877
878 if (IPH_PROTO(oiph) != IP_PROTO_ICMP) {
879#if 0
880 /* don't spam with every "destination unreachable" in the system */
881 DPRINTF2(("%s: ignoring protocol %d\n", __func__, IPH_PROTO(oiph)));
882#endif
883 return;
884 }
885
886 oiphlen = IPH_HL(oiph) * 4;
887 if (oiplen < oiphlen + ICMP_HLEN) {
888 DPRINTF2(("%s: original datagram truncated to %d bytes\n",
889 __func__, oiplen));
890 return;
891 }
892
893 oicmph = (struct icmp_echo_hdr *)(pollmgr_udpbuf + oipoff + oiphlen);
894 if (ICMPH_TYPE(oicmph) != ICMP_ECHO) {
895 DPRINTF2(("%s: ignoring ICMP error for original ICMP type %d\n",
896 __func__, ICMPH_TYPE(oicmph)));
897 return;
898 }
899
900 id = oicmph->id;
901 seq = oicmph->seqno;
902
903 {
904 char addrbuf[sizeof "255.255.255.255"];
905 const char *addrstr;
906
907 addrstr = inet_ntop(AF_INET, &oiph->dest, addrbuf, sizeof(addrbuf));
908 DPRINTF2(("%s: ping %s id 0x%x seq %d",
909 __func__, addrstr, ntohs(id), ntohs(seq)));
910 }
911
912 if (ICMPH_TYPE(icmph) == ICMP_DUR) {
913 DPRINTF2((" unreachable (code %d)\n", ICMPH_CODE(icmph)));
914 }
915 else {
916 DPRINTF2((" time exceeded\n"));
917 }
918
919 sys_mutex_lock(&pxping->lock);
920 pcb = pxping_pcb_for_reply(pxping, 0, ip_2_ipX(&oiph->dest), id);
921 if (pcb == NULL) {
922 sys_mutex_unlock(&pxping->lock);
923 DPRINTF2(("%s: no match\n", __func__));
924 return;
925 }
926
927 DPRINTF2(("%s: pcb %p\n", __func__, (void *)pcb));
928
929 /* save info before unlocking since pcb may expire */
930 ip_addr_copy(pcb_src, *ipX_2_ip(&pcb->src));
931 ip_addr_copy(pcb_dst, *ipX_2_ip(&pcb->dst));
932 guest_id = pcb->guest_id;
933
934 sys_mutex_unlock(&pxping->lock);
935
936 /*
937 * NB: Checksum in the outer ICMP error header is not affected by
938 * changes to inner headers.
939 */
940
941 /* rewrite inner ICMP echo header */
942 sum = (u16_t)~oicmph->chksum;
943 sum += update16_with_chksum(&oicmph->id, guest_id);
944 sum = FOLD_U32T(sum);
945 oicmph->chksum = ~sum;
946
947 /* rewrite inner IP header */
948 sum = (u16_t)~IPH_CHKSUM(oiph);
949 sum += update32_with_chksum((u32_t *)&oiph->src, ip4_addr_get_u32(&pcb_src));
950 sum = FOLD_U32T(sum);
951 IPH_CHKSUM_SET(oiph, ~sum);
952
953 /* rewrite outer IP header */
954 sum = (u16_t)~IPH_CHKSUM(iph);
955 sum += update32_with_chksum((u32_t *)&iph->dest, ip4_addr_get_u32(&pcb_src));
956 IPH_TTL_SET(iph, IPH_TTL(iph) - 1);
957 sum += PP_NTOHS(~0x0100);
958 sum = FOLD_U32T(sum);
959 IPH_CHKSUM_SET(iph, ~sum);
960
961 pxping_pmgr_forward_inbound(pxping, iplen);
962}
963
964
965/**
966 * Hand off ICMP datagram to the lwip thread where it will be
967 * forwarded to the guest.
968 *
969 * We no longer need ping_pcb. The pcb may get expired on the lwip
970 * thread, but we have already patched necessary information into the
971 * datagram.
972 */
973static void
974pxping_pmgr_forward_inbound(struct pxping *pxping, u16_t iplen)
975{
976 struct pbuf *p;
977 struct ping_msg *msg;
978 err_t error;
979
980 p = pbuf_alloc(PBUF_LINK, iplen, PBUF_RAM);
981 if (p == NULL) {
982 DPRINTF(("%s: pbuf_alloc(%d) failed\n",
983 __func__, (unsigned int)iplen));
984 return;
985 }
986
987 error = pbuf_take(p, pollmgr_udpbuf, (u16_t)iplen);
988 if (error != ERR_OK) {
989 DPRINTF(("%s: pbuf_take(%d) failed\n",
990 __func__, (unsigned int)iplen));
991 pbuf_free(p);
992 return;
993 }
994
995 msg = pxping_msg_allocate(pxping, p);
996 if (msg == NULL) {
997 pbuf_free(p);
998 return;
999 }
1000
1001 /* call pxping_pcb_forward_inbound() on lwip thread */
1002 proxy_lwip_post(&msg->msg);
1003}
1004
1005
1006static void
1007pxping_pcb_forward_inbound(void *arg)
1008{
1009 struct ping_msg *msg = (struct ping_msg *)arg;
1010 err_t error;
1011
1012 LWIP_ASSERT1(msg != NULL);
1013 LWIP_ASSERT1(msg->pxping != NULL);
1014 LWIP_ASSERT1(msg->p != NULL);
1015
1016 error = ip_raw_output_if(msg->p, msg->pxping->netif);
1017 if (error != ERR_OK) {
1018 DPRINTF(("%s: ip_output_if: %s\n",
1019 __func__, proxy_lwip_strerr(error)));
1020 }
1021
1022 free(msg);
1023}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette