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>