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(&param, 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(&param, &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, &param);
        !           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, &param, 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, &param);
        !           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, &param, 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(&timestamp, 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, &timestamp);
        !           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>