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

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: 
                     14:     You should have received a copy of the GNU General Public License
                     15:     along with this program; if not, write to the Free Software
                     16:     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
                     17: */
                     18: 
                     19: #include "probe.h"
                     20: 
                     21: #include <errno.h>
                     22: #include <stdio.h>
                     23: #include <winternl.h>
                     24: 
                     25: #include "protocols.h"
                     26: 
                     27: /*  Windows doesn't require any initialization at a privileged level  */
                     28: void init_net_state_privileged(
                     29:     struct net_state_t *net_state)
                     30: {
                     31: }
                     32: 
                     33: /*  Open the ICMP.DLL interface  */
                     34: void init_net_state(
                     35:     struct net_state_t *net_state)
                     36: {
                     37:     memset(net_state, 0, sizeof(struct net_state_t));
                     38: 
                     39:     net_state->platform.icmp4 = IcmpCreateFile();
                     40:     net_state->platform.icmp6 = Icmp6CreateFile();
                     41: 
                     42:     if (net_state->platform.icmp4 == INVALID_HANDLE_VALUE
                     43:         && net_state->platform.icmp6 == INVALID_HANDLE_VALUE) {
                     44:         fprintf(stderr, "Failure opening ICMP %d\n", GetLastError());
                     45:         exit(EXIT_FAILURE);
                     46:     }
                     47: }
                     48: 
                     49: /*
                     50:     If we succeeded at opening the ICMP file handle, we can
                     51:     assume that IP protocol version is supported.
                     52: */
                     53: bool is_ip_version_supported(
                     54:     struct net_state_t *net_state,
                     55:     int ip_version)
                     56: {
                     57:     if (ip_version == 4) {
                     58:         return (net_state->platform.icmp4 != INVALID_HANDLE_VALUE);
                     59:     } else if (ip_version == 6) {
                     60:         return (net_state->platform.icmp6 != INVALID_HANDLE_VALUE);
                     61:     }
                     62: 
                     63:     return false;
                     64: }
                     65: 
                     66: /*  On Windows, we only support ICMP probes  */
                     67: bool is_protocol_supported(
                     68:     struct net_state_t * net_state,
                     69:     int protocol)
                     70: {
                     71:     if (protocol == IPPROTO_ICMP) {
                     72:         return true;
                     73:     }
                     74: 
                     75:     return false;
                     76: }
                     77: 
                     78: /*  Set the back pointer to the net_state when a probe is allocated  */
                     79: void platform_alloc_probe(
                     80:     struct net_state_t *net_state,
                     81:     struct probe_t *probe)
                     82: {
                     83:     probe->platform.net_state = net_state;
                     84: }
                     85: 
                     86: /*  Free the reply buffer when the probe is freed  */
                     87: void platform_free_probe(
                     88:     struct probe_t *probe)
                     89: {
                     90:     if (probe->platform.reply4) {
                     91:         free(probe->platform.reply4);
                     92:         probe->platform.reply4 = NULL;
                     93:     }
                     94: }
                     95: 
                     96: /*  Report a windows error code using a platform-independent error string  */
                     97: static
                     98: void report_win_error(
                     99:     int command_token,
                    100:     int err)
                    101: {
                    102:     /*  It could be that we got no reply because of timeout  */
                    103:     if (err == IP_REQ_TIMED_OUT || err == IP_SOURCE_QUENCH) {
                    104:         printf("%d no-reply\n", command_token);
                    105:     } else if (err == IP_DEST_HOST_UNREACHABLE
                    106:                || err == IP_DEST_PORT_UNREACHABLE
                    107:                || err == IP_DEST_PROT_UNREACHABLE
                    108:                || err == IP_DEST_NET_UNREACHABLE
                    109:                || err == IP_DEST_UNREACHABLE
                    110:                || err == IP_DEST_NO_ROUTE
                    111:                || err == IP_BAD_ROUTE || err == IP_BAD_DESTINATION) {
                    112:         printf("%d no-route\n", command_token);
                    113:     } else if (err == ERROR_INVALID_NETNAME) {
                    114:         printf("%d address-not-available\n", command_token);
                    115:     } else if (err == ERROR_INVALID_PARAMETER) {
                    116:         printf("%d invalid-argument\n", command_token);
                    117:     } else {
                    118:         printf("%d unexpected-error winerror %d\n", command_token, err);
                    119:     }
                    120: }
                    121: 
                    122: /*
                    123:     The overlapped I/O style completion routine to be called by
                    124:     Windows during an altertable wait when an ICMP probe has
                    125:     completed, either by reply, or by ICMP.DLL timeout.
                    126: */
                    127: static
                    128: void WINAPI on_icmp_reply(
                    129:     PVOID context,
                    130:     PIO_STATUS_BLOCK status,
                    131:     ULONG reserved)
                    132: {
                    133:     struct probe_t *probe = (struct probe_t *) context;
                    134:     struct net_state_t *net_state = probe->platform.net_state;
                    135:     int icmp_type;
                    136:     int round_trip_us = 0;
                    137:     int reply_count;
                    138:     int reply_status = 0;
                    139:     struct sockaddr_storage remote_addr;
                    140:     struct sockaddr_in *remote_addr4;
                    141:     struct sockaddr_in6 *remote_addr6;
                    142:     ICMP_ECHO_REPLY32 *reply4;
                    143:     ICMPV6_ECHO_REPLY *reply6;
                    144: 
                    145:     if (probe->platform.ip_version == 6) {
                    146:         reply6 = probe->platform.reply6;
                    147:         reply_count = Icmp6ParseReplies(reply6, sizeof(ICMPV6_ECHO_REPLY));
                    148: 
                    149:         if (reply_count > 0) {
                    150:             reply_status = reply6->Status;
                    151: 
                    152:             /*  Unfortunately, ICMP.DLL only has millisecond precision  */
                    153:             round_trip_us = reply6->RoundTripTime * 1000;
                    154: 
                    155:             remote_addr6 = (struct sockaddr_in6 *) &remote_addr;
                    156:             remote_addr6->sin6_family = AF_INET6;
                    157:             remote_addr6->sin6_port = 0;
                    158:             remote_addr6->sin6_flowinfo = 0;
                    159:             memcpy(&remote_addr6->sin6_addr, reply6->AddressBits,
                    160:                    sizeof(struct in6_addr));
                    161:             remote_addr6->sin6_scope_id = 0;
                    162:         }
                    163:     } else {
                    164:         reply4 = probe->platform.reply4;
                    165:         reply_count = IcmpParseReplies(reply4, sizeof(ICMP_ECHO_REPLY));
                    166: 
                    167:         if (reply_count > 0) {
                    168:             reply_status = reply4->Status;
                    169: 
                    170:             /*  Unfortunately, ICMP.DLL only has millisecond precision  */
                    171:             round_trip_us = reply4->RoundTripTime * 1000;
                    172: 
                    173:             remote_addr4 = (struct sockaddr_in *) &remote_addr;
                    174:             remote_addr4->sin_family = AF_INET;
                    175:             remote_addr4->sin_port = 0;
                    176:             remote_addr4->sin_addr.s_addr = reply4->Address;
                    177:         }
                    178:     }
                    179: 
                    180:     if (reply_count == 0) {
                    181:         reply_status = GetLastError();
                    182:     }
                    183: 
                    184:     icmp_type = -1;
                    185:     if (reply_status == IP_SUCCESS) {
                    186:         icmp_type = ICMP_ECHOREPLY;
                    187:     } else if (reply_status == IP_TTL_EXPIRED_TRANSIT
                    188:                || reply_status == IP_TTL_EXPIRED_REASSEM) {
                    189:         icmp_type = ICMP_TIME_EXCEEDED;
                    190:     }
                    191: 
                    192:     if (icmp_type != -1) {
                    193:         /*  Record probe result  */
                    194:         respond_to_probe(net_state, probe, icmp_type,
                    195:                          &remote_addr, round_trip_us, 0, NULL);
                    196:     } else {
                    197:         report_win_error(probe->token, reply_status);
                    198:         free_probe(net_state, probe);
                    199:     }
                    200: }
                    201: 
                    202: /*  Use ICMP.DLL's send echo support to send a probe  */
                    203: static
                    204: void icmp_send_probe(
                    205:     struct net_state_t *net_state,
                    206:     struct probe_t *probe,
                    207:     const struct probe_param_t *param,
                    208:     struct sockaddr_storage *src_sockaddr,
                    209:     struct sockaddr_storage *dest_sockaddr,
                    210:     char *payload,
                    211:     int payload_size)
                    212: {
                    213:     IP_OPTION_INFORMATION option;
                    214:     DWORD timeout;
                    215:     DWORD send_result;
                    216:     int reply_size;
                    217:     int err;
                    218:     struct sockaddr_in *dest_sockaddr4;
                    219:     struct sockaddr_in6 *src_sockaddr6;
                    220:     struct sockaddr_in6 *dest_sockaddr6;
                    221: 
                    222:     if (param->timeout > 0) {
                    223:         timeout = 1000 * param->timeout;
                    224:     } else {
                    225:         /*
                    226:            IcmpSendEcho2 will return invalid argument on a timeout of 
                    227:            zero.  Our Unix implementation allows it.  Bump up the timeout
                    228:            to 1 millisecond.
                    229:          */
                    230:         timeout = 1;
                    231:     }
                    232: 
                    233:     memset(&option, 0, sizeof(IP_OPTION_INFORMATION32));
                    234:     option.Ttl = param->ttl;
                    235: 
                    236:     if (param->ip_version == 6) {
                    237:         reply_size = sizeof(ICMPV6_ECHO_REPLY) + payload_size;
                    238:     } else {
                    239:         reply_size = sizeof(ICMP_ECHO_REPLY32) + payload_size;
                    240:     }
                    241: 
                    242:     probe->platform.reply4 = malloc(reply_size);
                    243:     if (probe->platform.reply4 == NULL) {
                    244:         perror("failure to allocate reply buffer");
                    245:         exit(EXIT_FAILURE);
                    246:     }
                    247: 
                    248:     if (param->ip_version == 6) {
                    249:         src_sockaddr6 = (struct sockaddr_in6 *) src_sockaddr;
                    250:         dest_sockaddr6 = (struct sockaddr_in6 *) dest_sockaddr;
                    251: 
                    252:         send_result = Icmp6SendEcho2(net_state->platform.icmp6, NULL,
                    253:                                      (FARPROC) on_icmp_reply, probe,
                    254:                                      src_sockaddr6, dest_sockaddr6,
                    255:                                      payload, payload_size, &option,
                    256:                                      probe->platform.reply6, reply_size,
                    257:                                      timeout);
                    258:     } else {
                    259:         dest_sockaddr4 = (struct sockaddr_in *) dest_sockaddr;
                    260: 
                    261:         send_result = IcmpSendEcho2(net_state->platform.icmp4, NULL,
                    262:                                     (FARPROC) on_icmp_reply, probe,
                    263:                                     dest_sockaddr4->sin_addr.s_addr,
                    264:                                     payload, payload_size, &option,
                    265:                                     probe->platform.reply4, reply_size,
                    266:                                     timeout);
                    267:     }
                    268: 
                    269:     if (send_result == 0) {
                    270:         err = GetLastError();
                    271: 
                    272:         /*
                    273:            ERROR_IO_PENDING is expected for asynchronous probes,
                    274:            but any other error is unexpected.
                    275:          */
                    276:         if (err != ERROR_IO_PENDING) {
                    277:             report_win_error(probe->token, err);
                    278:             free_probe(net_state, probe);
                    279:         }
                    280:     }
                    281: }
                    282: 
                    283: /*  Fill the payload of the packet as specified by the probe parameters  */
                    284: static
                    285: int fill_payload(
                    286:     const struct probe_param_t *param,
                    287:     char *payload,
                    288:     int payload_buffer_size)
                    289: {
                    290:     int ip_icmp_size;
                    291:     int payload_size;
                    292: 
                    293:     if (param->ip_version == 6) {
                    294:         ip_icmp_size =
                    295:             sizeof(struct IP6Header) + sizeof(struct ICMPHeader);
                    296:     } else if (param->ip_version == 4) {
                    297:         ip_icmp_size = sizeof(struct IPHeader) + sizeof(struct ICMPHeader);
                    298:     } else {
                    299:         errno = EINVAL;
                    300:         return -1;
                    301:     }
                    302: 
                    303:     payload_size = param->packet_size - ip_icmp_size;
                    304:     if (payload_size < 0) {
                    305:         payload_size = 0;
                    306:     }
                    307: 
                    308:     if (payload_size > payload_buffer_size) {
                    309:         errno = EINVAL;
                    310:         return -1;
                    311:     }
                    312: 
                    313:     memset(payload, param->bit_pattern, payload_size);
                    314: 
                    315:     return payload_size;
                    316: }
                    317: 
                    318: /*  Decode the probe parameters and send a probe  */
                    319: void send_probe(
                    320:     struct net_state_t *net_state,
                    321:     const struct probe_param_t *param)
                    322: {
                    323:     struct probe_t *probe;
                    324:     struct sockaddr_storage dest_sockaddr;
                    325:     struct sockaddr_storage src_sockaddr;
                    326:     char payload[PACKET_BUFFER_SIZE];
                    327:     int payload_size;
                    328: 
                    329:     if (resolve_probe_addresses(param, &dest_sockaddr, &src_sockaddr)) {
                    330:         printf("%d invalid-argument\n", param->command_token);
                    331:         return;
                    332:     }
                    333: 
                    334:     probe = alloc_probe(net_state, param->command_token);
                    335:     if (probe == NULL) {
                    336:         printf("%d probes-exhausted\n", param->command_token);
                    337:         return;
                    338:     }
                    339: 
                    340:     probe->platform.ip_version = param->ip_version;
                    341: 
                    342:     payload_size = fill_payload(param, payload, PACKET_BUFFER_SIZE);
                    343:     if (payload_size < 0) {
                    344:         perror("Error construction packet");
                    345:         exit(EXIT_FAILURE);
                    346:     }
                    347: 
                    348:     icmp_send_probe(net_state, probe, param,
                    349:                     &src_sockaddr, &dest_sockaddr, payload, payload_size);
                    350: }
                    351: 
                    352: /*
                    353:     On Windows, an implementation of receive_replies is unnecessary, because,
                    354:     unlike Unix, replies are completed using Overlapped I/O during an
                    355:     alertable wait, and don't require explicit reads.
                    356: */
                    357: void receive_replies(
                    358:     struct net_state_t *net_state)
                    359: {
                    360: }
                    361: 
                    362: /*
                    363:     On Windows, an implementation of check_probe_timeout is unnecesary because
                    364:     timeouts are managed by ICMP.DLL, including a call to the I/O completion
                    365:     routine when the time fully expires.
                    366: */
                    367: void check_probe_timeouts(
                    368:     struct net_state_t *net_state)
                    369: {
                    370: }
                    371: 
                    372: /*
                    373:     As in the case of check_probe_timeout, getting the next probe timeout is
                    374:     unnecessary under Windows, as ICMP.DLL manages timeouts for us.
                    375: */
                    376: bool get_next_probe_timeout(
                    377:     const struct net_state_t *net_state,
                    378:     struct timeval *timeout)
                    379: {
                    380:     return false;
                    381: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>