File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / mtr / packet / probe_cygwin.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 00:07:30 2021 UTC (3 years, 4 months ago) by misho
Branches: mtr, MAIN
CVS tags: v0_94, HEAD
mtr 0.94

    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: 
   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.
   17: */
   18: 
   19: #include "probe.h"
   20: 
   21: #include <assert.h>
   22: #include <errno.h>
   23: #include <error.h>
   24: #include <fcntl.h>
   25: #include <io.h>
   26: #include <stdio.h>
   27: #include <unistd.h>
   28: #include <winternl.h>
   29: 
   30: #include "protocols.h"
   31: 
   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: 
   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: 
   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  */
  106: void init_net_state(
  107:     struct net_state_t *net_state)
  108: {
  109:     HANDLE thread;
  110:     int in_pipe[2], out_pipe[2];
  111:     int err;
  112: 
  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) {
  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");
  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: /*
  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: /*
  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: {
  258:     struct icmp_thread_request_t *request =
  259:         (struct icmp_thread_request_t *) context;
  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;
  267:     ICMP_ECHO_REPLY *reply4;
  268:     ICMPV6_ECHO_REPLY *reply6;
  269: 
  270:     if (request->ip_version == 6) {
  271:         reply6 = request->reply6;
  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 {
  289:         reply4 = request->reply4;
  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: 
  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);
  333: }
  334: 
  335: /*  Use ICMP.DLL's send echo support to send a probe  */
  336: static
  337: void icmp_send_probe(
  338:     struct icmp_thread_request_t *request,
  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: 
  351:     if (request->timeout > 0) {
  352:         timeout = 1000 * request->timeout;
  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: 
  362:     memset(&option, 0, sizeof(IP_OPTION_INFORMATION));
  363:     option.Ttl = request->ttl;
  364: 
  365:     if (request->ip_version == 6) {
  366:         reply_size = sizeof(ICMPV6_ECHO_REPLY) + payload_size;
  367:     } else {
  368:         reply_size = sizeof(ICMP_ECHO_REPLY) + payload_size;
  369:     }
  370: 
  371:     request->reply4 = malloc(reply_size);
  372:     if (request->reply4 == NULL) {
  373:         error(EXIT_FAILURE, errno, "failure to allocate reply buffer");
  374:     }
  375: 
  376:     if (request->ip_version == 6) {
  377:         src_sockaddr6 = (struct sockaddr_in6 *) &request->src_sockaddr;
  378:         dest_sockaddr6 = (struct sockaddr_in6 *) &request->dest_sockaddr;
  379: 
  380:         send_result = Icmp6SendEcho2(request->net_state->platform.icmp6,
  381:                                      NULL,
  382:                                      (FARPROC) on_icmp_reply,
  383:                                      request,
  384:                                      src_sockaddr6, dest_sockaddr6,
  385:                                      payload, payload_size, &option,
  386:                                      request->reply6,
  387:                                      reply_size, timeout);
  388:     } else {
  389:         dest_sockaddr4 = (struct sockaddr_in *) &request->dest_sockaddr;
  390: 
  391:         send_result = IcmpSendEcho2(request->net_state->platform.icmp4,
  392:                                     NULL,
  393:                                     (FARPROC) on_icmp_reply,
  394:                                     request,
  395:                                     dest_sockaddr4->sin_addr.s_addr,
  396:                                     payload, payload_size, &option,
  397:                                     request->reply4,
  398:                                     reply_size, timeout);
  399:     }
  400: 
  401:     if (send_result == 0) {
  402:         err = GetLastError();
  403: 
  404:         /*
  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:         */
  409:         if (err != ERROR_IO_PENDING) {
  410:             request->icmp_type = -1;
  411:             request->reply_status = err;
  412:             queue_thread_result(request);
  413:         }
  414:     }
  415: }
  416: 
  417: /*  Fill the payload of the packet as specified by the probe parameters  */
  418: static
  419: int fill_payload(
  420:     const struct icmp_thread_request_t *request,
  421:     char *payload,
  422:     int payload_buffer_size)
  423: {
  424:     int ip_icmp_size;
  425:     int payload_size;
  426: 
  427:     if (request->ip_version == 6) {
  428:         ip_icmp_size =
  429:             sizeof(struct IP6Header) + sizeof(struct ICMPHeader);
  430:     } else if (request->ip_version == 4) {
  431:         ip_icmp_size = sizeof(struct IPHeader) + sizeof(struct ICMPHeader);
  432:     } else {
  433:         errno = EINVAL;
  434:         return -1;
  435:     }
  436: 
  437:     payload_size = request->packet_size - ip_icmp_size;
  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: 
  447:     memset(payload, request->bit_pattern, payload_size);
  448: 
  449:     return payload_size;
  450: }
  451: 
  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: 
  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: 
  634:     if (resolve_probe_addresses(net_state, param, &dest_sockaddr,
  635:                 &src_sockaddr)) {
  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: 
  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;
  669: 
  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:     }
  679: }
  680: 
  681: /*
  682:     Read the status of completed probes from the ICMP service
  683:     if any has completed.
  684: */
  685: void receive_replies(
  686:     struct net_state_t *net_state)
  687: {
  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);
  717: }
  718: 
  719: /*
  720:     On Windows, an implementation of check_probe_timeout is unnecessary because
  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>