VirtualBox

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

Last change on this file since 53624 was 53624, checked in by vboxsync, 10 years ago

scm automatic cleanups.

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