Annotation of embedaddon/mtr/packet/probe_unix.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:     mtr  --  a network diagnostic tool
                      3:     Copyright (C) 2016  Matt Kimball
                      4: 
                      5:     This program is free software; you can redistribute it and/or modify
                      6:     it under the terms of the GNU General Public License version 2 as
                      7:     published by the Free Software Foundation.
                      8: 
                      9:     This program is distributed in the hope that it will be useful,
                     10:     but WITHOUT ANY WARRANTY; without even the implied warranty of
                     11:     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     12:     GNU General Public License for more details.
                     13: 
                     14:     You should have received a copy of the GNU General Public License
                     15:     along with this program; if not, write to the Free Software
                     16:     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
                     17: */
                     18: 
                     19: #include "probe.h"
                     20: 
                     21: #include <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>