Annotation of embedaddon/mtr/packet/probe_cygwin.c, revision 1.1.1.2
1.1 misho 1: /*
2: mtr -- a network diagnostic tool
3: Copyright (C) 2016 Matt Kimball
4:
5: This program is free software; you can redistribute it and/or modify
6: it under the terms of the GNU General Public License version 2 as
7: published by the Free Software Foundation.
8:
9: This program is distributed in the hope that it will be useful,
10: but WITHOUT ANY WARRANTY; without even the implied warranty of
11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12: GNU General Public License for more details.
13:
1.1.1.2 ! misho 14: You should have received a copy of the GNU General Public License along
! 15: with this program; if not, write to the Free Software Foundation, Inc.,
! 16: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1.1 misho 17: */
18:
19: #include "probe.h"
20:
1.1.1.2 ! misho 21: #include <assert.h>
1.1 misho 22: #include <errno.h>
1.1.1.2 ! misho 23: #include <error.h>
! 24: #include <fcntl.h>
! 25: #include <io.h>
1.1 misho 26: #include <stdio.h>
1.1.1.2 ! misho 27: #include <unistd.h>
1.1 misho 28: #include <winternl.h>
29:
30: #include "protocols.h"
31:
1.1.1.2 ! misho 32:
! 33: /*
! 34: Implementation notes (or "Why this uses a worker thread")
! 35:
! 36: Having done my time debugging various race conditions over the
! 37: last twenty-plus years as a software developer, both of my own
! 38: creation and discovered in the code of others, I almost always
! 39: try to structure my code to be single-threaded. However,
! 40: I think in this case, the ICMP service thread is unavoidable.
! 41:
! 42: I would have liked to avoid multithreading entirely, but here are
! 43: the constraints:
! 44:
! 45: a) mtr was originally a Unix program which used "raw sockets".
! 46: b) In order to port mtr to Windows, Cygwin is used to get a
! 47: Unix-like environment.
! 48: c) You can't use a raw socket to receive an ICMP reply on Windows.
! 49: However, Windows provides a separate API in the form of
! 50: ICMP.DLL for sending and receiving ICMP messages.
! 51: d) The ICMP API works asynchronously, and requires completion
! 52: through an asynchronous procedure call ("APC")
! 53: e) APCs are only delivered during blocking Win32 operations
! 54: which are flagged as "alertable." This prevents apps from
! 55: having APCs execute unexpectedly during an I/O operation.
! 56: f) Cygwin's implementation of POSIX functions does all I/O
! 57: through non-alertable I/O operations. This is reasonable
! 58: because APCs don't exist in the POSIX API.
! 59: g) Cygwin implements Unix-style signals at the application level,
! 60: since the Windows kernel doesn't have them. We want our
! 61: program to respond to SIGTERM and SIGKILL, at least.
! 62: h) Cygwin's signal implementation will deliver signals during
! 63: blocking I/O functions in the Cygwin library, but won't
! 64: respond to signals if the signal is sent while the application
! 65: is in a blocking Windows API call which Cygwin is not aware of.
! 66: i) Since we want to both send/receive ICMP probes and also respond
! 67: to Unix-style signals, we require two threads: one which
! 68: uses Cygwin's POSIX style blocking I/O and can respond to
! 69: signals, and one which uses alertable waits using Win32
! 70: blocking APIs.
! 71:
! 72: The solution is to have the main thread using select() as the
! 73: blocking operation in its loop, and also to have an ICMP service
! 74: thread using WaitForSingleObjectEx() as its blocking operation.
! 75: The main thread will respond to signals. The ICMP service thread
! 76: will run the APCs completing ICMP.DLL requests.
! 77:
! 78: These two threads communicate through a pair of pipes. One pipe
! 79: sends requests from the main thread to the ICMP service thread,
! 80: and another pipe sends the requests back as they complete.
! 81:
! 82: We use the Cygwin pipe() to create the pipes, but in the ICMP
! 83: service thread we use the Win32 HANDLE that corresponds to the
! 84: receiving end of the input pipe to wait for ICMP requests.
! 85: */
! 86:
! 87:
! 88: static DWORD WINAPI icmp_service_thread(LPVOID param);
! 89:
1.1 misho 90: /* Windows doesn't require any initialization at a privileged level */
91: void init_net_state_privileged(
92: struct net_state_t *net_state)
93: {
94: }
95:
1.1.1.2 ! misho 96: /*
! 97: Convenience similar to error(), but for reporting Windows
! 98: error codes instead of errno codes.
! 99: */
! 100: void error_win(int exit_code, int win_error, const char *str) {
! 101: fprintf(stderr, "%s (code %d)\n", str, win_error);
! 102: exit(exit_code);
! 103: }
! 104:
! 105: /* Open the ICMP.DLL interface and start the ICMP service thread */
1.1 misho 106: void init_net_state(
107: struct net_state_t *net_state)
108: {
1.1.1.2 ! misho 109: HANDLE thread;
! 110: int in_pipe[2], out_pipe[2];
! 111: int err;
! 112:
1.1 misho 113: memset(net_state, 0, sizeof(struct net_state_t));
114:
115: net_state->platform.icmp4 = IcmpCreateFile();
116: net_state->platform.icmp6 = Icmp6CreateFile();
117:
118: if (net_state->platform.icmp4 == INVALID_HANDLE_VALUE
119: && net_state->platform.icmp6 == INVALID_HANDLE_VALUE) {
1.1.1.2 ! misho 120:
! 121: error_win(EXIT_FAILURE, GetLastError(), "Failure opening ICMP");
! 122: }
! 123: net_state->platform.ip4_socket_raw = false;
! 124: net_state->platform.ip6_socket_raw = false;
! 125:
! 126: /*
! 127: We need a pipe for communication with the ICMP thread
! 128: in each direction.
! 129: */
! 130: if (pipe(in_pipe) == -1 || pipe(out_pipe) == -1) {
! 131: error(EXIT_FAILURE, errno, "Failure creating thread pipe");
! 132: }
! 133:
! 134: net_state->platform.thread_in_pipe_read = in_pipe[0];
! 135: net_state->platform.thread_in_pipe_write = in_pipe[1];
! 136: net_state->platform.thread_out_pipe_read = out_pipe[0];
! 137: net_state->platform.thread_out_pipe_write = out_pipe[1];
! 138:
! 139: net_state->platform.thread_in_pipe_read_handle =
! 140: (HANDLE)get_osfhandle(in_pipe[0]);
! 141:
! 142: /*
! 143: The read on the out pipe needs to be nonblocking because
! 144: it will be occasionally checked in the main thread.
! 145: */
! 146: err = fcntl(out_pipe[0], F_SETFL, O_NONBLOCK);
! 147: if (err == -1) {
! 148: error(
! 149: EXIT_FAILURE, errno,
! 150: "Failure setting pipe to non-blocking");
! 151: }
! 152:
! 153: /* Spin up the ICMP service thread */
! 154: thread = CreateThread(
! 155: NULL, 0, icmp_service_thread, net_state, 0, NULL);
! 156:
! 157: if (thread == NULL) {
! 158: error_win(
! 159: EXIT_FAILURE, GetLastError(),
! 160: "Failure creating ICMP service thread");
1.1 misho 161: }
162: }
163:
164: /*
165: If we succeeded at opening the ICMP file handle, we can
166: assume that IP protocol version is supported.
167: */
168: bool is_ip_version_supported(
169: struct net_state_t *net_state,
170: int ip_version)
171: {
172: if (ip_version == 4) {
173: return (net_state->platform.icmp4 != INVALID_HANDLE_VALUE);
174: } else if (ip_version == 6) {
175: return (net_state->platform.icmp6 != INVALID_HANDLE_VALUE);
176: }
177:
178: return false;
179: }
180:
181: /* On Windows, we only support ICMP probes */
182: bool is_protocol_supported(
183: struct net_state_t * net_state,
184: int protocol)
185: {
186: if (protocol == IPPROTO_ICMP) {
187: return true;
188: }
189:
190: return false;
191: }
192:
193: /* Set the back pointer to the net_state when a probe is allocated */
194: void platform_alloc_probe(
195: struct net_state_t *net_state,
196: struct probe_t *probe)
197: {
198: probe->platform.net_state = net_state;
199: }
200:
201: /* Free the reply buffer when the probe is freed */
202: void platform_free_probe(
203: struct probe_t *probe)
204: {
205: }
206:
207: /* Report a windows error code using a platform-independent error string */
208: static
209: void report_win_error(
210: int command_token,
211: int err)
212: {
213: /* It could be that we got no reply because of timeout */
214: if (err == IP_REQ_TIMED_OUT || err == IP_SOURCE_QUENCH) {
215: printf("%d no-reply\n", command_token);
216: } else if (err == ERROR_INVALID_NETNAME) {
217: printf("%d address-not-available\n", command_token);
218: } else if (err == ERROR_INVALID_PARAMETER) {
219: printf("%d invalid-argument\n", command_token);
220: } else {
221: printf("%d unexpected-error winerror %d\n", command_token, err);
222: }
223: }
224:
225: /*
1.1.1.2 ! misho 226: After we have the result of an ICMP probe on the ICMP service
! 227: thread, this is used to send the result back to the main thread
! 228: for probe result reporting.
! 229: */
! 230: static
! 231: void queue_thread_result(struct icmp_thread_request_t *request)
! 232: {
! 233: int byte_count;
! 234:
! 235: /* Pass ownership of the request back through the result pipe */
! 236: byte_count = write(
! 237: request->net_state->platform.thread_out_pipe_write,
! 238: &request,
! 239: sizeof(struct icmp_thread_request_t *));
! 240: if (byte_count == -1) {
! 241: error(
! 242: EXIT_FAILURE, errno,
! 243: "failure writing to probe result queue");
! 244: }
! 245: }
! 246:
! 247: /*
1.1 misho 248: The overlapped I/O style completion routine to be called by
249: Windows during an altertable wait when an ICMP probe has
250: completed, either by reply, or by ICMP.DLL timeout.
251: */
252: static
253: void WINAPI on_icmp_reply(
254: PVOID context,
255: PIO_STATUS_BLOCK status,
256: ULONG reserved)
257: {
1.1.1.2 ! misho 258: struct icmp_thread_request_t *request =
! 259: (struct icmp_thread_request_t *) context;
1.1 misho 260: int icmp_type;
261: int round_trip_us = 0;
262: int reply_count;
263: int reply_status = 0;
264: struct sockaddr_storage remote_addr;
265: struct sockaddr_in *remote_addr4;
266: struct sockaddr_in6 *remote_addr6;
1.1.1.2 ! misho 267: ICMP_ECHO_REPLY *reply4;
1.1 misho 268: ICMPV6_ECHO_REPLY *reply6;
269:
1.1.1.2 ! misho 270: if (request->ip_version == 6) {
! 271: reply6 = request->reply6;
1.1 misho 272: reply_count = Icmp6ParseReplies(reply6, sizeof(ICMPV6_ECHO_REPLY));
273:
274: if (reply_count > 0) {
275: reply_status = reply6->Status;
276:
277: /* Unfortunately, ICMP.DLL only has millisecond precision */
278: round_trip_us = reply6->RoundTripTime * 1000;
279:
280: remote_addr6 = (struct sockaddr_in6 *) &remote_addr;
281: remote_addr6->sin6_family = AF_INET6;
282: remote_addr6->sin6_port = 0;
283: remote_addr6->sin6_flowinfo = 0;
284: memcpy(&remote_addr6->sin6_addr, reply6->AddressBits,
285: sizeof(struct in6_addr));
286: remote_addr6->sin6_scope_id = 0;
287: }
288: } else {
1.1.1.2 ! misho 289: reply4 = request->reply4;
1.1 misho 290: reply_count = IcmpParseReplies(reply4, sizeof(ICMP_ECHO_REPLY));
291:
292: if (reply_count > 0) {
293: reply_status = reply4->Status;
294:
295: /* Unfortunately, ICMP.DLL only has millisecond precision */
296: round_trip_us = reply4->RoundTripTime * 1000;
297:
298: remote_addr4 = (struct sockaddr_in *) &remote_addr;
299: remote_addr4->sin_family = AF_INET;
300: remote_addr4->sin_port = 0;
301: remote_addr4->sin_addr.s_addr = reply4->Address;
302: }
303: }
304:
305: if (reply_count == 0) {
306: reply_status = GetLastError();
307: }
308:
309: icmp_type = -1;
310: if (reply_status == IP_SUCCESS) {
311: icmp_type = ICMP_ECHOREPLY;
312: } else if (reply_status == IP_TTL_EXPIRED_TRANSIT
313: || reply_status == IP_TTL_EXPIRED_REASSEM) {
314:
1.1.1.2 ! misho 315: icmp_type = ICMP_TIME_EXCEEDED;
! 316: } else if (reply_status == IP_DEST_HOST_UNREACHABLE
! 317: || reply_status == IP_DEST_PORT_UNREACHABLE
! 318: || reply_status == IP_DEST_PROT_UNREACHABLE
! 319: || reply_status == IP_DEST_NET_UNREACHABLE
! 320: || reply_status == IP_DEST_UNREACHABLE
! 321: || reply_status == IP_DEST_NO_ROUTE
! 322: || reply_status == IP_BAD_ROUTE
! 323: || reply_status == IP_BAD_DESTINATION) {
! 324:
! 325: icmp_type = ICMP_DEST_UNREACH;
! 326: }
! 327:
! 328: request->icmp_type = icmp_type;
! 329: request->reply_status = reply_status;
! 330: request->remote_addr = remote_addr;
! 331: request->round_trip_us = round_trip_us;
! 332: queue_thread_result(request);
1.1 misho 333: }
334:
335: /* Use ICMP.DLL's send echo support to send a probe */
336: static
337: void icmp_send_probe(
1.1.1.2 ! misho 338: struct icmp_thread_request_t *request,
1.1 misho 339: char *payload,
340: int payload_size)
341: {
342: IP_OPTION_INFORMATION option;
343: DWORD timeout;
344: DWORD send_result;
345: int reply_size;
346: int err;
347: struct sockaddr_in *dest_sockaddr4;
348: struct sockaddr_in6 *src_sockaddr6;
349: struct sockaddr_in6 *dest_sockaddr6;
350:
1.1.1.2 ! misho 351: if (request->timeout > 0) {
! 352: timeout = 1000 * request->timeout;
1.1 misho 353: } else {
354: /*
355: IcmpSendEcho2 will return invalid argument on a timeout of
356: zero. Our Unix implementation allows it. Bump up the timeout
357: to 1 millisecond.
358: */
359: timeout = 1;
360: }
361:
1.1.1.2 ! misho 362: memset(&option, 0, sizeof(IP_OPTION_INFORMATION));
! 363: option.Ttl = request->ttl;
1.1 misho 364:
1.1.1.2 ! misho 365: if (request->ip_version == 6) {
1.1 misho 366: reply_size = sizeof(ICMPV6_ECHO_REPLY) + payload_size;
367: } else {
1.1.1.2 ! misho 368: reply_size = sizeof(ICMP_ECHO_REPLY) + payload_size;
1.1 misho 369: }
370:
1.1.1.2 ! misho 371: request->reply4 = malloc(reply_size);
! 372: if (request->reply4 == NULL) {
! 373: error(EXIT_FAILURE, errno, "failure to allocate reply buffer");
1.1 misho 374: }
375:
1.1.1.2 ! misho 376: if (request->ip_version == 6) {
! 377: src_sockaddr6 = (struct sockaddr_in6 *) &request->src_sockaddr;
! 378: dest_sockaddr6 = (struct sockaddr_in6 *) &request->dest_sockaddr;
1.1 misho 379:
1.1.1.2 ! misho 380: send_result = Icmp6SendEcho2(request->net_state->platform.icmp6,
! 381: NULL,
! 382: (FARPROC) on_icmp_reply,
! 383: request,
1.1 misho 384: src_sockaddr6, dest_sockaddr6,
385: payload, payload_size, &option,
1.1.1.2 ! misho 386: request->reply6,
! 387: reply_size, timeout);
1.1 misho 388: } else {
1.1.1.2 ! misho 389: dest_sockaddr4 = (struct sockaddr_in *) &request->dest_sockaddr;
1.1 misho 390:
1.1.1.2 ! misho 391: send_result = IcmpSendEcho2(request->net_state->platform.icmp4,
! 392: NULL,
! 393: (FARPROC) on_icmp_reply,
! 394: request,
1.1 misho 395: dest_sockaddr4->sin_addr.s_addr,
396: payload, payload_size, &option,
1.1.1.2 ! misho 397: request->reply4,
! 398: reply_size, timeout);
1.1 misho 399: }
400:
401: if (send_result == 0) {
402: err = GetLastError();
403:
404: /*
1.1.1.2 ! misho 405: ERROR_IO_PENDING is expected when the probe is sent.
! 406: Other errors indicate the probe wasn't sent, and should
! 407: be reported in the main thread.
! 408: */
1.1 misho 409: if (err != ERROR_IO_PENDING) {
1.1.1.2 ! misho 410: request->icmp_type = -1;
! 411: request->reply_status = err;
! 412: queue_thread_result(request);
1.1 misho 413: }
414: }
415: }
416:
417: /* Fill the payload of the packet as specified by the probe parameters */
418: static
419: int fill_payload(
1.1.1.2 ! misho 420: const struct icmp_thread_request_t *request,
1.1 misho 421: char *payload,
422: int payload_buffer_size)
423: {
424: int ip_icmp_size;
425: int payload_size;
426:
1.1.1.2 ! misho 427: if (request->ip_version == 6) {
1.1 misho 428: ip_icmp_size =
429: sizeof(struct IP6Header) + sizeof(struct ICMPHeader);
1.1.1.2 ! misho 430: } else if (request->ip_version == 4) {
1.1 misho 431: ip_icmp_size = sizeof(struct IPHeader) + sizeof(struct ICMPHeader);
432: } else {
433: errno = EINVAL;
434: return -1;
435: }
436:
1.1.1.2 ! misho 437: payload_size = request->packet_size - ip_icmp_size;
1.1 misho 438: if (payload_size < 0) {
439: payload_size = 0;
440: }
441:
442: if (payload_size > payload_buffer_size) {
443: errno = EINVAL;
444: return -1;
445: }
446:
1.1.1.2 ! misho 447: memset(payload, request->bit_pattern, payload_size);
1.1 misho 448:
449: return payload_size;
450: }
451:
1.1.1.2 ! misho 452: /*
! 453: We've received a probe request from the main thread, so
! 454: fill out a payload buffer and then send the probe.
! 455: */
! 456: static
! 457: void icmp_handle_probe_request(struct icmp_thread_request_t *request)
! 458: {
! 459: char payload[PACKET_BUFFER_SIZE];
! 460: int payload_size;
! 461:
! 462: payload_size = fill_payload(request, payload, PACKET_BUFFER_SIZE);
! 463: if (payload_size < 0) {
! 464: error(EXIT_FAILURE, errno, "Error constructing packet");
! 465: }
! 466:
! 467: icmp_send_probe(request, payload, payload_size);
! 468: }
! 469:
! 470: /*
! 471: The main loop of the ICMP service thread. The loop starts
! 472: an overlapped read on the incoming request pipe, then waits
! 473: in an alertable wait for that read to complete. Because
! 474: the wait is alertable, ICMP probes can complete through
! 475: APCs in that wait.
! 476: */
! 477: static
! 478: DWORD WINAPI icmp_service_thread(LPVOID param) {
! 479: struct net_state_t *net_state;
! 480: struct icmp_thread_request_t *request;
! 481: DWORD wait_status;
! 482: OVERLAPPED overlapped;
! 483: HANDLE event;
! 484: BOOL success;
! 485: bool read_pending;
! 486: DWORD read_count;
! 487: int err;
! 488:
! 489: /*
! 490: We need an event to signal completion of reads from the request
! 491: pipe.
! 492: */
! 493: event = CreateEvent(NULL, TRUE, FALSE, NULL);
! 494: if (event == NULL) {
! 495: error_win(
! 496: EXIT_FAILURE, GetLastError(),
! 497: "failure creating ICMP thread event");
! 498: }
! 499:
! 500: net_state = (struct net_state_t *)param;
! 501: read_pending = false;
! 502: while (true) {
! 503: /*
! 504: Start a new read on the request pipe if none is
! 505: currently pending.
! 506: */
! 507: if (!read_pending) {
! 508: request = NULL;
! 509:
! 510: ResetEvent(event);
! 511:
! 512: memset(&overlapped, 0, sizeof(OVERLAPPED));
! 513: overlapped.hEvent = event;
! 514:
! 515: success = ReadFile(
! 516: net_state->platform.thread_in_pipe_read_handle,
! 517: &request,
! 518: sizeof(struct icmp_thread_request_t *),
! 519: NULL,
! 520: &overlapped);
! 521:
! 522: if (!success) {
! 523: err = GetLastError();
! 524:
! 525: if (err != ERROR_IO_PENDING) {
! 526: error_win(
! 527: EXIT_FAILURE, err,
! 528: "failure starting overlapped thread pipe read");
! 529: }
! 530: }
! 531:
! 532: read_pending = true;
! 533: }
! 534:
! 535: /*
! 536: Wait for either the request read to complete, or
! 537: an APC which completes an ICMP probe.
! 538: */
! 539: wait_status = WaitForSingleObjectEx(
! 540: event,
! 541: INFINITE,
! 542: TRUE);
! 543:
! 544: /*
! 545: If the event we waited on has been signalled, read
! 546: the request from the pipe.
! 547: */
! 548: if (wait_status == WAIT_OBJECT_0) {
! 549: read_pending = false;
! 550:
! 551: success = GetOverlappedResult(
! 552: net_state->platform.thread_in_pipe_read_handle,
! 553: &overlapped,
! 554: &read_count,
! 555: FALSE);
! 556:
! 557: if (!success) {
! 558: error_win(
! 559: EXIT_FAILURE, GetLastError(),
! 560: "failure completing overlapped thread pipe read");
! 561: }
! 562:
! 563: if (read_count == 0) {
! 564: continue;
! 565: }
! 566:
! 567: assert(
! 568: read_count == sizeof(struct icmp_thread_request_t *));
! 569:
! 570: /* Start the new probe from the request */
! 571: icmp_handle_probe_request(request);
! 572: }
! 573: }
! 574: }
! 575:
! 576: /*
! 577: When we are on the main thread and need the ICMP service thread
! 578: to start a new probe, this is used to pass the request for the
! 579: new probe to the service thread.
! 580: */
! 581: static
! 582: void queue_thread_request(
! 583: struct net_state_t *net_state,
! 584: struct probe_t *probe,
! 585: const struct probe_param_t *param,
! 586: struct sockaddr_storage *dest_sockaddr,
! 587: struct sockaddr_storage *src_sockaddr)
! 588: {
! 589: struct icmp_thread_request_t *request;
! 590: int byte_count;
! 591:
! 592: request = malloc(sizeof(struct icmp_thread_request_t));
! 593: if (request == NULL) {
! 594: error(EXIT_FAILURE, errno, "failure to allocate request");
! 595: }
! 596: memset(request, 0, sizeof(struct icmp_thread_request_t));
! 597:
! 598: request->ip_version = param->ip_version;
! 599: request->ttl = param->ttl;
! 600: request->timeout = param->timeout;
! 601: request->packet_size = param->packet_size;
! 602: request->bit_pattern = param->bit_pattern;
! 603:
! 604: request->net_state = net_state;
! 605: request->probe = probe;
! 606: request->dest_sockaddr = *dest_sockaddr;
! 607: request->src_sockaddr = *src_sockaddr;
! 608:
! 609: /*
! 610: The ownership of the request is passed to the ICMP thread
! 611: through the pipe.
! 612: */
! 613: byte_count = write(
! 614: net_state->platform.thread_in_pipe_write,
! 615: &request,
! 616: sizeof(struct icmp_thread_request_t *));
! 617:
! 618: if (byte_count == -1) {
! 619: error(
! 620: EXIT_FAILURE, errno,
! 621: "failure writing to probe request queue");
! 622: }
! 623: }
! 624:
1.1 misho 625: /* Decode the probe parameters and send a probe */
626: void send_probe(
627: struct net_state_t *net_state,
628: const struct probe_param_t *param)
629: {
630: struct probe_t *probe;
631: struct sockaddr_storage dest_sockaddr;
632: struct sockaddr_storage src_sockaddr;
633:
1.1.1.2 ! misho 634: if (resolve_probe_addresses(net_state, param, &dest_sockaddr,
! 635: &src_sockaddr)) {
1.1 misho 636: printf("%d invalid-argument\n", param->command_token);
637: return;
638: }
639:
640: probe = alloc_probe(net_state, param->command_token);
641: if (probe == NULL) {
642: printf("%d probes-exhausted\n", param->command_token);
643: return;
644: }
645:
646: probe->platform.ip_version = param->ip_version;
647:
1.1.1.2 ! misho 648: queue_thread_request(
! 649: net_state, probe, param, &dest_sockaddr, &src_sockaddr);
! 650: }
! 651:
! 652: /*
! 653: After we've receive the result from the ICMP service thread,
! 654: report either the probe status, or any Windows error we
! 655: encountered while attempting to send the probe.
! 656: */
! 657: static
! 658: void complete_icmp_result(struct icmp_thread_request_t *request)
! 659: {
! 660: struct net_state_t *net_state;
! 661: struct probe_t *probe;
! 662:
! 663: /*
! 664: We can de-const the net_state and probe, since we are back
! 665: on the main thread.
! 666: */
! 667: net_state = (struct net_state_t *)request->net_state;
! 668: probe = (struct probe_t *)request->probe;
1.1 misho 669:
1.1.1.2 ! misho 670: if (request->icmp_type != -1) {
! 671: /* Record probe result */
! 672: respond_to_probe(net_state, probe,
! 673: request->icmp_type, &request->remote_addr,
! 674: request->round_trip_us, 0, NULL);
! 675: } else {
! 676: report_win_error(probe->token, request->reply_status);
! 677: free_probe(net_state, probe);
! 678: }
1.1 misho 679: }
680:
681: /*
1.1.1.2 ! misho 682: Read the status of completed probes from the ICMP service
! 683: if any has completed.
1.1 misho 684: */
685: void receive_replies(
686: struct net_state_t *net_state)
687: {
1.1.1.2 ! misho 688: int read_count;
! 689: struct icmp_thread_request_t *request;
! 690:
! 691: read_count = read(
! 692: net_state->platform.thread_out_pipe_read,
! 693: &request,
! 694: sizeof(struct icmp_thread_request_t *));
! 695:
! 696: if (read_count == -1) {
! 697: /*
! 698: EINTR and EAGAIN can occur under normal conditions, and
! 699: should be retried. We will retry the next iteration
! 700: of the main loop.
! 701: */
! 702: if (errno == EINTR || errno == EAGAIN) {
! 703: return;
! 704: }
! 705:
! 706: error(EXIT_FAILURE, errno, "thread result pipe read error");
! 707: }
! 708:
! 709: assert(read_count == sizeof(struct icmp_thread_request_t *));
! 710: complete_icmp_result(request);
! 711:
! 712: if (request->reply4) {
! 713: free(request->reply4);
! 714: request->reply4 = NULL;
! 715: }
! 716: free(request);
1.1 misho 717: }
718:
719: /*
1.1.1.2 ! misho 720: On Windows, an implementation of check_probe_timeout is unnecessary because
1.1 misho 721: timeouts are managed by ICMP.DLL, including a call to the I/O completion
722: routine when the time fully expires.
723: */
724: void check_probe_timeouts(
725: struct net_state_t *net_state)
726: {
727: }
728:
729: /*
730: As in the case of check_probe_timeout, getting the next probe timeout is
731: unnecessary under Windows, as ICMP.DLL manages timeouts for us.
732: */
733: bool get_next_probe_timeout(
734: const struct net_state_t *net_state,
735: struct timeval *timeout)
736: {
737: return false;
738: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>