Annotation of embedaddon/mtr/packet/probe_cygwin.c, revision 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>