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>