VirtualBox

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

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

NAT/Net: Careful with that DWORD... It's unsigned, so assigning
negative values to it is a bad idea, especially when a coercion to a
wider signed type is about to happen. While here, don't test that
DWORD values are <= 0, just test for zero instead.

In particulat this fixes returning socket errors from
pxtcp_sock_recv() and pxtcp_sock_send() wrappers on Windows.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.3 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) + p->tot_len;
195 pong = (struct pong4 *)malloc(sizeof(*pong) - sizeof(pong->buf) + bufsize);
196 if (RT_UNLIKELY(pong == NULL)) {
197 goto out;
198 }
199 pong->bufsize = bufsize;
200 pong->netif = pxping->netif;
201
202 memcpy(&pong->reqiph, iph, sizeof(*iph));
203 memcpy(&pong->reqicmph, icmph, sizeof(*icmph));
204
205 reqsize = p->tot_len;
206 if (p->next == NULL) {
207 /* single pbuf can be directly used as request data source */
208 reqdata = p->payload;
209 }
210 else {
211 /* data from pbuf chain must be concatenated */
212 pbuf_copy_partial(p, pong->buf, p->tot_len, 0);
213 reqdata = pong->buf;
214 }
215
216 opts.Ttl = ttl;
217 opts.Tos = IPH_TOS(iph); /* affected by DisableUserTOSSetting key */
218 opts.Flags = (IPH_OFFSET(iph) & PP_HTONS(IP_DF)) != 0 ? IP_FLAG_DF : 0;
219 opts.OptionsSize = 0;
220 opts.OptionsData = 0;
221
222 status = IcmpSendEcho2(pxping->hdl4, NULL,
223 pxping->callback4, pong,
224 dst, reqdata, (WORD)reqsize, &opts,
225 pong->buf, (DWORD)pong->bufsize,
226 5 * 1000 /* ms */);
227
228 if (RT_UNLIKELY(status != 0)) {
229 DPRINTF(("IcmpSendEcho2: unexpected status %d\n", status));
230 goto out;
231 }
232 else if ((status = GetLastError()) != ERROR_IO_PENDING) {
233 int code;
234
235 DPRINTF(("IcmpSendEcho2: error %d\n", status));
236 switch (status) {
237 case ERROR_NETWORK_UNREACHABLE:
238 code = ICMP_DUR_NET;
239 break;
240 case ERROR_HOST_UNREACHABLE:
241 code = ICMP_DUR_HOST;
242 break;
243 default:
244 code = -1;
245 break;
246 }
247
248 if (code != -1) {
249 /* move payload back to IP header */
250 status = pbuf_header(p, (u16_t)(sizeof(*icmph) + iphlen));
251 if (RT_LIKELY(status == 0)) {
252 icmp_dest_unreach(p, code);
253 }
254 }
255 goto out;
256 }
257
258 pong = NULL; /* callback owns it now */
259 out:
260 if (pong != NULL) {
261 free(pong);
262 }
263 pbuf_free(p);
264}
265
266
267static VOID WINAPI
268pxping_icmp4_callback_apc(void *ctx, PIO_STATUS_BLOCK iob, ULONG reserved)
269{
270 struct pong4 *pong = (struct pong4 *)ctx;
271 LWIP_UNUSED_ARG(iob);
272 LWIP_UNUSED_ARG(reserved);
273
274 if (pong != NULL) {
275 pxping_icmp4_callback(pong);
276 free(pong);
277 }
278}
279
280
281static VOID WINAPI
282pxping_icmp4_callback_old(void *ctx)
283{
284 struct pong4 *pong = (struct pong4 *)ctx;
285
286 if (pong != NULL) {
287 pxping_icmp4_callback(pong);
288 free(pong);
289 }
290}
291
292
293static void
294pxping_icmp4_callback(struct pong4 *pong)
295{
296 ICMP_ECHO_REPLY *reply;
297 DWORD nreplies;
298 size_t icmplen;
299 struct pbuf *p;
300 struct icmp_echo_hdr *icmph;
301 ip_addr_t src;
302 int mapped;
303
304 nreplies = IcmpParseReplies(pong->buf, (DWORD)pong->bufsize);
305 if (nreplies == 0) {
306 DWORD error = GetLastError();
307 if (error == IP_REQ_TIMED_OUT) {
308 DPRINTF2(("pong4: %p timed out\n", (void *)pong));
309 }
310 else {
311 DPRINTF(("pong4: %p: IcmpParseReplies: error %d\n",
312 (void *)pong, error));
313 }
314 return;
315 }
316
317 reply = (ICMP_ECHO_REPLY *)pong->buf;
318
319 if (reply->Options.OptionsSize != 0) { /* don't do options */
320 return;
321 }
322
323 mapped = pxremap_inbound_ip4(&src, (ip_addr_t *)&reply->Address);
324 if (mapped == PXREMAP_FAILED) {
325 return;
326 }
327 if (mapped == PXREMAP_ASIS) {
328 if (reply->Options.Ttl == 1) {
329 return;
330 }
331 --reply->Options.Ttl;
332 }
333
334 if (reply->Status == IP_SUCCESS) {
335 icmplen = sizeof(struct icmp_echo_hdr) + reply->DataSize;
336 if ((reply->Options.Flags & IP_FLAG_DF) != 0
337 && IP_HLEN + icmplen > pong->netif->mtu)
338 {
339 return;
340 }
341
342 p = pbuf_alloc(PBUF_IP, (u16_t)icmplen, PBUF_RAM);
343 if (RT_UNLIKELY(p == NULL)) {
344 return;
345 }
346
347 icmph = (struct icmp_echo_hdr *)p->payload;
348 icmph->type = ICMP_ER;
349 icmph->code = 0;
350 icmph->chksum = 0;
351 icmph->id = pong->reqicmph.id;
352 icmph->seqno = pong->reqicmph.seqno;
353
354 memcpy((u8_t *)p->payload + sizeof(*icmph),
355 reply->Data, reply->DataSize);
356 }
357 else {
358 u8_t type, code;
359
360 switch (reply->Status) {
361 case IP_DEST_NET_UNREACHABLE:
362 type = ICMP_DUR; code = ICMP_DUR_NET;
363 break;
364 case IP_DEST_HOST_UNREACHABLE:
365 type = ICMP_DUR; code = ICMP_DUR_HOST;
366 break;
367 case IP_DEST_PROT_UNREACHABLE:
368 type = ICMP_DUR; code = ICMP_DUR_PROTO;
369 break;
370 case IP_PACKET_TOO_BIG:
371 type = ICMP_DUR; code = ICMP_DUR_FRAG;
372 break;
373 case IP_SOURCE_QUENCH:
374 type = ICMP_SQ; code = 0;
375 break;
376 case IP_TTL_EXPIRED_TRANSIT:
377 type = ICMP_TE; code = ICMP_TE_TTL;
378 break;
379 case IP_TTL_EXPIRED_REASSEM:
380 type = ICMP_TE; code = ICMP_TE_FRAG;
381 break;
382 default:
383 DPRINTF(("pong4: reply status %d, dropped\n", reply->Status));
384 return;
385 }
386
387 DPRINTF(("pong4: reply status %d -> type %d/code %d\n",
388 reply->Status, type, code));
389
390 icmplen = sizeof(*icmph) + sizeof(pong->reqiph) + sizeof(pong->reqicmph);
391
392 p = pbuf_alloc(PBUF_IP, (u16_t)icmplen, PBUF_RAM);
393 if (RT_UNLIKELY(p == NULL)) {
394 return;
395 }
396
397 icmph = (struct icmp_echo_hdr *)p->payload;
398 icmph->type = type;
399 icmph->code = code;
400 icmph->chksum = 0;
401 icmph->id = 0;
402 icmph->seqno = 0;
403
404 /*
405 * XXX: we don't know the TTL of the request at the time this
406 * ICMP error was generated (we can guess it was 1 for ttl
407 * exceeded, but don't bother faking it).
408 */
409 memcpy((u8_t *)p->payload + sizeof(*icmph),
410 &pong->reqiph, sizeof(pong->reqiph));
411
412 memcpy((u8_t *)p->payload + sizeof(*icmph) + sizeof(pong->reqiph),
413 &pong->reqicmph, sizeof(pong->reqicmph));
414 }
415
416 icmph->chksum = inet_chksum(p->payload, (u16_t)icmplen);
417 ip_output_if(p, &src,
418 (ip_addr_t *)&pong->reqiph.src, /* dst */
419 reply->Options.Ttl,
420 reply->Options.Tos,
421 IPPROTO_ICMP,
422 pong->netif);
423 pbuf_free(p);
424}
425
426
427static void
428pxping_recv6(void *arg, struct pbuf *p)
429{
430 struct pxping *pxping = (struct pxping *)arg;
431 struct icmp6_echo_hdr *icmph;
432 size_t bufsize;
433 struct pong6 *pong;
434 int mapped;
435 void *reqdata;
436 size_t reqsize;
437 struct sockaddr_in6 src, dst;
438 int hopl;
439 IP_OPTION_INFORMATION opts;
440 int status;
441
442 pong = NULL;
443
444 icmph = (struct icmp6_echo_hdr *)p->payload;
445
446 memset(&dst, 0, sizeof(dst));
447 dst.sin6_family = AF_INET6;
448 mapped = pxremap_outbound_ip6((ip6_addr_t *)&dst.sin6_addr,
449 ip6_current_dest_addr());
450 if (RT_UNLIKELY(mapped == PXREMAP_FAILED)) {
451 goto out;
452 }
453
454 hopl = IP6H_HOPLIM(ip6_current_header());
455 if (mapped == PXREMAP_ASIS) {
456 if (RT_UNLIKELY(hopl == 1)) {
457 status = pbuf_header(p, ip_current_header_tot_len());
458 if (RT_LIKELY(status == 0)) {
459 icmp6_time_exceeded(p, ICMP6_TE_HL);
460 }
461 goto out;
462 }
463 --hopl;
464 }
465
466 status = pbuf_header(p, -(u16_t)sizeof(*icmph)); /* to ping payload */
467 if (RT_UNLIKELY(status != 0)) {
468 goto out;
469 }
470
471 bufsize = sizeof(ICMPV6_ECHO_REPLY) + p->tot_len;
472 pong = (struct pong6 *)malloc(sizeof(*pong) - sizeof(pong->buf) + bufsize);
473 if (RT_UNLIKELY(pong == NULL)) {
474 goto out;
475 }
476 pong->bufsize = bufsize;
477 pong->netif = pxping->netif;
478
479 ip6_addr_copy(pong->reqsrc, *ip6_current_src_addr());
480 memcpy(&pong->reqicmph, icmph, sizeof(*icmph));
481
482 memset(pong->buf, 0xa5, pong->bufsize);
483
484 pong->reqsize = reqsize = p->tot_len;
485 if (p->next == NULL) {
486 /* single pbuf can be directly used as request data source */
487 reqdata = p->payload;
488 }
489 else {
490 /* data from pbuf chain must be concatenated */
491 pbuf_copy_partial(p, pong->buf, p->tot_len, 0);
492 reqdata = pong->buf;
493 }
494
495 memset(&src, 0, sizeof(src));
496 src.sin6_family = AF_INET6;
497 src.sin6_addr = in6addr_any; /* let the OS select host source address */
498
499 memset(&opts, 0, sizeof(opts));
500 opts.Ttl = hopl;
501
502 status = Icmp6SendEcho2(pxping->hdl6, NULL,
503 pxping->callback6, pong,
504 &src, &dst, reqdata, (WORD)reqsize, &opts,
505 pong->buf, (DWORD)pong->bufsize,
506 5 * 1000 /* ms */);
507
508 if (RT_UNLIKELY(status != 0)) {
509 DPRINTF(("Icmp6SendEcho2: unexpected status %d\n", status));
510 goto out;
511 }
512 else if ((status = GetLastError()) != ERROR_IO_PENDING) {
513 int code;
514
515 DPRINTF(("Icmp6SendEcho2: error %d\n", status));
516 switch (status) {
517 case ERROR_NETWORK_UNREACHABLE:
518 case ERROR_HOST_UNREACHABLE:
519 code = ICMP6_DUR_NO_ROUTE;
520 break;
521 default:
522 code = -1;
523 break;
524 }
525
526 if (code != -1) {
527 /* move payload back to IP header */
528 status = pbuf_header(p, (u16_t)(sizeof(*icmph)
529 + ip_current_header_tot_len()));
530 if (RT_LIKELY(status == 0)) {
531 icmp6_dest_unreach(p, code);
532 }
533 }
534 goto out;
535 }
536
537 pong = NULL; /* callback owns it now */
538 out:
539 if (pong != NULL) {
540 free(pong);
541 }
542 pbuf_free(p);
543}
544
545
546static VOID WINAPI
547pxping_icmp6_callback_apc(void *ctx, PIO_STATUS_BLOCK iob, ULONG reserved)
548{
549 struct pong6 *pong = (struct pong6 *)ctx;
550 LWIP_UNUSED_ARG(iob);
551 LWIP_UNUSED_ARG(reserved);
552
553 if (pong != NULL) {
554 pxping_icmp6_callback(pong);
555 free(pong);
556 }
557}
558
559
560static VOID WINAPI
561pxping_icmp6_callback_old(void *ctx)
562{
563 struct pong6 *pong = (struct pong6 *)ctx;
564
565 if (pong != NULL) {
566 pxping_icmp6_callback(pong);
567 free(pong);
568 }
569}
570
571
572static void
573pxping_icmp6_callback(struct pong6 *pong)
574{
575 DWORD nreplies;
576 ICMPV6_ECHO_REPLY *reply;
577 struct pbuf *p;
578 struct icmp6_echo_hdr *icmph;
579 size_t icmplen;
580 ip6_addr_t src;
581 int mapped;
582
583 nreplies = Icmp6ParseReplies(pong->buf, (DWORD)pong->bufsize);
584 if (nreplies == 0) {
585 DWORD error = GetLastError();
586 if (error == IP_REQ_TIMED_OUT) {
587 DPRINTF2(("pong6: %p timed out\n", (void *)pong));
588 }
589 else {
590 DPRINTF(("pong6: %p: Icmp6ParseReplies: error %d\n",
591 (void *)pong, error));
592 }
593 return;
594 }
595
596 reply = (ICMPV6_ECHO_REPLY *)pong->buf;
597
598 mapped = pxremap_inbound_ip6(&src, (ip6_addr_t *)reply->Address.sin6_addr);
599 if (mapped == PXREMAP_FAILED) {
600 return;
601 }
602
603 /*
604 * Reply data follows ICMPV6_ECHO_REPLY structure in memory, but
605 * it doesn't tell us its size. Assume it's equal the size of the
606 * request.
607 */
608 icmplen = sizeof(*icmph) + pong->reqsize;
609 p = pbuf_alloc(PBUF_IP, (u16_t)icmplen, PBUF_RAM);
610 if (RT_UNLIKELY(p == NULL)) {
611 return;
612 }
613
614 icmph = (struct icmp6_echo_hdr *)p->payload;
615 icmph->type = ICMP6_TYPE_EREP;
616 icmph->code = 0;
617 icmph->chksum = 0;
618 icmph->id = pong->reqicmph.id;
619 icmph->seqno = pong->reqicmph.seqno;
620
621 memcpy((u8_t *)p->payload + sizeof(*icmph),
622 pong->buf + sizeof(*reply), pong->reqsize);
623
624 icmph->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len,
625 &src, &pong->reqsrc);
626 ip6_output_if(p, /* :src */ &src, /* :dst */ &pong->reqsrc,
627 LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6,
628 pong->netif);
629 pbuf_free(p);
630}
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