Annotation of embedaddon/mtr/packet/probe_cygwin.c, revision 1.1.1.3

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:         /*
1.1.1.3 ! misho     355:            IcmpSendEcho2 will return invalid argument on a timeout of
1.1       misho     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>