Annotation of embedaddon/mtr/packet/probe_unix.c, revision 1.1.1.2
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:
21: #include <assert.h>
22: #include <errno.h>
23: #include <fcntl.h>
1.1.1.2 ! misho 24: #ifdef HAVE_ERROR_H
! 25: #include <error.h>
! 26: #else
! 27: #include "portability/error.h"
! 28: #endif
! 29: #ifdef HAVE_LINUX_ERRQUEUE_H
! 30: #include <linux/errqueue.h>
! 31: #endif
1.1 misho 32: #include <stdio.h>
33: #include <stdlib.h>
34: #include <string.h>
35: #include <sys/socket.h>
36: #include <unistd.h>
37:
38: #include "platform.h"
39: #include "protocols.h"
1.1.1.2 ! misho 40: #include "sockaddr.h"
1.1 misho 41: #include "construct_unix.h"
42: #include "deconstruct_unix.h"
43: #include "timeval.h"
44:
45: /* A wrapper around sendto for mixed IPv4 and IPv6 sending */
46: static
47: int send_packet(
48: const struct net_state_t *net_state,
49: const struct probe_param_t *param,
1.1.1.2 ! misho 50: int sequence,
1.1 misho 51: const char *packet,
52: int packet_size,
53: const struct sockaddr_storage *sockaddr)
54: {
1.1.1.2 ! misho 55: struct sockaddr_storage dst;
1.1 misho 56: int send_socket = 0;
57: int sockaddr_length;
58:
1.1.1.2 ! misho 59: memcpy(&dst, sockaddr, sizeof(struct sockaddr_storage));
! 60:
1.1 misho 61: if (sockaddr->ss_family == AF_INET6) {
62: sockaddr_length = sizeof(struct sockaddr_in6);
63:
64: if (param->protocol == IPPROTO_ICMP) {
1.1.1.2 ! misho 65: if (net_state->platform.ip6_socket_raw) {
! 66: send_socket = net_state->platform.icmp6_send_socket;
! 67: } else {
! 68: send_socket = net_state->platform.ip6_txrx_icmp_socket;
! 69: }
1.1 misho 70: } else if (param->protocol == IPPROTO_UDP) {
1.1.1.2 ! misho 71: if (net_state->platform.ip6_socket_raw) {
! 72: send_socket = net_state->platform.udp6_send_socket;
! 73: /* we got a ipv6 udp raw socket
! 74: * the remote port is in the payload
! 75: * we do not set in the sockaddr
! 76: */
! 77: *sockaddr_port_offset(&dst) = 0;
! 78: } else {
! 79: send_socket = net_state->platform.ip6_txrx_udp_socket;
! 80: if (param->dest_port) {
! 81: *sockaddr_port_offset(&dst) = htons(param->dest_port);
! 82: } else {
! 83: *sockaddr_port_offset(&dst) = sequence;
! 84: }
! 85: }
1.1 misho 86: }
87: } else if (sockaddr->ss_family == AF_INET) {
88: sockaddr_length = sizeof(struct sockaddr_in);
89:
1.1.1.2 ! misho 90: if (net_state->platform.ip4_socket_raw) {
! 91: send_socket = net_state->platform.ip4_send_socket;
! 92: } else {
! 93: if (param->protocol == IPPROTO_ICMP) {
! 94: if (param->is_probing_byte_order) {
! 95: send_socket = net_state->platform.ip4_tmp_icmp_socket;;
! 96: } else {
! 97: send_socket = net_state->platform.ip4_txrx_icmp_socket;
! 98: }
! 99: } else if (param->protocol == IPPROTO_UDP) {
! 100: send_socket = net_state->platform.ip4_txrx_udp_socket;
! 101: if (param->dest_port) {
! 102: *sockaddr_port_offset(&dst) = htons(param->dest_port);
! 103: } else {
! 104: *sockaddr_port_offset(&dst) = sequence;
! 105: }
! 106: }
! 107: }
1.1 misho 108: }
109:
110: if (send_socket == 0) {
111: errno = EINVAL;
112: return -1;
113: }
114:
115: return sendto(send_socket, packet, packet_size, 0,
1.1.1.2 ! misho 116: (struct sockaddr *) &dst, sockaddr_length);
1.1 misho 117: }
118:
119: /*
120: Nearly all fields in the IP header should be encoded in network byte
121: order prior to passing to send(). However, the required byte order of
122: the length field of the IP header is inconsistent between operating
123: systems and operating system versions. FreeBSD 11 requires the length
124: field in network byte order, but some older versions of FreeBSD
125: require host byte order. OS X requires the length field in host
126: byte order. Linux will accept either byte order.
127:
128: Test for a byte order which works by sending a ping to localhost.
129: */
130: static
131: void check_length_order(
132: struct net_state_t *net_state)
133: {
134: char packet[PACKET_BUFFER_SIZE];
135: struct probe_param_t param;
1.1.1.2 ! misho 136: struct probe_t p0 = {.sequence = MIN_PORT };
1.1 misho 137: ssize_t bytes_sent;
138: int packet_size;
139:
1.1.1.2 ! misho 140: #ifdef __linux__
! 141: /* Linux will accept either byte order and check below fails to work
! 142: * in some cases due to sendto() returning EPERM. */
! 143: return;
! 144: #endif
! 145:
1.1 misho 146: memset(¶m, 0, sizeof(struct probe_param_t));
147: param.ip_version = 4;
148: param.protocol = IPPROTO_ICMP;
149: param.ttl = 255;
150: param.remote_address = "127.0.0.1";
1.1.1.2 ! misho 151: param.is_probing_byte_order = true;
1.1 misho 152:
1.1.1.2 ! misho 153:
! 154: if (resolve_probe_addresses(net_state, ¶m, &p0.remote_addr,
! 155: &p0.local_addr)) {
! 156: fprintf(stderr, "Error decoding localhost address (%s/%s)\n",
! 157: probe_err, strerror (errno));
1.1 misho 158: exit(EXIT_FAILURE);
159: }
160:
161: /* First attempt to ping the localhost with network byte order */
162: net_state->platform.ip_length_host_order = false;
163:
1.1.1.2 ! misho 164: packet_size = construct_packet(net_state, NULL, &p0,
1.1 misho 165: packet, PACKET_BUFFER_SIZE,
1.1.1.2 ! misho 166: ¶m);
1.1 misho 167: if (packet_size < 0) {
1.1.1.2 ! misho 168: error(EXIT_FAILURE, errno, "Unable to send to localhost");
1.1 misho 169: }
170:
171: bytes_sent =
1.1.1.2 ! misho 172: send_packet(net_state, ¶m, MIN_PORT, packet, packet_size,
! 173: &p0.remote_addr);
1.1 misho 174: if (bytes_sent > 0) {
175: return;
176: }
177:
178: /* Since network byte order failed, try host byte order */
179: net_state->platform.ip_length_host_order = true;
180:
1.1.1.2 ! misho 181: packet_size = construct_packet(net_state, NULL, &p0,
1.1 misho 182: packet, PACKET_BUFFER_SIZE,
1.1.1.2 ! misho 183: ¶m);
1.1 misho 184: if (packet_size < 0) {
1.1.1.2 ! misho 185: error(EXIT_FAILURE, errno, "Unable to send to localhost");
1.1 misho 186: }
187:
188: bytes_sent =
1.1.1.2 ! misho 189: send_packet(net_state, ¶m, MIN_PORT, packet, packet_size,
! 190: &p0.remote_addr);
1.1 misho 191: if (bytes_sent < 0) {
1.1.1.2 ! misho 192: error(EXIT_FAILURE, errno, "Unable to send with swapped length");
1.1 misho 193: }
194: }
195:
196: /*
197: Check to see if SCTP is support. We can't just rely on checking
198: if IPPROTO_SCTP is defined, because while that is necessary,
199: MacOS as of "Sierra" defines IPPROTO_SCTP, but creating an SCTP
200: socket results in an error.
201: */
202: static
203: void check_sctp_support(
204: struct net_state_t *net_state)
205: {
206: #ifdef IPPROTO_SCTP
207: int sctp_socket;
208:
209: sctp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
210: if (sctp_socket != -1) {
211: close(sctp_socket);
212:
213: net_state->platform.sctp_support = true;
214: }
215: #endif
216: }
217:
218: /* Set a socket to non-blocking mode */
219: void set_socket_nonblocking(
220: int socket)
221: {
222: int flags;
223:
224: flags = fcntl(socket, F_GETFL, 0);
225: if (flags == -1) {
1.1.1.2 ! misho 226: error(EXIT_FAILURE, errno, "Unexpected socket F_GETFL error");
1.1 misho 227: }
228:
229: if (fcntl(socket, F_SETFL, flags | O_NONBLOCK)) {
1.1.1.2 ! misho 230: error(EXIT_FAILURE, errno, "Unexpected socket F_SETFL O_NONBLOCK error");
1.1 misho 231: }
232: }
233:
234: /* Open the raw sockets for sending/receiving IPv4 packets */
235: static
1.1.1.2 ! misho 236: int open_ip4_sockets_raw(
1.1 misho 237: struct net_state_t *net_state)
238: {
239: int send_socket;
240: int recv_socket;
241: int trueopt = 1;
242:
243: send_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
244: if (send_socket == -1) {
1.1.1.2 ! misho 245: send_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
! 246: if (send_socket == -1) {
! 247: return -1;
! 248: }
1.1 misho 249: }
250:
251: /*
252: We will be including the IP header in transmitted packets.
253: Linux doesn't require this, but BSD derived network stacks do.
254: */
255: if (setsockopt
256: (send_socket, IPPROTO_IP, IP_HDRINCL, &trueopt, sizeof(int))) {
257:
258: close(send_socket);
259: return -1;
260: }
261:
262: /*
263: Open a second socket with IPPROTO_ICMP because we are only
264: interested in receiving ICMP packets, not all packets.
265: */
266: recv_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
267: if (recv_socket == -1) {
268: close(send_socket);
269: return -1;
270: }
271:
272: net_state->platform.ip4_present = true;
1.1.1.2 ! misho 273: net_state->platform.ip4_socket_raw = true;
1.1 misho 274: net_state->platform.ip4_send_socket = send_socket;
275: net_state->platform.ip4_recv_socket = recv_socket;
276:
277: return 0;
278: }
279:
1.1.1.2 ! misho 280: #ifdef HAVE_LINUX_ERRQUEUE_H
! 281: /* Open DGRAM sockets for sending/receiving IPv4 packets */
! 282: static
! 283: int open_ip4_sockets_dgram(
! 284: struct net_state_t *net_state)
! 285: {
! 286: int udp_socket;
! 287: int icmp_socket, icmp_tmp_socket;
! 288: #ifdef HAVE_LINUX_ERRQUEUE_H
! 289: int val = 1;
! 290: #endif
! 291:
! 292: icmp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
! 293: if (icmp_socket == -1) {
! 294: return -1;
! 295: }
! 296: #ifdef HAVE_LINUX_ERRQUEUE_H
! 297: if (setsockopt(icmp_socket, SOL_IP, IP_RECVERR, &val, sizeof(val)) < 0) {
! 298: return -1;
! 299: }
! 300: #endif
! 301:
! 302: udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
! 303: if (udp_socket == -1) {
! 304: close(icmp_socket);
! 305: return -1;
! 306: }
! 307: #ifdef HAVE_LINUX_ERRQUEUE_H
! 308: if (setsockopt(udp_socket, SOL_IP, IP_RECVERR, &val, sizeof(val)) < 0) {
! 309: close(icmp_socket);
! 310: close(udp_socket);
! 311: return -1;
! 312: }
! 313: #endif
! 314:
! 315: icmp_tmp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
! 316: if (icmp_tmp_socket == -1) {
! 317: close(icmp_socket);
! 318: close(udp_socket);
! 319: return -1;
! 320: }
! 321:
! 322: net_state->platform.ip4_present = true;
! 323: net_state->platform.ip4_socket_raw = false;
! 324: net_state->platform.ip4_txrx_icmp_socket = icmp_socket;
! 325: net_state->platform.ip4_tmp_icmp_socket = icmp_tmp_socket;
! 326: net_state->platform.ip4_txrx_udp_socket = udp_socket;
! 327:
! 328: return 0;
! 329: }
! 330: #endif
! 331:
1.1 misho 332: /* Open the raw sockets for sending/receiving IPv6 packets */
333: static
1.1.1.2 ! misho 334: int open_ip6_sockets_raw(
1.1 misho 335: struct net_state_t *net_state)
336: {
337: int send_socket_icmp;
338: int send_socket_udp;
339: int recv_socket;
340:
341: send_socket_icmp = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
342: if (send_socket_icmp == -1) {
343: return -1;
344: }
345:
346: send_socket_udp = socket(AF_INET6, SOCK_RAW, IPPROTO_UDP);
347: if (send_socket_udp == -1) {
348: close(send_socket_icmp);
349:
350: return -1;
351: }
352:
353: recv_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
354: if (recv_socket == -1) {
355: close(send_socket_icmp);
356: close(send_socket_udp);
357:
358: return -1;
359: }
360:
361: net_state->platform.ip6_present = true;
1.1.1.2 ! misho 362: net_state->platform.ip6_socket_raw = true;
1.1 misho 363: net_state->platform.icmp6_send_socket = send_socket_icmp;
364: net_state->platform.udp6_send_socket = send_socket_udp;
365: net_state->platform.ip6_recv_socket = recv_socket;
366:
367: return 0;
368: }
369:
1.1.1.2 ! misho 370: #ifdef HAVE_LINUX_ERRQUEUE_H
! 371: /* Open DGRAM sockets for sending/receiving IPv6 packets */
! 372: static
! 373: int open_ip6_sockets_dgram(
! 374: struct net_state_t *net_state)
! 375: {
! 376: int icmp_socket;
! 377: int udp_socket;
! 378: #ifdef HAVE_LINUX_ERRQUEUE_H
! 379: int val = 1;
! 380: #endif
! 381:
! 382: icmp_socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
! 383: if (icmp_socket == -1) {
! 384: return -1;
! 385: }
! 386: #ifdef HAVE_LINUX_ERRQUEUE_H
! 387: if (setsockopt(icmp_socket, SOL_IPV6, IPV6_RECVERR, &val, sizeof(val)) < 0) {
! 388: return -1;
! 389: }
! 390: #endif
! 391:
! 392: udp_socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
! 393: if (udp_socket == -1) {
! 394: close(icmp_socket);
! 395: return -1;
! 396: }
! 397: #ifdef HAVE_LINUX_ERRQUEUE_H
! 398: if (setsockopt(udp_socket, SOL_IPV6, IPV6_RECVERR, &val, sizeof(val)) < 0) {
! 399: close(icmp_socket);
! 400: close(udp_socket);
! 401: return -1;
! 402: }
! 403: #endif
! 404:
! 405: net_state->platform.ip6_present = true;
! 406: net_state->platform.ip6_socket_raw = false;
! 407: net_state->platform.ip6_txrx_icmp_socket = icmp_socket;
! 408: net_state->platform.ip6_txrx_udp_socket = udp_socket;
! 409:
! 410: return 0;
! 411: }
! 412: #endif
! 413:
1.1 misho 414: /*
415: The first half of the net state initialization. Since this
416: happens with elevated privileges, this is kept as minimal
417: as possible to minimize security risk.
418: */
419: void init_net_state_privileged(
420: struct net_state_t *net_state)
421: {
422: int ip4_err = 0;
423: int ip6_err = 0;
424:
425: memset(net_state, 0, sizeof(struct net_state_t));
426:
427: net_state->platform.next_sequence = MIN_PORT;
428:
1.1.1.2 ! misho 429: if (open_ip4_sockets_raw(net_state)) {
! 430: #ifdef HAVE_LINUX_ERRQUEUE_H
! 431: /* fall back to using unprivileged sockets */
! 432: if (open_ip4_sockets_dgram(net_state)) {
! 433: ip4_err = errno;
! 434: }
! 435: #endif
1.1 misho 436: }
1.1.1.2 ! misho 437: if (open_ip6_sockets_raw(net_state)) {
! 438: #ifdef HAVE_LINUX_ERRQUEUE_H
! 439: /* fall back to using unprivileged sockets */
! 440: if (open_ip6_sockets_dgram(net_state)) {
! 441: ip6_err = errno;
! 442: }
! 443: #endif
1.1 misho 444: }
445:
446: /*
447: If we couldn't open either IPv4 or IPv6 sockets, we can't do
448: much, so print errors and exit.
449: */
450: if (!net_state->platform.ip4_present
451: && !net_state->platform.ip6_present) {
1.1.1.2 ! misho 452: error(0, ip4_err, "Failure to open IPv4 sockets");
! 453: error(0, ip6_err, "Failure to open IPv6 sockets");
1.1 misho 454: exit(EXIT_FAILURE);
455: }
456: }
457:
458: /*
459: The second half of net state initialization, which is run
460: at normal privilege levels.
461: */
462: void init_net_state(
463: struct net_state_t *net_state)
464: {
1.1.1.2 ! misho 465: if (net_state->platform.ip4_socket_raw) {
! 466: set_socket_nonblocking(net_state->platform.ip4_recv_socket);
! 467: } else {
! 468: set_socket_nonblocking(net_state->platform.ip4_txrx_icmp_socket);
! 469: set_socket_nonblocking(net_state->platform.ip4_txrx_udp_socket);
! 470: }
! 471: if (net_state->platform.ip6_socket_raw) {
! 472: set_socket_nonblocking(net_state->platform.ip6_recv_socket);
! 473: } else {
! 474: set_socket_nonblocking(net_state->platform.ip6_txrx_icmp_socket);
! 475: set_socket_nonblocking(net_state->platform.ip6_txrx_udp_socket);
! 476: }
1.1 misho 477:
478: if (net_state->platform.ip4_present) {
479: check_length_order(net_state);
480: }
481:
482: check_sctp_support(net_state);
483: }
484:
485: /*
486: Returns true if we were able to open sockets for a particular
487: IP protocol version.
488: */
489: bool is_ip_version_supported(
490: struct net_state_t *net_state,
491: int ip_version)
492: {
493: if (ip_version == 4) {
494: return net_state->platform.ip4_present;
495: } else if (ip_version == 6) {
496: return net_state->platform.ip6_present;
497: } else {
498: return false;
499: }
500: }
501:
502: /* Returns true if we can transmit probes using the specified protocol */
503: bool is_protocol_supported(
504: struct net_state_t * net_state,
505: int protocol)
506: {
507: if (protocol == IPPROTO_ICMP) {
508: return true;
509: }
510:
511: if (protocol == IPPROTO_UDP) {
512: return true;
513: }
514:
515: if (protocol == IPPROTO_TCP) {
516: return true;
517: }
518: #ifdef IPPROTO_SCTP
519: if (protocol == IPPROTO_SCTP) {
520: return net_state->platform.sctp_support;
521: }
522: #endif
523:
524: return false;
525: }
526:
527: /* Report an error during send_probe based on the errno value */
528: static
529: void report_packet_error(
530: int command_token)
531: {
532: if (errno == EINVAL) {
533: printf("%d invalid-argument\n", command_token);
534: } else if (errno == ENETDOWN) {
535: printf("%d network-down\n", command_token);
536: } else if (errno == ENETUNREACH) {
537: printf("%d no-route\n", command_token);
538: } else if (errno == EHOSTUNREACH) {
539: printf("%d no-route\n", command_token);
540: } else if (errno == EPERM) {
541: printf("%d permission-denied\n", command_token);
542: } else if (errno == EADDRINUSE) {
543: printf("%d address-in-use\n", command_token);
544: } else if (errno == EADDRNOTAVAIL) {
545: printf("%d address-not-available\n", command_token);
546: } else {
547: printf("%d unexpected-error errno %d\n", command_token, errno);
548: }
549: }
550:
551: /* Craft a custom ICMP packet for a network probe. */
552: void send_probe(
553: struct net_state_t *net_state,
554: const struct probe_param_t *param)
555: {
556: char packet[PACKET_BUFFER_SIZE];
557: struct probe_t *probe;
1.1.1.2 ! misho 558: int trytimes;
1.1 misho 559: int packet_size;
560:
561: probe = alloc_probe(net_state, param->command_token);
562: if (probe == NULL) {
563: printf("%d probes-exhausted\n", param->command_token);
564: return;
565: }
566:
1.1.1.2 ! misho 567: if (resolve_probe_addresses(net_state, param, &probe->remote_addr,
! 568: &probe->local_addr)) {
1.1 misho 569: printf("%d invalid-argument\n", param->command_token);
570: free_probe(net_state, probe);
571: return;
572: }
573:
574: if (gettimeofday(&probe->platform.departure_time, NULL)) {
1.1.1.2 ! misho 575: error(EXIT_FAILURE, errno, "gettimeofday failure");
1.1 misho 576: }
577:
1.1.1.2 ! misho 578: // there might be an off-by-one in the number of tries here.
! 579: // this is intentional. It is no use exhausting the very last
! 580: // open port. Max 10 retries would've been acceptable too I think.
! 581: for (trytimes=MIN_PORT; trytimes < MAX_PORT; trytimes++) {
! 582:
! 583: packet_size = construct_packet(net_state, &probe->platform.socket,
! 584: probe, packet, PACKET_BUFFER_SIZE,
! 585: param);
! 586:
! 587: if (packet_size > 0) break; // no retry if we succeed.
! 588:
! 589: if ((param->protocol != IPPROTO_TCP) &&
! 590: (param->protocol != IPPROTO_SCTP)) break; // no retry if not TCP/SCTP
! 591:
! 592: if ((errno != EADDRINUSE) && (errno != EADDRNOTAVAIL)) {
! 593: break; // no retry
! 594: }
! 595:
! 596: probe->sequence = net_state->platform.next_sequence++;
! 597:
! 598: if (net_state->platform.next_sequence > MAX_PORT) {
! 599: net_state->platform.next_sequence = MIN_PORT;
! 600: }
! 601: }
1.1 misho 602:
603: if (packet_size < 0) {
604: /*
605: When using a stream protocol, FreeBSD will return ECONNREFUSED
606: when connecting to localhost if the port doesn't exist,
607: even if the socket is non-blocking, so we should be
608: prepared for that.
609: */
610: if (errno == ECONNREFUSED) {
611: receive_probe(net_state, probe, ICMP_ECHOREPLY,
612: &probe->remote_addr, NULL, 0, NULL);
613: } else {
614: report_packet_error(param->command_token);
615: free_probe(net_state, probe);
616: }
617:
618: return;
619: }
620:
621: if (packet_size > 0) {
1.1.1.2 ! misho 622: if (send_packet(net_state, param, probe->sequence,
1.1 misho 623: packet, packet_size, &probe->remote_addr) == -1) {
624:
625: report_packet_error(param->command_token);
626: free_probe(net_state, probe);
627: return;
628: }
629: }
630:
631: probe->platform.timeout_time = probe->platform.departure_time;
632: probe->platform.timeout_time.tv_sec += param->timeout;
633: }
634:
635: /* When allocating a probe, assign it a unique port number */
636: void platform_alloc_probe(
637: struct net_state_t *net_state,
638: struct probe_t *probe)
639: {
640: probe->sequence = net_state->platform.next_sequence++;
641:
642: if (net_state->platform.next_sequence > MAX_PORT) {
643: net_state->platform.next_sequence = MIN_PORT;
644: }
645: }
646:
647: /*
648: When freeing the probe, close the socket for the probe,
649: if one has been opened
650: */
651: void platform_free_probe(
652: struct probe_t *probe)
653: {
654: if (probe->platform.socket) {
655: close(probe->platform.socket);
656: probe->platform.socket = 0;
657: }
658: }
659:
660: /*
661: Compute the round trip time of a just-received probe and pass it
662: to the platform agnostic response handling.
663: */
664: void receive_probe(
665: struct net_state_t *net_state,
666: struct probe_t *probe,
667: int icmp_type,
668: const struct sockaddr_storage *remote_addr,
669: struct timeval *timestamp,
670: int mpls_count,
671: struct mpls_label_t *mpls)
672: {
673: unsigned int round_trip_us;
674: struct timeval *departure_time = &probe->platform.departure_time;
675: struct timeval now;
676:
677: if (timestamp == NULL) {
678: if (gettimeofday(&now, NULL)) {
1.1.1.2 ! misho 679: error(EXIT_FAILURE, errno, "gettimeofday failure");
1.1 misho 680: }
681:
682: timestamp = &now;
683: }
684:
685: round_trip_us =
686: (timestamp->tv_sec - departure_time->tv_sec) * 1000000 +
687: timestamp->tv_usec - departure_time->tv_usec;
688:
689: respond_to_probe(net_state, probe, icmp_type,
690: remote_addr, round_trip_us, mpls_count, mpls);
691: }
692:
693: /*
694: Read all available packets through our receiving raw socket, and
1.1.1.2 ! misho 695: handle any responses to probes we have previously sent.
1.1 misho 696: */
697: static
1.1.1.2 ! misho 698: void receive_replies_from_recv_socket(
1.1 misho 699: struct net_state_t *net_state,
700: int socket,
701: received_packet_func_t handle_received_packet)
702: {
703: char packet[PACKET_BUFFER_SIZE];
704: int packet_length;
705: struct sockaddr_storage remote_addr;
706: struct timeval timestamp;
1.1.1.2 ! misho 707: int flag = 0;
! 708: #ifdef HAVE_LINUX_ERRQUEUE_H
! 709: struct cmsghdr *cm;
! 710: struct sock_extended_err *ee = NULL;
! 711: bool icmp_connrefused_received = false;
! 712: bool icmp_hostunreach_received = false;
! 713: #endif
1.1 misho 714:
715: /* Read until no more packets are available */
716: while (true) {
1.1.1.2 ! misho 717: struct iovec iov;
! 718: struct msghdr msg;
! 719: char control[1024];
! 720:
! 721: memset(&msg, 0, sizeof(msg));
! 722: memset(&iov, 0, sizeof(iov));
! 723: iov.iov_base = packet;
! 724: iov.iov_len = sizeof(packet);
! 725: msg.msg_iov = &iov;
! 726: msg.msg_iovlen = 1;
! 727: msg.msg_name = (struct sockaddr*) &remote_addr;
! 728: msg.msg_namelen = sizeof(remote_addr);
! 729: msg.msg_control = control;
! 730: msg.msg_controllen = sizeof(control);
! 731: packet_length = recvmsg(socket, &msg, flag);
1.1 misho 732:
733: /*
734: Get the time immediately after reading the packet to
735: keep the timing as precise as we can.
736: */
737: if (gettimeofday(×tamp, NULL)) {
1.1.1.2 ! misho 738: error(EXIT_FAILURE, errno, "gettimeofday failure");
1.1 misho 739: }
740:
741: if (packet_length == -1) {
742: /*
743: EAGAIN will be returned if there is no current packet
744: available.
745: */
746: if (errno == EAGAIN) {
747: return;
748: }
749:
750: /*
751: EINTER will be returned if we received a signal during
752: receive.
753: */
754: if (errno == EINTR) {
1.1.1.2 ! misho 755: /* clear error */
! 756: int so_err;
! 757: socklen_t so_err_size = sizeof(so_err);
! 758: int err;
! 759:
! 760: do {
! 761: err = getsockopt(socket, SOL_SOCKET, SO_ERROR, &so_err, &so_err_size);
! 762: } while (err < 0 && errno == EINTR);
1.1 misho 763: continue;
764: }
765:
1.1.1.2 ! misho 766: /* handle error received in error queue */
! 767: if (errno == EHOSTUNREACH) {
! 768: /* potential error caused by ttl, read inner icmp hdr from err queue */
! 769: #ifdef HAVE_LINUX_ERRQUEUE_H
! 770: icmp_hostunreach_received = true;
! 771: flag |= MSG_ERRQUEUE;
! 772: #endif
! 773: continue;
! 774: }
! 775:
! 776: if (errno == ECONNREFUSED) {
! 777: /* udp packet reached dst, read inner udp hdr from err queue */
! 778: #ifdef HAVE_LINUX_ERRQUEUE_H
! 779: icmp_connrefused_received = true;
! 780: flag |= MSG_ERRQUEUE;
! 781: #endif
! 782: continue;
! 783: }
! 784:
! 785: error(EXIT_FAILURE, errno, "Failure receiving replies");
1.1 misho 786: }
787:
1.1.1.2 ! misho 788: #ifdef HAVE_LINUX_ERRQUEUE_H
! 789: /* get src ip for packets read from err queue */
! 790: if (flag & MSG_ERRQUEUE) {
! 791: for (cm = CMSG_FIRSTHDR(&msg); cm; cm = CMSG_NXTHDR(&msg, cm)) {
! 792: if (cm->cmsg_level == SOL_IP) {
! 793: if (cm->cmsg_type == IP_RECVERR) {
! 794: ee = (struct sock_extended_err *) CMSG_DATA(cm);
! 795: }
! 796: }
! 797: else if (cm->cmsg_level == SOL_IPV6) {
! 798: if (cm->cmsg_type == IPV6_RECVERR) {
! 799: ee = (struct sock_extended_err *) CMSG_DATA(cm);
! 800: }
! 801: }
! 802: }
! 803: if (ee) {
! 804: memcpy(&remote_addr, SO_EE_OFFENDER(ee), sizeof(remote_addr));
! 805: }
! 806: }
! 807:
! 808: #ifdef SO_PROTOCOL
! 809: if (icmp_connrefused_received) {
! 810: /* using ICMP type ICMP_ECHOREPLY is not a bug, it is an
! 811: indication of successfully reaching dst host.
! 812: */
! 813: handle_error_queue_packet(net_state, &remote_addr, ICMP_ECHOREPLY, IPPROTO_UDP,
! 814: packet, packet_length, ×tamp);
! 815: } else if (icmp_hostunreach_received) {
! 816: /* handle packet based on send socket protocol */
! 817: int proto, length = sizeof(int);
! 818:
! 819: if (getsockopt(socket, SOL_SOCKET, SO_PROTOCOL, &proto, &length) < 0) {
! 820: error(EXIT_FAILURE, errno, "getsockopt SO_PROTOCOL error");
! 821: }
! 822: handle_error_queue_packet(net_state, &remote_addr, ICMP_TIME_EXCEEDED, proto,
! 823: packet, packet_length, ×tamp);
! 824: } else {
! 825: #endif
! 826: #endif
! 827: /* ICMP packets received from raw socket */
! 828: handle_received_packet(net_state, &remote_addr, packet,
! 829: packet_length, ×tamp);
! 830: #ifdef HAVE_LINUX_ERRQUEUE_H
! 831: #ifdef SO_PROTOCOL
! 832: }
! 833: #endif
! 834: #endif
1.1 misho 835: }
836: }
837:
838: /*
839: Attempt to send using the probe's socket, in order to check whether
840: the connection has completed, for stream oriented protocols such as
841: TCP.
842: */
843: static
844: void receive_replies_from_probe_socket(
845: struct net_state_t *net_state,
846: struct probe_t *probe)
847: {
848: int probe_socket;
849: struct timeval zero_time;
850: int err;
851: int err_length = sizeof(int);
852: fd_set write_set;
853:
854: probe_socket = probe->platform.socket;
855: if (!probe_socket) {
856: return;
857: }
858:
859: FD_ZERO(&write_set);
860: FD_SET(probe_socket, &write_set);
861:
862: zero_time.tv_sec = 0;
863: zero_time.tv_usec = 0;
864:
865: if (select(probe_socket + 1, NULL, &write_set, NULL, &zero_time) == -1) {
866: if (errno == EAGAIN) {
867: return;
868: } else {
1.1.1.2 ! misho 869: error(EXIT_FAILURE, errno, "probe socket select error");
1.1 misho 870: }
871: }
872:
873: /*
874: If the socket is writable, the connection attempt has completed.
875: */
876: if (!FD_ISSET(probe_socket, &write_set)) {
877: return;
878: }
879:
880: if (getsockopt(probe_socket, SOL_SOCKET, SO_ERROR, &err, &err_length)) {
1.1.1.2 ! misho 881: error(EXIT_FAILURE, errno, "probe socket SO_ERROR");
1.1 misho 882: }
883:
884: /*
885: If the connection complete successfully, or was refused, we can
886: assume our probe arrived at the destination.
887: */
888: if (!err || err == ECONNREFUSED) {
889: receive_probe(net_state, probe, ICMP_ECHOREPLY,
890: &probe->remote_addr, NULL, 0, NULL);
891: } else {
892: errno = err;
893: report_packet_error(probe->token);
894: free_probe(net_state, probe);
895: }
896: }
897:
898: /* Check both the IPv4 and IPv6 sockets for incoming packets */
899: void receive_replies(
900: struct net_state_t *net_state)
901: {
902: struct probe_t *probe;
903: struct probe_t *probe_safe_iter;
904:
905: if (net_state->platform.ip4_present) {
1.1.1.2 ! misho 906: if (net_state->platform.ip4_socket_raw) {
! 907: receive_replies_from_recv_socket(net_state,
! 908: net_state->platform.
! 909: ip4_recv_socket,
! 910: handle_received_ip4_packet);
! 911: } else {
! 912: receive_replies_from_recv_socket(net_state,
! 913: net_state->platform.
! 914: ip4_txrx_icmp_socket,
! 915: handle_received_ip4_packet);
! 916: receive_replies_from_recv_socket(net_state,
! 917: net_state->platform.
! 918: ip4_txrx_udp_socket,
! 919: handle_received_ip4_packet);
! 920: }
1.1 misho 921: }
922:
923: if (net_state->platform.ip6_present) {
1.1.1.2 ! misho 924: if (net_state->platform.ip6_socket_raw) {
! 925: receive_replies_from_recv_socket(net_state,
! 926: net_state->platform.
! 927: ip6_recv_socket,
! 928: handle_received_ip6_packet);
! 929: } else {
! 930: receive_replies_from_recv_socket(net_state,
! 931: net_state->platform.
! 932: ip6_txrx_icmp_socket,
! 933: handle_received_ip6_packet);
! 934: receive_replies_from_recv_socket(net_state,
! 935: net_state->platform.
! 936: ip6_txrx_udp_socket,
! 937: handle_received_ip6_packet);
! 938: }
1.1 misho 939: }
940:
941: LIST_FOREACH_SAFE(probe, &net_state->outstanding_probes,
942: probe_list_entry, probe_safe_iter) {
943:
944: receive_replies_from_probe_socket(net_state, probe);
945: }
946: }
947:
948: /*
949: Put all of our probe sockets in the read set used for an upcoming
950: select so we can wake when any of them become readable.
951: */
952: int gather_probe_sockets(
953: const struct net_state_t *net_state,
954: fd_set * write_set)
955: {
956: int probe_socket;
957: int nfds;
958: const struct probe_t *probe;
959:
960: nfds = 0;
961:
962: LIST_FOREACH(probe, &net_state->outstanding_probes, probe_list_entry) {
963: probe_socket = probe->platform.socket;
964:
965: if (probe_socket) {
966: FD_SET(probe_socket, write_set);
967: if (probe_socket >= nfds) {
968: nfds = probe_socket + 1;
969: }
970: }
971: }
972:
973: return nfds;
974: }
975:
976: /*
977: Check for any probes for which we have not received a response
978: for some time, and report a time-out, assuming that we won't
979: receive a future reply.
980: */
981: void check_probe_timeouts(
982: struct net_state_t *net_state)
983: {
984: struct timeval now;
985: struct probe_t *probe;
986: struct probe_t *probe_safe_iter;
987:
988: if (gettimeofday(&now, NULL)) {
1.1.1.2 ! misho 989: error(EXIT_FAILURE, errno, "gettimeofday failure");
1.1 misho 990: }
991:
992: LIST_FOREACH_SAFE(probe, &net_state->outstanding_probes,
993: probe_list_entry, probe_safe_iter) {
994:
995: if (compare_timeval(probe->platform.timeout_time, now) < 0) {
996: /* Report timeout to the command stream */
997: printf("%d no-reply\n", probe->token);
998:
999: free_probe(net_state, probe);
1000: }
1001: }
1002: }
1003:
1004: /*
1005: Find the remaining time until the next probe times out.
1006: This may be a negative value if the next probe timeout has
1007: already elapsed.
1008:
1009: Returns false if no probes are currently outstanding, and true
1010: if a timeout value for the next probe exists.
1011: */
1012: bool get_next_probe_timeout(
1013: const struct net_state_t *net_state,
1014: struct timeval *timeout)
1015: {
1016: bool have_timeout;
1017: const struct probe_t *probe;
1018: struct timeval now;
1019: struct timeval probe_timeout;
1020:
1021: if (gettimeofday(&now, NULL)) {
1.1.1.2 ! misho 1022: error(EXIT_FAILURE, errno, "gettimeofday failure");
1.1 misho 1023: }
1024:
1025: have_timeout = false;
1026: LIST_FOREACH(probe, &net_state->outstanding_probes, probe_list_entry) {
1027: probe_timeout.tv_sec =
1028: probe->platform.timeout_time.tv_sec - now.tv_sec;
1029: probe_timeout.tv_usec =
1030: probe->platform.timeout_time.tv_usec - now.tv_usec;
1031:
1032: normalize_timeval(&probe_timeout);
1033: if (have_timeout) {
1034: if (compare_timeval(probe_timeout, *timeout) < 0) {
1035: /* If this probe has a sooner timeout, store it instead */
1036: *timeout = probe_timeout;
1037: }
1038: } else {
1039: *timeout = probe_timeout;
1040: have_timeout = true;
1041: }
1042: }
1043:
1044: return have_timeout;
1045: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>