VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/NAT/pxping_win.c@ 53588

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

NET/Nat: pxping_recv6 - parrot pxping_recv4 change r96987.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.9 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 "pxremap.h"
7
8#include "lwip/ip.h"
9#include "lwip/icmp.h"
10#include "lwip/inet_chksum.h"
11
12/* XXX: lwIP names conflict with winsock <iphlpapi.h> */
13#undef IP_STATS
14#undef ICMP_STATS
15#undef TCP_STATS
16#undef UDP_STATS
17#undef IP6_STATS
18
19#include <winternl.h> /* for PIO_APC_ROUTINE &c */
20#include <iphlpapi.h>
21#include <icmpapi.h>
22
23#include <stdio.h>
24
25
26struct pxping {
27 /*
28 * We use single ICMP handle for all pings. This means that all
29 * proxied pings will have the same id and share single sequence
30 * of sequence numbers.
31 */
32 HANDLE hdl4;
33 HANDLE hdl6;
34
35 struct netif *netif;
36
37 /*
38 * On Windows XP and Windows Server 2003 IcmpSendEcho2() callback
39 * is FARPROC, but starting from Vista it's PIO_APC_ROUTINE with
40 * two extra arguments. Callbacks use WINAPI (stdcall) calling
41 * convention with callee responsible for popping the arguments,
42 * so to avoid stack corruption we check windows version at run
43 * time and provide correct callback.
44 */
45 void *callback4;
46 void *callback6;
47};
48
49
50struct pong4 {
51 struct netif *netif;
52
53 struct ip_hdr reqiph;
54 struct icmp_echo_hdr reqicmph;
55
56 size_t bufsize;
57 u8_t buf[1];
58};
59
60
61struct pong6 {
62 struct netif *netif;
63
64 ip6_addr_t reqsrc;
65 struct icmp6_echo_hdr reqicmph;
66 size_t reqsize;
67
68 size_t bufsize;
69 u8_t buf[1];
70};
71
72
73static void pxping_recv4(void *arg, struct pbuf *p);
74static void pxping_recv6(void *arg, struct pbuf *p);
75
76static VOID WINAPI pxping_icmp4_callback_old(void *);
77static VOID WINAPI pxping_icmp4_callback_apc(void *, PIO_STATUS_BLOCK, ULONG);
78static void pxping_icmp4_callback(struct pong4 *pong);
79
80static VOID WINAPI pxping_icmp6_callback_old(void *);
81static VOID WINAPI pxping_icmp6_callback_apc(void *, PIO_STATUS_BLOCK, ULONG);
82static void pxping_icmp6_callback(struct pong6 *pong);
83
84
85struct pxping g_pxping;
86
87
88err_t
89pxping_init(struct netif *netif, SOCKET sock4, SOCKET sock6)
90{
91 OSVERSIONINFO osvi;
92 int status;
93
94 LWIP_UNUSED_ARG(sock4);
95 LWIP_UNUSED_ARG(sock6);
96
97 ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
98 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
99 status = GetVersionEx(&osvi);
100 if (status == 0) {
101 return ERR_ARG;
102 }
103
104 if (osvi.dwMajorVersion >= 6) {
105 g_pxping.callback4 = (void *)pxping_icmp4_callback_apc;
106 g_pxping.callback6 = (void *)pxping_icmp6_callback_apc;
107 }
108 else {
109 g_pxping.callback4 = (void *)pxping_icmp4_callback_old;
110 g_pxping.callback6 = (void *)pxping_icmp6_callback_old;
111 }
112
113
114 g_pxping.hdl4 = IcmpCreateFile();
115 if (g_pxping.hdl4 != INVALID_HANDLE_VALUE) {
116 ping_proxy_accept(pxping_recv4, &g_pxping);
117 }
118 else {
119 DPRINTF(("IcmpCreateFile: error %d\n", GetLastError()));
120 }
121
122 g_pxping.hdl6 = Icmp6CreateFile();
123 if (g_pxping.hdl6 != INVALID_HANDLE_VALUE) {
124 ping6_proxy_accept(pxping_recv6, &g_pxping);
125 }
126 else {
127 DPRINTF(("Icmp6CreateFile: error %d\n", GetLastError()));
128 }
129
130 if (g_pxping.hdl4 == INVALID_HANDLE_VALUE
131 && g_pxping.hdl6 == INVALID_HANDLE_VALUE)
132 {
133 return ERR_ARG;
134 }
135
136 g_pxping.netif = netif;
137
138 return ERR_OK;
139}
140
141
142/**
143 * ICMP Echo Request in pbuf "p" is to be proxied.
144 */
145static void
146pxping_recv4(void *arg, struct pbuf *p)
147{
148 struct pxping *pxping = (struct pxping *)arg;
149 const struct ip_hdr *iph;
150 const struct icmp_echo_hdr *icmph;
151 u16_t iphlen;
152 size_t bufsize;
153 struct pong4 *pong;
154 IPAddr dst;
155 int mapped;
156 int ttl;
157 IP_OPTION_INFORMATION opts;
158 void *reqdata;
159 size_t reqsize;
160 int status;
161
162 pong = NULL;
163
164 iphlen = ip_current_header_tot_len();
165 if (RT_UNLIKELY(iphlen != IP_HLEN)) { /* we don't do options */
166 goto out;
167 }
168
169 iph = (const struct ip_hdr *)ip_current_header();
170 icmph = (const struct icmp_echo_hdr *)p->payload;
171
172 mapped = pxremap_outbound_ip4((ip_addr_t *)&dst, (ip_addr_t *)&iph->dest);
173 if (RT_UNLIKELY(mapped == PXREMAP_FAILED)) {
174 goto out;
175 }
176
177 ttl = IPH_TTL(iph);
178 if (mapped == PXREMAP_ASIS) {
179 if (RT_UNLIKELY(ttl == 1)) {
180 status = pbuf_header(p, iphlen); /* back to IP header */
181 if (RT_LIKELY(status == 0)) {
182 icmp_time_exceeded(p, ICMP_TE_TTL);
183 }
184 goto out;
185 }
186 --ttl;
187 }
188
189 status = pbuf_header(p, -(u16_t)sizeof(*icmph)); /* to ping payload */
190 if (RT_UNLIKELY(status != 0)) {
191 goto out;
192 }
193
194 bufsize = sizeof(ICMP_ECHO_REPLY);
195 if (p->tot_len < sizeof(IO_STATUS_BLOCK) + sizeof(struct icmp_echo_hdr))
196 bufsize += sizeof(IO_STATUS_BLOCK) + sizeof(struct icmp_echo_hdr);
197 else
198 bufsize += p->tot_len;
199 bufsize += 16; /* whatever that is; empirically at least XP needs it */
200
201 pong = (struct pong4 *)malloc(RT_OFFSETOF(struct pong4, buf) + bufsize);
202 if (RT_UNLIKELY(pong == NULL)) {
203 goto out;
204 }
205 pong->bufsize = bufsize;
206 pong->netif = pxping->netif;
207
208 memcpy(&pong->reqiph, iph, sizeof(*iph));
209 memcpy(&pong->reqicmph, icmph, sizeof(*icmph));
210
211 reqsize = p->tot_len;
212 if (p->next == NULL) {
213 /* single pbuf can be directly used as request data source */
214 reqdata = p->payload;
215 }
216 else {
217 /* data from pbuf chain must be concatenated */
218 pbuf_copy_partial(p, pong->buf, p->tot_len, 0);
219 reqdata = pong->buf;
220 }
221
222 opts.Ttl = ttl;
223 opts.Tos = IPH_TOS(iph); /* affected by DisableUserTOSSetting key */
224 opts.Flags = (IPH_OFFSET(iph) & PP_HTONS(IP_DF)) != 0 ? IP_FLAG_DF : 0;
225 opts.OptionsSize = 0;
226 opts.OptionsData = 0;
227
228 status = IcmpSendEcho2(pxping->hdl4, NULL,
229 pxping->callback4, pong,
230 dst, reqdata, (WORD)reqsize, &opts,
231 pong->buf, (DWORD)pong->bufsize,
232 5 * 1000 /* ms */);
233
234 if (RT_UNLIKELY(status != 0)) {
235 DPRINTF(("IcmpSendEcho2: unexpected status %d\n", status));
236 goto out;
237 }
238 else if ((status = GetLastError()) != ERROR_IO_PENDING) {
239 int code;
240
241 DPRINTF(("IcmpSendEcho2: error %d\n", status));
242 switch (status) {
243 case ERROR_NETWORK_UNREACHABLE:
244 code = ICMP_DUR_NET;
245 break;
246 case ERROR_HOST_UNREACHABLE:
247 code = ICMP_DUR_HOST;
248 break;
249 default:
250 code = -1;
251 break;
252 }
253
254 if (code != -1) {
255 /* move payload back to IP header */
256 status = pbuf_header(p, (u16_t)(sizeof(*icmph) + iphlen));
257 if (RT_LIKELY(status == 0)) {
258 icmp_dest_unreach(p, code);
259 }
260 }
261 goto out;
262 }
263
264 pong = NULL; /* callback owns it now */
265 out:
266 if (pong != NULL) {
267 free(pong);
268 }
269 pbuf_free(p);
270}
271
272
273static VOID WINAPI
274pxping_icmp4_callback_apc(void *ctx, PIO_STATUS_BLOCK iob, ULONG reserved)
275{
276 struct pong4 *pong = (struct pong4 *)ctx;
277 LWIP_UNUSED_ARG(iob);
278 LWIP_UNUSED_ARG(reserved);
279
280 if (pong != NULL) {
281 pxping_icmp4_callback(pong);
282 free(pong);
283 }
284}
285
286
287static VOID WINAPI
288pxping_icmp4_callback_old(void *ctx)
289{
290 struct pong4 *pong = (struct pong4 *)ctx;
291
292 if (pong != NULL) {
293 pxping_icmp4_callback(pong);
294 free(pong);
295 }
296}
297
298
299static void
300pxping_icmp4_callback(struct pong4 *pong)
301{
302 ICMP_ECHO_REPLY *reply;
303 DWORD nreplies;
304 size_t icmplen;
305 struct pbuf *p;
306 struct icmp_echo_hdr *icmph;
307 ip_addr_t src;
308 int mapped;
309
310 nreplies = IcmpParseReplies(pong->buf, (DWORD)pong->bufsize);
311 if (nreplies == 0) {
312 DWORD error = GetLastError();
313 if (error == IP_REQ_TIMED_OUT) {
314 DPRINTF2(("pong4: %p timed out\n", (void *)pong));
315 }
316 else {
317 DPRINTF(("pong4: %p: IcmpParseReplies: error %d\n",
318 (void *)pong, error));
319 }
320 return;
321 }
322
323 reply = (ICMP_ECHO_REPLY *)pong->buf;
324
325 if (reply->Options.OptionsSize != 0) { /* don't do options */
326 return;
327 }
328
329 mapped = pxremap_inbound_ip4(&src, (ip_addr_t *)&reply->Address);
330 if (mapped == PXREMAP_FAILED) {
331 return;
332 }
333 if (mapped == PXREMAP_ASIS) {
334 if (reply->Options.Ttl == 1) {
335 return;
336 }
337 --reply->Options.Ttl;
338 }
339
340 if (reply->Status == IP_SUCCESS) {
341 icmplen = sizeof(struct icmp_echo_hdr) + reply->DataSize;
342 if ((reply->Options.Flags & IP_FLAG_DF) != 0
343 && IP_HLEN + icmplen > pong->netif->mtu)
344 {
345 return;
346 }
347
348 p = pbuf_alloc(PBUF_IP, (u16_t)icmplen, PBUF_RAM);
349 if (RT_UNLIKELY(p == NULL)) {
350 return;
351 }
352
353 icmph = (struct icmp_echo_hdr *)p->payload;
354 icmph->type = ICMP_ER;
355 icmph->code = 0;
356 icmph->chksum = 0;
357 icmph->id = pong->reqicmph.id;
358 icmph->seqno = pong->reqicmph.seqno;
359
360 memcpy((u8_t *)p->payload + sizeof(*icmph),
361 reply->Data, reply->DataSize);
362 }
363 else {
364 u8_t type, code;
365
366 switch (reply->Status) {
367 case IP_DEST_NET_UNREACHABLE:
368 type = ICMP_DUR; code = ICMP_DUR_NET;
369 break;
370 case IP_DEST_HOST_UNREACHABLE:
371 type = ICMP_DUR; code = ICMP_DUR_HOST;
372 break;
373 case IP_DEST_PROT_UNREACHABLE:
374 type = ICMP_DUR; code = ICMP_DUR_PROTO;
375 break;
376 case IP_PACKET_TOO_BIG:
377 type = ICMP_DUR; code = ICMP_DUR_FRAG;
378 break;
379 case IP_SOURCE_QUENCH:
380 type = ICMP_SQ; code = 0;
381 break;
382 case IP_TTL_EXPIRED_TRANSIT:
383 type = ICMP_TE; code = ICMP_TE_TTL;
384 break;
385 case IP_TTL_EXPIRED_REASSEM:
386 type = ICMP_TE; code = ICMP_TE_FRAG;
387 break;
388 default:
389 DPRINTF(("pong4: reply status %d, dropped\n", reply->Status));
390 return;
391 }
392
393 DPRINTF(("pong4: reply status %d -> type %d/code %d\n",
394 reply->Status, type, code));
395
396 icmplen = sizeof(*icmph) + sizeof(pong->reqiph) + sizeof(pong->reqicmph);
397
398 p = pbuf_alloc(PBUF_IP, (u16_t)icmplen, PBUF_RAM);
399 if (RT_UNLIKELY(p == NULL)) {
400 return;
401 }
402
403 icmph = (struct icmp_echo_hdr *)p->payload;
404 icmph->type = type;
405 icmph->code = code;
406 icmph->chksum = 0;
407 icmph->id = 0;
408 icmph->seqno = 0;
409
410 /*
411 * XXX: we don't know the TTL of the request at the time this
412 * ICMP error was generated (we can guess it was 1 for ttl
413 * exceeded, but don't bother faking it).
414 */
415 memcpy((u8_t *)p->payload + sizeof(*icmph),
416 &pong->reqiph, sizeof(pong->reqiph));
417
418 memcpy((u8_t *)p->payload + sizeof(*icmph) + sizeof(pong->reqiph),
419 &pong->reqicmph, sizeof(pong->reqicmph));
420 }
421
422 icmph->chksum = inet_chksum(p->payload, (u16_t)icmplen);
423 ip_output_if(p, &src,
424 (ip_addr_t *)&pong->reqiph.src, /* dst */
425 reply->Options.Ttl,
426 reply->Options.Tos,
427 IPPROTO_ICMP,
428 pong->netif);
429 pbuf_free(p);
430}
431
432
433static void
434pxping_recv6(void *arg, struct pbuf *p)
435{
436 struct pxping *pxping = (struct pxping *)arg;
437 struct icmp6_echo_hdr *icmph;
438 size_t bufsize;
439 struct pong6 *pong;
440 int mapped;
441 void *reqdata;
442 size_t reqsize;
443 struct sockaddr_in6 src, dst;
444 int hopl;
445 IP_OPTION_INFORMATION opts;
446 int status;
447
448 pong = NULL;
449
450 icmph = (struct icmp6_echo_hdr *)p->payload;
451
452 memset(&dst, 0, sizeof(dst));
453 dst.sin6_family = AF_INET6;
454 mapped = pxremap_outbound_ip6((ip6_addr_t *)&dst.sin6_addr,
455 ip6_current_dest_addr());
456 if (RT_UNLIKELY(mapped == PXREMAP_FAILED)) {
457 goto out;
458 }
459
460 hopl = IP6H_HOPLIM(ip6_current_header());
461 if (mapped == PXREMAP_ASIS) {
462 if (RT_UNLIKELY(hopl == 1)) {
463 status = pbuf_header(p, ip_current_header_tot_len());
464 if (RT_LIKELY(status == 0)) {
465 icmp6_time_exceeded(p, ICMP6_TE_HL);
466 }
467 goto out;
468 }
469 --hopl;
470 }
471
472 status = pbuf_header(p, -(u16_t)sizeof(*icmph)); /* to ping payload */
473 if (RT_UNLIKELY(status != 0)) {
474 goto out;
475 }
476
477 /* XXX: parrotted from IPv4 version, not tested all os version/bitness */
478 bufsize = sizeof(ICMPV6_ECHO_REPLY);
479 if (p->tot_len < sizeof(IO_STATUS_BLOCK) + sizeof(struct icmp6_echo_hdr))
480 bufsize += sizeof(IO_STATUS_BLOCK) + sizeof(struct icmp6_echo_hdr);
481 else
482 bufsize += p->tot_len;
483 bufsize += 16;
484
485 pong = (struct pong6 *)malloc(RT_OFFSETOF(struct pong6, buf) + bufsize);
486 if (RT_UNLIKELY(pong == NULL)) {
487 goto out;
488 }
489 pong->bufsize = bufsize;
490 pong->netif = pxping->netif;
491
492 ip6_addr_copy(pong->reqsrc, *ip6_current_src_addr());
493 memcpy(&pong->reqicmph, icmph, sizeof(*icmph));
494
495 memset(pong->buf, 0xa5, pong->bufsize);
496
497 pong->reqsize = reqsize = p->tot_len;
498 if (p->next == NULL) {
499 /* single pbuf can be directly used as request data source */
500 reqdata = p->payload;
501 }
502 else {
503 /* data from pbuf chain must be concatenated */
504 pbuf_copy_partial(p, pong->buf, p->tot_len, 0);
505 reqdata = pong->buf;
506 }
507
508 memset(&src, 0, sizeof(src));
509 src.sin6_family = AF_INET6;
510 src.sin6_addr = in6addr_any; /* let the OS select host source address */
511
512 memset(&opts, 0, sizeof(opts));
513 opts.Ttl = hopl;
514
515 status = Icmp6SendEcho2(pxping->hdl6, NULL,
516 pxping->callback6, pong,
517 &src, &dst, reqdata, (WORD)reqsize, &opts,
518 pong->buf, (DWORD)pong->bufsize,
519 5 * 1000 /* ms */);
520
521 if (RT_UNLIKELY(status != 0)) {
522 DPRINTF(("Icmp6SendEcho2: unexpected status %d\n", status));
523 goto out;
524 }
525 else if ((status = GetLastError()) != ERROR_IO_PENDING) {
526 int code;
527
528 DPRINTF(("Icmp6SendEcho2: error %d\n", status));
529 switch (status) {
530 case ERROR_NETWORK_UNREACHABLE:
531 case ERROR_HOST_UNREACHABLE:
532 code = ICMP6_DUR_NO_ROUTE;
533 break;
534 default:
535 code = -1;
536 break;
537 }
538
539 if (code != -1) {
540 /* move payload back to IP header */
541 status = pbuf_header(p, (u16_t)(sizeof(*icmph)
542 + ip_current_header_tot_len()));
543 if (RT_LIKELY(status == 0)) {
544 icmp6_dest_unreach(p, code);
545 }
546 }
547 goto out;
548 }
549
550 pong = NULL; /* callback owns it now */
551 out:
552 if (pong != NULL) {
553 free(pong);
554 }
555 pbuf_free(p);
556}
557
558
559static VOID WINAPI
560pxping_icmp6_callback_apc(void *ctx, PIO_STATUS_BLOCK iob, ULONG reserved)
561{
562 struct pong6 *pong = (struct pong6 *)ctx;
563 LWIP_UNUSED_ARG(iob);
564 LWIP_UNUSED_ARG(reserved);
565
566 if (pong != NULL) {
567 pxping_icmp6_callback(pong);
568 free(pong);
569 }
570}
571
572
573static VOID WINAPI
574pxping_icmp6_callback_old(void *ctx)
575{
576 struct pong6 *pong = (struct pong6 *)ctx;
577
578 if (pong != NULL) {
579 pxping_icmp6_callback(pong);
580 free(pong);
581 }
582}
583
584
585static void
586pxping_icmp6_callback(struct pong6 *pong)
587{
588 DWORD nreplies;
589 ICMPV6_ECHO_REPLY *reply;
590 struct pbuf *p;
591 struct icmp6_echo_hdr *icmph;
592 size_t icmplen;
593 ip6_addr_t src;
594 int mapped;
595
596 nreplies = Icmp6ParseReplies(pong->buf, (DWORD)pong->bufsize);
597 if (nreplies == 0) {
598 DWORD error = GetLastError();
599 if (error == IP_REQ_TIMED_OUT) {
600 DPRINTF2(("pong6: %p timed out\n", (void *)pong));
601 }
602 else {
603 DPRINTF(("pong6: %p: Icmp6ParseReplies: error %d\n",
604 (void *)pong, error));
605 }
606 return;
607 }
608
609 reply = (ICMPV6_ECHO_REPLY *)pong->buf;
610
611 mapped = pxremap_inbound_ip6(&src, (ip6_addr_t *)reply->Address.sin6_addr);
612 if (mapped == PXREMAP_FAILED) {
613 return;
614 }
615
616 /*
617 * Reply data follows ICMPV6_ECHO_REPLY structure in memory, but
618 * it doesn't tell us its size. Assume it's equal the size of the
619 * request.
620 */
621 icmplen = sizeof(*icmph) + pong->reqsize;
622 p = pbuf_alloc(PBUF_IP, (u16_t)icmplen, PBUF_RAM);
623 if (RT_UNLIKELY(p == NULL)) {
624 return;
625 }
626
627 icmph = (struct icmp6_echo_hdr *)p->payload;
628 icmph->type = ICMP6_TYPE_EREP;
629 icmph->code = 0;
630 icmph->chksum = 0;
631 icmph->id = pong->reqicmph.id;
632 icmph->seqno = pong->reqicmph.seqno;
633
634 memcpy((u8_t *)p->payload + sizeof(*icmph),
635 pong->buf + sizeof(*reply), pong->reqsize);
636
637 icmph->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len,
638 &src, &pong->reqsrc);
639 ip6_output_if(p, /* :src */ &src, /* :dst */ &pong->reqsrc,
640 LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6,
641 pong->netif);
642 pbuf_free(p);
643}
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