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>