Annotation of embedaddon/mtr/packet/probe_unix.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 <assert.h>
! 22: #include <errno.h>
! 23: #include <fcntl.h>
! 24: #include <stdio.h>
! 25: #include <stdlib.h>
! 26: #include <string.h>
! 27: #include <sys/socket.h>
! 28: #include <unistd.h>
! 29:
! 30: #include "platform.h"
! 31: #include "protocols.h"
! 32: #include "construct_unix.h"
! 33: #include "deconstruct_unix.h"
! 34: #include "timeval.h"
! 35:
! 36: /* A wrapper around sendto for mixed IPv4 and IPv6 sending */
! 37: static
! 38: int send_packet(
! 39: const struct net_state_t *net_state,
! 40: const struct probe_param_t *param,
! 41: const char *packet,
! 42: int packet_size,
! 43: const struct sockaddr_storage *sockaddr)
! 44: {
! 45: int send_socket = 0;
! 46: int sockaddr_length;
! 47:
! 48: if (sockaddr->ss_family == AF_INET6) {
! 49: sockaddr_length = sizeof(struct sockaddr_in6);
! 50:
! 51: if (param->protocol == IPPROTO_ICMP) {
! 52: send_socket = net_state->platform.icmp6_send_socket;
! 53: } else if (param->protocol == IPPROTO_UDP) {
! 54: send_socket = net_state->platform.udp6_send_socket;
! 55: }
! 56: } else if (sockaddr->ss_family == AF_INET) {
! 57: sockaddr_length = sizeof(struct sockaddr_in);
! 58:
! 59: send_socket = net_state->platform.ip4_send_socket;
! 60: }
! 61:
! 62: if (send_socket == 0) {
! 63: errno = EINVAL;
! 64: return -1;
! 65: }
! 66:
! 67: return sendto(send_socket, packet, packet_size, 0,
! 68: (struct sockaddr *) sockaddr, sockaddr_length);
! 69: }
! 70:
! 71: /*
! 72: Nearly all fields in the IP header should be encoded in network byte
! 73: order prior to passing to send(). However, the required byte order of
! 74: the length field of the IP header is inconsistent between operating
! 75: systems and operating system versions. FreeBSD 11 requires the length
! 76: field in network byte order, but some older versions of FreeBSD
! 77: require host byte order. OS X requires the length field in host
! 78: byte order. Linux will accept either byte order.
! 79:
! 80: Test for a byte order which works by sending a ping to localhost.
! 81: */
! 82: static
! 83: void check_length_order(
! 84: struct net_state_t *net_state)
! 85: {
! 86: char packet[PACKET_BUFFER_SIZE];
! 87: struct probe_param_t param;
! 88: struct sockaddr_storage dest_sockaddr;
! 89: struct sockaddr_storage src_sockaddr;
! 90: ssize_t bytes_sent;
! 91: int packet_size;
! 92:
! 93: memset(¶m, 0, sizeof(struct probe_param_t));
! 94: param.ip_version = 4;
! 95: param.protocol = IPPROTO_ICMP;
! 96: param.ttl = 255;
! 97: param.remote_address = "127.0.0.1";
! 98:
! 99: if (resolve_probe_addresses(¶m, &dest_sockaddr, &src_sockaddr)) {
! 100: fprintf(stderr, "Error decoding localhost address\n");
! 101: exit(EXIT_FAILURE);
! 102: }
! 103:
! 104: /* First attempt to ping the localhost with network byte order */
! 105: net_state->platform.ip_length_host_order = false;
! 106:
! 107: packet_size = construct_packet(net_state, NULL, MIN_PORT,
! 108: packet, PACKET_BUFFER_SIZE,
! 109: &dest_sockaddr, &src_sockaddr, ¶m);
! 110: if (packet_size < 0) {
! 111: perror("Unable to send to localhost");
! 112: exit(EXIT_FAILURE);
! 113: }
! 114:
! 115: bytes_sent =
! 116: send_packet(net_state, ¶m, packet, packet_size,
! 117: &dest_sockaddr);
! 118: if (bytes_sent > 0) {
! 119: return;
! 120: }
! 121:
! 122: /* Since network byte order failed, try host byte order */
! 123: net_state->platform.ip_length_host_order = true;
! 124:
! 125: packet_size = construct_packet(net_state, NULL, MIN_PORT,
! 126: packet, PACKET_BUFFER_SIZE,
! 127: &dest_sockaddr, &src_sockaddr, ¶m);
! 128: if (packet_size < 0) {
! 129: perror("Unable to send to localhost");
! 130: exit(EXIT_FAILURE);
! 131: }
! 132:
! 133: bytes_sent =
! 134: send_packet(net_state, ¶m, packet, packet_size,
! 135: &dest_sockaddr);
! 136: if (bytes_sent < 0) {
! 137: perror("Unable to send with swapped length");
! 138: exit(EXIT_FAILURE);
! 139: }
! 140: }
! 141:
! 142: /*
! 143: Check to see if SCTP is support. We can't just rely on checking
! 144: if IPPROTO_SCTP is defined, because while that is necessary,
! 145: MacOS as of "Sierra" defines IPPROTO_SCTP, but creating an SCTP
! 146: socket results in an error.
! 147: */
! 148: static
! 149: void check_sctp_support(
! 150: struct net_state_t *net_state)
! 151: {
! 152: #ifdef IPPROTO_SCTP
! 153: int sctp_socket;
! 154:
! 155: sctp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
! 156: if (sctp_socket != -1) {
! 157: close(sctp_socket);
! 158:
! 159: net_state->platform.sctp_support = true;
! 160: }
! 161: #endif
! 162: }
! 163:
! 164: /* Set a socket to non-blocking mode */
! 165: void set_socket_nonblocking(
! 166: int socket)
! 167: {
! 168: int flags;
! 169:
! 170: flags = fcntl(socket, F_GETFL, 0);
! 171: if (flags == -1) {
! 172: perror("Unexpected socket F_GETFL error");
! 173: exit(EXIT_FAILURE);
! 174: }
! 175:
! 176: if (fcntl(socket, F_SETFL, flags | O_NONBLOCK)) {
! 177: perror("Unexpected socket F_SETFL O_NONBLOCK error");
! 178: exit(EXIT_FAILURE);
! 179: }
! 180: }
! 181:
! 182: /* Open the raw sockets for sending/receiving IPv4 packets */
! 183: static
! 184: int open_ip4_sockets(
! 185: struct net_state_t *net_state)
! 186: {
! 187: int send_socket;
! 188: int recv_socket;
! 189: int trueopt = 1;
! 190:
! 191: send_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
! 192: if (send_socket == -1) {
! 193: return -1;
! 194: }
! 195:
! 196: /*
! 197: We will be including the IP header in transmitted packets.
! 198: Linux doesn't require this, but BSD derived network stacks do.
! 199: */
! 200: if (setsockopt
! 201: (send_socket, IPPROTO_IP, IP_HDRINCL, &trueopt, sizeof(int))) {
! 202:
! 203: close(send_socket);
! 204: return -1;
! 205: }
! 206:
! 207: /*
! 208: Open a second socket with IPPROTO_ICMP because we are only
! 209: interested in receiving ICMP packets, not all packets.
! 210: */
! 211: recv_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
! 212: if (recv_socket == -1) {
! 213: close(send_socket);
! 214: return -1;
! 215: }
! 216:
! 217: net_state->platform.ip4_present = true;
! 218: net_state->platform.ip4_send_socket = send_socket;
! 219: net_state->platform.ip4_recv_socket = recv_socket;
! 220:
! 221: return 0;
! 222: }
! 223:
! 224: /* Open the raw sockets for sending/receiving IPv6 packets */
! 225: static
! 226: int open_ip6_sockets(
! 227: struct net_state_t *net_state)
! 228: {
! 229: int send_socket_icmp;
! 230: int send_socket_udp;
! 231: int recv_socket;
! 232:
! 233: send_socket_icmp = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
! 234: if (send_socket_icmp == -1) {
! 235: return -1;
! 236: }
! 237:
! 238: send_socket_udp = socket(AF_INET6, SOCK_RAW, IPPROTO_UDP);
! 239: if (send_socket_udp == -1) {
! 240: close(send_socket_icmp);
! 241:
! 242: return -1;
! 243: }
! 244:
! 245: recv_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
! 246: if (recv_socket == -1) {
! 247: close(send_socket_icmp);
! 248: close(send_socket_udp);
! 249:
! 250: return -1;
! 251: }
! 252:
! 253: net_state->platform.ip6_present = true;
! 254: net_state->platform.icmp6_send_socket = send_socket_icmp;
! 255: net_state->platform.udp6_send_socket = send_socket_udp;
! 256: net_state->platform.ip6_recv_socket = recv_socket;
! 257:
! 258: return 0;
! 259: }
! 260:
! 261: /*
! 262: The first half of the net state initialization. Since this
! 263: happens with elevated privileges, this is kept as minimal
! 264: as possible to minimize security risk.
! 265: */
! 266: void init_net_state_privileged(
! 267: struct net_state_t *net_state)
! 268: {
! 269: int ip4_err = 0;
! 270: int ip6_err = 0;
! 271:
! 272: memset(net_state, 0, sizeof(struct net_state_t));
! 273:
! 274: net_state->platform.next_sequence = MIN_PORT;
! 275:
! 276: if (open_ip4_sockets(net_state)) {
! 277: ip4_err = errno;
! 278: }
! 279: if (open_ip6_sockets(net_state)) {
! 280: ip6_err = errno;
! 281: }
! 282:
! 283: /*
! 284: If we couldn't open either IPv4 or IPv6 sockets, we can't do
! 285: much, so print errors and exit.
! 286: */
! 287: if (!net_state->platform.ip4_present
! 288: && !net_state->platform.ip6_present) {
! 289:
! 290: errno = ip4_err;
! 291: perror("Failure to open IPv4 sockets");
! 292:
! 293: errno = ip6_err;
! 294: perror("Failure to open IPv6 sockets");
! 295:
! 296: exit(EXIT_FAILURE);
! 297: }
! 298: }
! 299:
! 300: /*
! 301: The second half of net state initialization, which is run
! 302: at normal privilege levels.
! 303: */
! 304: void init_net_state(
! 305: struct net_state_t *net_state)
! 306: {
! 307: set_socket_nonblocking(net_state->platform.ip4_recv_socket);
! 308: set_socket_nonblocking(net_state->platform.ip6_recv_socket);
! 309:
! 310: if (net_state->platform.ip4_present) {
! 311: check_length_order(net_state);
! 312: }
! 313:
! 314: check_sctp_support(net_state);
! 315: }
! 316:
! 317: /*
! 318: Returns true if we were able to open sockets for a particular
! 319: IP protocol version.
! 320: */
! 321: bool is_ip_version_supported(
! 322: struct net_state_t *net_state,
! 323: int ip_version)
! 324: {
! 325: if (ip_version == 4) {
! 326: return net_state->platform.ip4_present;
! 327: } else if (ip_version == 6) {
! 328: return net_state->platform.ip6_present;
! 329: } else {
! 330: return false;
! 331: }
! 332: }
! 333:
! 334: /* Returns true if we can transmit probes using the specified protocol */
! 335: bool is_protocol_supported(
! 336: struct net_state_t * net_state,
! 337: int protocol)
! 338: {
! 339: if (protocol == IPPROTO_ICMP) {
! 340: return true;
! 341: }
! 342:
! 343: if (protocol == IPPROTO_UDP) {
! 344: return true;
! 345: }
! 346:
! 347: if (protocol == IPPROTO_TCP) {
! 348: return true;
! 349: }
! 350: #ifdef IPPROTO_SCTP
! 351: if (protocol == IPPROTO_SCTP) {
! 352: return net_state->platform.sctp_support;
! 353: }
! 354: #endif
! 355:
! 356: return false;
! 357: }
! 358:
! 359: /* Report an error during send_probe based on the errno value */
! 360: static
! 361: void report_packet_error(
! 362: int command_token)
! 363: {
! 364: if (errno == EINVAL) {
! 365: printf("%d invalid-argument\n", command_token);
! 366: } else if (errno == ENETDOWN) {
! 367: printf("%d network-down\n", command_token);
! 368: } else if (errno == ENETUNREACH) {
! 369: printf("%d no-route\n", command_token);
! 370: } else if (errno == EHOSTUNREACH) {
! 371: printf("%d no-route\n", command_token);
! 372: } else if (errno == EPERM) {
! 373: printf("%d permission-denied\n", command_token);
! 374: } else if (errno == EADDRINUSE) {
! 375: printf("%d address-in-use\n", command_token);
! 376: } else if (errno == EADDRNOTAVAIL) {
! 377: printf("%d address-not-available\n", command_token);
! 378: } else {
! 379: printf("%d unexpected-error errno %d\n", command_token, errno);
! 380: }
! 381: }
! 382:
! 383: /* Craft a custom ICMP packet for a network probe. */
! 384: void send_probe(
! 385: struct net_state_t *net_state,
! 386: const struct probe_param_t *param)
! 387: {
! 388: char packet[PACKET_BUFFER_SIZE];
! 389: struct probe_t *probe;
! 390: int packet_size;
! 391: struct sockaddr_storage src_sockaddr;
! 392:
! 393: probe = alloc_probe(net_state, param->command_token);
! 394: if (probe == NULL) {
! 395: printf("%d probes-exhausted\n", param->command_token);
! 396: return;
! 397: }
! 398:
! 399: if (resolve_probe_addresses(param, &probe->remote_addr, &src_sockaddr)) {
! 400: printf("%d invalid-argument\n", param->command_token);
! 401: free_probe(net_state, probe);
! 402: return;
! 403: }
! 404:
! 405: if (gettimeofday(&probe->platform.departure_time, NULL)) {
! 406: perror("gettimeofday failure");
! 407: exit(EXIT_FAILURE);
! 408: }
! 409:
! 410: packet_size =
! 411: construct_packet(net_state, &probe->platform.socket,
! 412: probe->sequence, packet, PACKET_BUFFER_SIZE,
! 413: &probe->remote_addr, &src_sockaddr, param);
! 414:
! 415: if (packet_size < 0) {
! 416: /*
! 417: When using a stream protocol, FreeBSD will return ECONNREFUSED
! 418: when connecting to localhost if the port doesn't exist,
! 419: even if the socket is non-blocking, so we should be
! 420: prepared for that.
! 421: */
! 422: if (errno == ECONNREFUSED) {
! 423: receive_probe(net_state, probe, ICMP_ECHOREPLY,
! 424: &probe->remote_addr, NULL, 0, NULL);
! 425: } else {
! 426: report_packet_error(param->command_token);
! 427: free_probe(net_state, probe);
! 428: }
! 429:
! 430: return;
! 431: }
! 432:
! 433: if (packet_size > 0) {
! 434: if (send_packet(net_state, param,
! 435: packet, packet_size, &probe->remote_addr) == -1) {
! 436:
! 437: report_packet_error(param->command_token);
! 438: free_probe(net_state, probe);
! 439: return;
! 440: }
! 441: }
! 442:
! 443: probe->platform.timeout_time = probe->platform.departure_time;
! 444: probe->platform.timeout_time.tv_sec += param->timeout;
! 445: }
! 446:
! 447: /* When allocating a probe, assign it a unique port number */
! 448: void platform_alloc_probe(
! 449: struct net_state_t *net_state,
! 450: struct probe_t *probe)
! 451: {
! 452: probe->sequence = net_state->platform.next_sequence++;
! 453:
! 454: if (net_state->platform.next_sequence > MAX_PORT) {
! 455: net_state->platform.next_sequence = MIN_PORT;
! 456: }
! 457: }
! 458:
! 459: /*
! 460: When freeing the probe, close the socket for the probe,
! 461: if one has been opened
! 462: */
! 463: void platform_free_probe(
! 464: struct probe_t *probe)
! 465: {
! 466: if (probe->platform.socket) {
! 467: close(probe->platform.socket);
! 468: probe->platform.socket = 0;
! 469: }
! 470: }
! 471:
! 472: /*
! 473: Compute the round trip time of a just-received probe and pass it
! 474: to the platform agnostic response handling.
! 475: */
! 476: void receive_probe(
! 477: struct net_state_t *net_state,
! 478: struct probe_t *probe,
! 479: int icmp_type,
! 480: const struct sockaddr_storage *remote_addr,
! 481: struct timeval *timestamp,
! 482: int mpls_count,
! 483: struct mpls_label_t *mpls)
! 484: {
! 485: unsigned int round_trip_us;
! 486: struct timeval *departure_time = &probe->platform.departure_time;
! 487: struct timeval now;
! 488:
! 489: if (timestamp == NULL) {
! 490: if (gettimeofday(&now, NULL)) {
! 491: perror("gettimeofday failure");
! 492: exit(EXIT_FAILURE);
! 493: }
! 494:
! 495: timestamp = &now;
! 496: }
! 497:
! 498: round_trip_us =
! 499: (timestamp->tv_sec - departure_time->tv_sec) * 1000000 +
! 500: timestamp->tv_usec - departure_time->tv_usec;
! 501:
! 502: respond_to_probe(net_state, probe, icmp_type,
! 503: remote_addr, round_trip_us, mpls_count, mpls);
! 504: }
! 505:
! 506: /*
! 507: Read all available packets through our receiving raw socket, and
! 508: handle any responses to probes we have preivously sent.
! 509: */
! 510: static
! 511: void receive_replies_from_icmp_socket(
! 512: struct net_state_t *net_state,
! 513: int socket,
! 514: received_packet_func_t handle_received_packet)
! 515: {
! 516: char packet[PACKET_BUFFER_SIZE];
! 517: int packet_length;
! 518: struct sockaddr_storage remote_addr;
! 519: socklen_t sockaddr_length;
! 520: struct timeval timestamp;
! 521:
! 522: /* Read until no more packets are available */
! 523: while (true) {
! 524: sockaddr_length = sizeof(struct sockaddr_storage);
! 525: packet_length = recvfrom(socket, packet, PACKET_BUFFER_SIZE, 0,
! 526: (struct sockaddr *) &remote_addr,
! 527: &sockaddr_length);
! 528:
! 529: /*
! 530: Get the time immediately after reading the packet to
! 531: keep the timing as precise as we can.
! 532: */
! 533: if (gettimeofday(×tamp, NULL)) {
! 534: perror("gettimeofday failure");
! 535: exit(EXIT_FAILURE);
! 536: }
! 537:
! 538: if (packet_length == -1) {
! 539: /*
! 540: EAGAIN will be returned if there is no current packet
! 541: available.
! 542: */
! 543: if (errno == EAGAIN) {
! 544: return;
! 545: }
! 546:
! 547: /*
! 548: EINTER will be returned if we received a signal during
! 549: receive.
! 550: */
! 551: if (errno == EINTR) {
! 552: continue;
! 553: }
! 554:
! 555: perror("Failure receiving replies");
! 556: exit(EXIT_FAILURE);
! 557: }
! 558:
! 559: handle_received_packet(net_state, &remote_addr, packet,
! 560: packet_length, ×tamp);
! 561: }
! 562: }
! 563:
! 564: /*
! 565: Attempt to send using the probe's socket, in order to check whether
! 566: the connection has completed, for stream oriented protocols such as
! 567: TCP.
! 568: */
! 569: static
! 570: void receive_replies_from_probe_socket(
! 571: struct net_state_t *net_state,
! 572: struct probe_t *probe)
! 573: {
! 574: int probe_socket;
! 575: struct timeval zero_time;
! 576: int err;
! 577: int err_length = sizeof(int);
! 578: fd_set write_set;
! 579:
! 580: probe_socket = probe->platform.socket;
! 581: if (!probe_socket) {
! 582: return;
! 583: }
! 584:
! 585: FD_ZERO(&write_set);
! 586: FD_SET(probe_socket, &write_set);
! 587:
! 588: zero_time.tv_sec = 0;
! 589: zero_time.tv_usec = 0;
! 590:
! 591: if (select(probe_socket + 1, NULL, &write_set, NULL, &zero_time) == -1) {
! 592: if (errno == EAGAIN) {
! 593: return;
! 594: } else {
! 595: perror("probe socket select error");
! 596: exit(EXIT_FAILURE);
! 597: }
! 598: }
! 599:
! 600: /*
! 601: If the socket is writable, the connection attempt has completed.
! 602: */
! 603: if (!FD_ISSET(probe_socket, &write_set)) {
! 604: return;
! 605: }
! 606:
! 607: if (getsockopt(probe_socket, SOL_SOCKET, SO_ERROR, &err, &err_length)) {
! 608: perror("probe socket SO_ERROR");
! 609: exit(EXIT_FAILURE);
! 610: }
! 611:
! 612: /*
! 613: If the connection complete successfully, or was refused, we can
! 614: assume our probe arrived at the destination.
! 615: */
! 616: if (!err || err == ECONNREFUSED) {
! 617: receive_probe(net_state, probe, ICMP_ECHOREPLY,
! 618: &probe->remote_addr, NULL, 0, NULL);
! 619: } else {
! 620: errno = err;
! 621: report_packet_error(probe->token);
! 622: free_probe(net_state, probe);
! 623: }
! 624: }
! 625:
! 626: /* Check both the IPv4 and IPv6 sockets for incoming packets */
! 627: void receive_replies(
! 628: struct net_state_t *net_state)
! 629: {
! 630: struct probe_t *probe;
! 631: struct probe_t *probe_safe_iter;
! 632:
! 633: if (net_state->platform.ip4_present) {
! 634: receive_replies_from_icmp_socket(net_state,
! 635: net_state->platform.
! 636: ip4_recv_socket,
! 637: handle_received_ip4_packet);
! 638: }
! 639:
! 640: if (net_state->platform.ip6_present) {
! 641: receive_replies_from_icmp_socket(net_state,
! 642: net_state->platform.
! 643: ip6_recv_socket,
! 644: handle_received_ip6_packet);
! 645: }
! 646:
! 647: LIST_FOREACH_SAFE(probe, &net_state->outstanding_probes,
! 648: probe_list_entry, probe_safe_iter) {
! 649:
! 650: receive_replies_from_probe_socket(net_state, probe);
! 651: }
! 652: }
! 653:
! 654: /*
! 655: Put all of our probe sockets in the read set used for an upcoming
! 656: select so we can wake when any of them become readable.
! 657: */
! 658: int gather_probe_sockets(
! 659: const struct net_state_t *net_state,
! 660: fd_set * write_set)
! 661: {
! 662: int probe_socket;
! 663: int nfds;
! 664: const struct probe_t *probe;
! 665:
! 666: nfds = 0;
! 667:
! 668: LIST_FOREACH(probe, &net_state->outstanding_probes, probe_list_entry) {
! 669: probe_socket = probe->platform.socket;
! 670:
! 671: if (probe_socket) {
! 672: FD_SET(probe_socket, write_set);
! 673: if (probe_socket >= nfds) {
! 674: nfds = probe_socket + 1;
! 675: }
! 676: }
! 677: }
! 678:
! 679: return nfds;
! 680: }
! 681:
! 682: /*
! 683: Check for any probes for which we have not received a response
! 684: for some time, and report a time-out, assuming that we won't
! 685: receive a future reply.
! 686: */
! 687: void check_probe_timeouts(
! 688: struct net_state_t *net_state)
! 689: {
! 690: struct timeval now;
! 691: struct probe_t *probe;
! 692: struct probe_t *probe_safe_iter;
! 693:
! 694: if (gettimeofday(&now, NULL)) {
! 695: perror("gettimeofday failure");
! 696: exit(EXIT_FAILURE);
! 697: }
! 698:
! 699: LIST_FOREACH_SAFE(probe, &net_state->outstanding_probes,
! 700: probe_list_entry, probe_safe_iter) {
! 701:
! 702: if (compare_timeval(probe->platform.timeout_time, now) < 0) {
! 703: /* Report timeout to the command stream */
! 704: printf("%d no-reply\n", probe->token);
! 705:
! 706: free_probe(net_state, probe);
! 707: }
! 708: }
! 709: }
! 710:
! 711: /*
! 712: Find the remaining time until the next probe times out.
! 713: This may be a negative value if the next probe timeout has
! 714: already elapsed.
! 715:
! 716: Returns false if no probes are currently outstanding, and true
! 717: if a timeout value for the next probe exists.
! 718: */
! 719: bool get_next_probe_timeout(
! 720: const struct net_state_t *net_state,
! 721: struct timeval *timeout)
! 722: {
! 723: bool have_timeout;
! 724: const struct probe_t *probe;
! 725: struct timeval now;
! 726: struct timeval probe_timeout;
! 727:
! 728: if (gettimeofday(&now, NULL)) {
! 729: perror("gettimeofday failure");
! 730: exit(EXIT_FAILURE);
! 731: }
! 732:
! 733: have_timeout = false;
! 734: LIST_FOREACH(probe, &net_state->outstanding_probes, probe_list_entry) {
! 735: probe_timeout.tv_sec =
! 736: probe->platform.timeout_time.tv_sec - now.tv_sec;
! 737: probe_timeout.tv_usec =
! 738: probe->platform.timeout_time.tv_usec - now.tv_usec;
! 739:
! 740: normalize_timeval(&probe_timeout);
! 741: if (have_timeout) {
! 742: if (compare_timeval(probe_timeout, *timeout) < 0) {
! 743: /* If this probe has a sooner timeout, store it instead */
! 744: *timeout = probe_timeout;
! 745: }
! 746: } else {
! 747: *timeout = probe_timeout;
! 748: have_timeout = true;
! 749: }
! 750: }
! 751:
! 752: return have_timeout;
! 753: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>