Blame packet/probe_unix.c

Packit b802ec
/*
Packit b802ec
    mtr  --  a network diagnostic tool
Packit b802ec
    Copyright (C) 2016  Matt Kimball
Packit b802ec
Packit b802ec
    This program is free software; you can redistribute it and/or modify
Packit b802ec
    it under the terms of the GNU General Public License version 2 as
Packit b802ec
    published by the Free Software Foundation.
Packit b802ec
Packit b802ec
    This program is distributed in the hope that it will be useful,
Packit b802ec
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit b802ec
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit b802ec
    GNU General Public License for more details.
Packit b802ec
Packit b802ec
    You should have received a copy of the GNU General Public License
Packit b802ec
    along with this program; if not, write to the Free Software
Packit b802ec
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Packit b802ec
*/
Packit b802ec
Packit b802ec
#include "probe.h"
Packit b802ec
Packit b802ec
#include <assert.h>
Packit b802ec
#include <errno.h>
Packit b802ec
#include <fcntl.h>
Packit b802ec
#include <stdio.h>
Packit b802ec
#include <stdlib.h>
Packit b802ec
#include <string.h>
Packit b802ec
#include <sys/socket.h>
Packit b802ec
#include <unistd.h>
Packit b802ec
Packit b802ec
#include "platform.h"
Packit b802ec
#include "protocols.h"
Packit b802ec
#include "construct_unix.h"
Packit b802ec
#include "deconstruct_unix.h"
Packit b802ec
#include "timeval.h"
Packit b802ec
Packit b802ec
/*  A wrapper around sendto for mixed IPv4 and IPv6 sending  */
Packit b802ec
static
Packit b802ec
int send_packet(
Packit b802ec
    const struct net_state_t *net_state,
Packit b802ec
    const struct probe_param_t *param,
Packit b802ec
    const char *packet,
Packit b802ec
    int packet_size,
Packit b802ec
    const struct sockaddr_storage *sockaddr)
Packit b802ec
{
Packit b802ec
    int send_socket = 0;
Packit b802ec
    int sockaddr_length;
Packit b802ec
Packit b802ec
    if (sockaddr->ss_family == AF_INET6) {
Packit b802ec
        sockaddr_length = sizeof(struct sockaddr_in6);
Packit b802ec
Packit b802ec
        if (param->protocol == IPPROTO_ICMP) {
Packit b802ec
            send_socket = net_state->platform.icmp6_send_socket;
Packit b802ec
        } else if (param->protocol == IPPROTO_UDP) {
Packit b802ec
            send_socket = net_state->platform.udp6_send_socket;
Packit b802ec
        }
Packit b802ec
    } else if (sockaddr->ss_family == AF_INET) {
Packit b802ec
        sockaddr_length = sizeof(struct sockaddr_in);
Packit b802ec
Packit b802ec
        send_socket = net_state->platform.ip4_send_socket;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (send_socket == 0) {
Packit b802ec
        errno = EINVAL;
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    return sendto(send_socket, packet, packet_size, 0,
Packit b802ec
                  (struct sockaddr *) sockaddr, sockaddr_length);
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    Nearly all fields in the IP header should be encoded in network byte
Packit b802ec
    order prior to passing to send().  However, the required byte order of
Packit b802ec
    the length field of the IP header is inconsistent between operating
Packit b802ec
    systems and operating system versions.  FreeBSD 11 requires the length
Packit b802ec
    field in network byte order, but some older versions of FreeBSD
Packit b802ec
    require host byte order.  OS X requires the length field in host
Packit b802ec
    byte order.  Linux will accept either byte order.
Packit b802ec
Packit b802ec
    Test for a byte order which works by sending a ping to localhost.
Packit b802ec
*/
Packit b802ec
static
Packit b802ec
void check_length_order(
Packit b802ec
    struct net_state_t *net_state)
Packit b802ec
{
Packit b802ec
    char packet[PACKET_BUFFER_SIZE];
Packit b802ec
    struct probe_param_t param;
Packit b802ec
    struct sockaddr_storage dest_sockaddr;
Packit b802ec
    struct sockaddr_storage src_sockaddr;
Packit b802ec
    ssize_t bytes_sent;
Packit b802ec
    int packet_size;
Packit b802ec
Packit b802ec
    memset(&param, 0, sizeof(struct probe_param_t));
Packit b802ec
    param.ip_version = 4;
Packit b802ec
    param.protocol = IPPROTO_ICMP;
Packit b802ec
    param.ttl = 255;
Packit b802ec
    param.remote_address = "127.0.0.1";
Packit b802ec
Packit b802ec
    if (resolve_probe_addresses(&param, &dest_sockaddr, &src_sockaddr)) {
Packit b802ec
        fprintf(stderr, "Error decoding localhost address\n");
Packit b802ec
        exit(EXIT_FAILURE);
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*  First attempt to ping the localhost with network byte order  */
Packit b802ec
    net_state->platform.ip_length_host_order = false;
Packit b802ec
Packit b802ec
    packet_size = construct_packet(net_state, NULL, MIN_PORT,
Packit b802ec
                                   packet, PACKET_BUFFER_SIZE,
Packit b802ec
                                   &dest_sockaddr, &src_sockaddr, ¶m;;
Packit b802ec
    if (packet_size < 0) {
Packit b802ec
        perror("Unable to send to localhost");
Packit b802ec
        exit(EXIT_FAILURE);
Packit b802ec
    }
Packit b802ec
Packit b802ec
    bytes_sent =
Packit b802ec
        send_packet(net_state, &param, packet, packet_size,
Packit b802ec
                    &dest_sockaddr);
Packit b802ec
    if (bytes_sent > 0) {
Packit b802ec
        return;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*  Since network byte order failed, try host byte order  */
Packit b802ec
    net_state->platform.ip_length_host_order = true;
Packit b802ec
Packit b802ec
    packet_size = construct_packet(net_state, NULL, MIN_PORT,
Packit b802ec
                                   packet, PACKET_BUFFER_SIZE,
Packit b802ec
                                   &dest_sockaddr, &src_sockaddr, ¶m;;
Packit b802ec
    if (packet_size < 0) {
Packit b802ec
        perror("Unable to send to localhost");
Packit b802ec
        exit(EXIT_FAILURE);
Packit b802ec
    }
Packit b802ec
Packit b802ec
    bytes_sent =
Packit b802ec
        send_packet(net_state, &param, packet, packet_size,
Packit b802ec
                    &dest_sockaddr);
Packit b802ec
    if (bytes_sent < 0) {
Packit b802ec
        perror("Unable to send with swapped length");
Packit b802ec
        exit(EXIT_FAILURE);
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    Check to see if SCTP is support.  We can't just rely on checking
Packit b802ec
    if IPPROTO_SCTP is defined, because while that is necessary,
Packit b802ec
    MacOS as of "Sierra" defines IPPROTO_SCTP, but creating an SCTP
Packit b802ec
    socket results in an error.
Packit b802ec
*/
Packit b802ec
static
Packit b802ec
void check_sctp_support(
Packit b802ec
    struct net_state_t *net_state)
Packit b802ec
{
Packit b802ec
#ifdef IPPROTO_SCTP
Packit b802ec
    int sctp_socket;
Packit b802ec
Packit b802ec
    sctp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
Packit b802ec
    if (sctp_socket != -1) {
Packit b802ec
        close(sctp_socket);
Packit b802ec
Packit b802ec
        net_state->platform.sctp_support = true;
Packit b802ec
    }
Packit b802ec
#endif
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Set a socket to non-blocking mode  */
Packit b802ec
void set_socket_nonblocking(
Packit b802ec
    int socket)
Packit b802ec
{
Packit b802ec
    int flags;
Packit b802ec
Packit b802ec
    flags = fcntl(socket, F_GETFL, 0);
Packit b802ec
    if (flags == -1) {
Packit b802ec
        perror("Unexpected socket F_GETFL error");
Packit b802ec
        exit(EXIT_FAILURE);
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (fcntl(socket, F_SETFL, flags | O_NONBLOCK)) {
Packit b802ec
        perror("Unexpected socket F_SETFL O_NONBLOCK error");
Packit b802ec
        exit(EXIT_FAILURE);
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Open the raw sockets for sending/receiving IPv4 packets  */
Packit b802ec
static
Packit b802ec
int open_ip4_sockets(
Packit b802ec
    struct net_state_t *net_state)
Packit b802ec
{
Packit b802ec
    int send_socket;
Packit b802ec
    int recv_socket;
Packit b802ec
    int trueopt = 1;
Packit b802ec
Packit b802ec
    send_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
Packit b802ec
    if (send_socket == -1) {
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*
Packit b802ec
       We will be including the IP header in transmitted packets.
Packit b802ec
       Linux doesn't require this, but BSD derived network stacks do.
Packit b802ec
     */
Packit b802ec
    if (setsockopt
Packit b802ec
        (send_socket, IPPROTO_IP, IP_HDRINCL, &trueopt, sizeof(int))) {
Packit b802ec
Packit b802ec
        close(send_socket);
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*
Packit b802ec
       Open a second socket with IPPROTO_ICMP because we are only
Packit b802ec
       interested in receiving ICMP packets, not all packets.
Packit b802ec
     */
Packit b802ec
    recv_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
Packit b802ec
    if (recv_socket == -1) {
Packit b802ec
        close(send_socket);
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    net_state->platform.ip4_present = true;
Packit b802ec
    net_state->platform.ip4_send_socket = send_socket;
Packit b802ec
    net_state->platform.ip4_recv_socket = recv_socket;
Packit b802ec
Packit b802ec
    return 0;
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Open the raw sockets for sending/receiving IPv6 packets  */
Packit b802ec
static
Packit b802ec
int open_ip6_sockets(
Packit b802ec
    struct net_state_t *net_state)
Packit b802ec
{
Packit b802ec
    int send_socket_icmp;
Packit b802ec
    int send_socket_udp;
Packit b802ec
    int recv_socket;
Packit b802ec
Packit b802ec
    send_socket_icmp = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
Packit b802ec
    if (send_socket_icmp == -1) {
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    send_socket_udp = socket(AF_INET6, SOCK_RAW, IPPROTO_UDP);
Packit b802ec
    if (send_socket_udp == -1) {
Packit b802ec
        close(send_socket_icmp);
Packit b802ec
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    recv_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
Packit b802ec
    if (recv_socket == -1) {
Packit b802ec
        close(send_socket_icmp);
Packit b802ec
        close(send_socket_udp);
Packit b802ec
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    net_state->platform.ip6_present = true;
Packit b802ec
    net_state->platform.icmp6_send_socket = send_socket_icmp;
Packit b802ec
    net_state->platform.udp6_send_socket = send_socket_udp;
Packit b802ec
    net_state->platform.ip6_recv_socket = recv_socket;
Packit b802ec
Packit b802ec
    return 0;
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    The first half of the net state initialization.  Since this
Packit b802ec
    happens with elevated privileges, this is kept as minimal
Packit b802ec
    as possible to minimize security risk.
Packit b802ec
*/
Packit b802ec
void init_net_state_privileged(
Packit b802ec
    struct net_state_t *net_state)
Packit b802ec
{
Packit b802ec
    int ip4_err = 0;
Packit b802ec
    int ip6_err = 0;
Packit b802ec
Packit b802ec
    memset(net_state, 0, sizeof(struct net_state_t));
Packit b802ec
Packit b802ec
    net_state->platform.next_sequence = MIN_PORT;
Packit b802ec
Packit b802ec
    if (open_ip4_sockets(net_state)) {
Packit b802ec
        ip4_err = errno;
Packit b802ec
    }
Packit b802ec
    if (open_ip6_sockets(net_state)) {
Packit b802ec
        ip6_err = errno;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*
Packit b802ec
       If we couldn't open either IPv4 or IPv6 sockets, we can't do
Packit b802ec
       much, so print errors and exit.
Packit b802ec
     */
Packit b802ec
    if (!net_state->platform.ip4_present
Packit b802ec
        && !net_state->platform.ip6_present) {
Packit b802ec
Packit b802ec
        errno = ip4_err;
Packit b802ec
        perror("Failure to open IPv4 sockets");
Packit b802ec
Packit b802ec
        errno = ip6_err;
Packit b802ec
        perror("Failure to open IPv6 sockets");
Packit b802ec
Packit b802ec
        exit(EXIT_FAILURE);
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    The second half of net state initialization, which is run
Packit b802ec
    at normal privilege levels.
Packit b802ec
*/
Packit b802ec
void init_net_state(
Packit b802ec
    struct net_state_t *net_state)
Packit b802ec
{
Packit b802ec
    set_socket_nonblocking(net_state->platform.ip4_recv_socket);
Packit b802ec
    set_socket_nonblocking(net_state->platform.ip6_recv_socket);
Packit b802ec
Packit b802ec
    if (net_state->platform.ip4_present) {
Packit b802ec
        check_length_order(net_state);
Packit b802ec
    }
Packit b802ec
Packit b802ec
    check_sctp_support(net_state);
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    Returns true if we were able to open sockets for a particular
Packit b802ec
    IP protocol version.
Packit b802ec
*/
Packit b802ec
bool is_ip_version_supported(
Packit b802ec
    struct net_state_t *net_state,
Packit b802ec
    int ip_version)
Packit b802ec
{
Packit b802ec
    if (ip_version == 4) {
Packit b802ec
        return net_state->platform.ip4_present;
Packit b802ec
    } else if (ip_version == 6) {
Packit b802ec
        return net_state->platform.ip6_present;
Packit b802ec
    } else {
Packit b802ec
        return false;
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Returns true if we can transmit probes using the specified protocol  */
Packit b802ec
bool is_protocol_supported(
Packit b802ec
    struct net_state_t * net_state,
Packit b802ec
    int protocol)
Packit b802ec
{
Packit b802ec
    if (protocol == IPPROTO_ICMP) {
Packit b802ec
        return true;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (protocol == IPPROTO_UDP) {
Packit b802ec
        return true;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (protocol == IPPROTO_TCP) {
Packit b802ec
        return true;
Packit b802ec
    }
Packit b802ec
#ifdef IPPROTO_SCTP
Packit b802ec
    if (protocol == IPPROTO_SCTP) {
Packit b802ec
        return net_state->platform.sctp_support;
Packit b802ec
    }
Packit b802ec
#endif
Packit b802ec
Packit b802ec
    return false;
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Report an error during send_probe based on the errno value  */
Packit b802ec
static
Packit b802ec
void report_packet_error(
Packit b802ec
    int command_token)
Packit b802ec
{
Packit b802ec
    if (errno == EINVAL) {
Packit b802ec
        printf("%d invalid-argument\n", command_token);
Packit b802ec
    } else if (errno == ENETDOWN) {
Packit b802ec
        printf("%d network-down\n", command_token);
Packit b802ec
    } else if (errno == ENETUNREACH) {
Packit b802ec
        printf("%d no-route\n", command_token);
Packit b802ec
    } else if (errno == EHOSTUNREACH) {
Packit b802ec
        printf("%d no-route\n", command_token);
Packit b802ec
    } else if (errno == EPERM) {
Packit b802ec
        printf("%d permission-denied\n", command_token);
Packit b802ec
    } else if (errno == EADDRINUSE) {
Packit b802ec
        printf("%d address-in-use\n", command_token);
Packit b802ec
    } else if (errno == EADDRNOTAVAIL) {
Packit b802ec
        printf("%d address-not-available\n", command_token);
Packit b802ec
    } else {
Packit b802ec
        printf("%d unexpected-error errno %d\n", command_token, errno);
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Craft a custom ICMP packet for a network probe.  */
Packit b802ec
void send_probe(
Packit b802ec
    struct net_state_t *net_state,
Packit b802ec
    const struct probe_param_t *param)
Packit b802ec
{
Packit b802ec
    char packet[PACKET_BUFFER_SIZE];
Packit b802ec
    struct probe_t *probe;
Packit b802ec
    int packet_size;
Packit b802ec
    struct sockaddr_storage src_sockaddr;
Packit b802ec
Packit b802ec
    probe = alloc_probe(net_state, param->command_token);
Packit b802ec
    if (probe == NULL) {
Packit b802ec
        printf("%d probes-exhausted\n", param->command_token);
Packit b802ec
        return;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (resolve_probe_addresses(param, &probe->remote_addr, &src_sockaddr)) {
Packit b802ec
        printf("%d invalid-argument\n", param->command_token);
Packit b802ec
        free_probe(net_state, probe);
Packit b802ec
        return;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (gettimeofday(&probe->platform.departure_time, NULL)) {
Packit b802ec
        perror("gettimeofday failure");
Packit b802ec
        exit(EXIT_FAILURE);
Packit b802ec
    }
Packit b802ec
Packit b802ec
    packet_size =
Packit b802ec
        construct_packet(net_state, &probe->platform.socket,
Packit b802ec
                         probe->sequence, packet, PACKET_BUFFER_SIZE,
Packit b802ec
                         &probe->remote_addr, &src_sockaddr, param);
Packit b802ec
Packit b802ec
    if (packet_size < 0) {
Packit b802ec
        /*
Packit b802ec
           When using a stream protocol, FreeBSD will return ECONNREFUSED
Packit b802ec
           when connecting to localhost if the port doesn't exist,
Packit b802ec
           even if the socket is non-blocking, so we should be
Packit b802ec
           prepared for that.
Packit b802ec
         */
Packit b802ec
        if (errno == ECONNREFUSED) {
Packit b802ec
            receive_probe(net_state, probe, ICMP_ECHOREPLY,
Packit b802ec
                          &probe->remote_addr, NULL, 0, NULL);
Packit b802ec
        } else {
Packit b802ec
            report_packet_error(param->command_token);
Packit b802ec
            free_probe(net_state, probe);
Packit b802ec
        }
Packit b802ec
Packit b802ec
        return;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (packet_size > 0) {
Packit b802ec
        if (send_packet(net_state, param,
Packit b802ec
                        packet, packet_size, &probe->remote_addr) == -1) {
Packit b802ec
Packit b802ec
            report_packet_error(param->command_token);
Packit b802ec
            free_probe(net_state, probe);
Packit b802ec
            return;
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
Packit b802ec
    probe->platform.timeout_time = probe->platform.departure_time;
Packit b802ec
    probe->platform.timeout_time.tv_sec += param->timeout;
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  When allocating a probe, assign it a unique port number  */
Packit b802ec
void platform_alloc_probe(
Packit b802ec
    struct net_state_t *net_state,
Packit b802ec
    struct probe_t *probe)
Packit b802ec
{
Packit b802ec
    probe->sequence = net_state->platform.next_sequence++;
Packit b802ec
Packit b802ec
    if (net_state->platform.next_sequence > MAX_PORT) {
Packit b802ec
        net_state->platform.next_sequence = MIN_PORT;
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    When freeing the probe, close the socket for the probe,
Packit b802ec
    if one has been opened
Packit b802ec
*/
Packit b802ec
void platform_free_probe(
Packit b802ec
    struct probe_t *probe)
Packit b802ec
{
Packit b802ec
    if (probe->platform.socket) {
Packit b802ec
        close(probe->platform.socket);
Packit b802ec
        probe->platform.socket = 0;
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    Compute the round trip time of a just-received probe and pass it
Packit b802ec
    to the platform agnostic response handling.
Packit b802ec
*/
Packit b802ec
void receive_probe(
Packit b802ec
    struct net_state_t *net_state,
Packit b802ec
    struct probe_t *probe,
Packit b802ec
    int icmp_type,
Packit b802ec
    const struct sockaddr_storage *remote_addr,
Packit b802ec
    struct timeval *timestamp,
Packit b802ec
    int mpls_count,
Packit b802ec
    struct mpls_label_t *mpls)
Packit b802ec
{
Packit b802ec
    unsigned int round_trip_us;
Packit b802ec
    struct timeval *departure_time = &probe->platform.departure_time;
Packit b802ec
    struct timeval now;
Packit b802ec
Packit b802ec
    if (timestamp == NULL) {
Packit b802ec
        if (gettimeofday(&now, NULL)) {
Packit b802ec
            perror("gettimeofday failure");
Packit b802ec
            exit(EXIT_FAILURE);
Packit b802ec
        }
Packit b802ec
Packit b802ec
        timestamp = &now;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    round_trip_us =
Packit b802ec
        (timestamp->tv_sec - departure_time->tv_sec) * 1000000 +
Packit b802ec
        timestamp->tv_usec - departure_time->tv_usec;
Packit b802ec
Packit b802ec
    respond_to_probe(net_state, probe, icmp_type,
Packit b802ec
                     remote_addr, round_trip_us, mpls_count, mpls);
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    Read all available packets through our receiving raw socket, and
Packit b802ec
    handle any responses to probes we have preivously sent.
Packit b802ec
*/
Packit b802ec
static
Packit b802ec
void receive_replies_from_icmp_socket(
Packit b802ec
    struct net_state_t *net_state,
Packit b802ec
    int socket,
Packit b802ec
    received_packet_func_t handle_received_packet)
Packit b802ec
{
Packit b802ec
    char packet[PACKET_BUFFER_SIZE];
Packit b802ec
    int packet_length;
Packit b802ec
    struct sockaddr_storage remote_addr;
Packit b802ec
    socklen_t sockaddr_length;
Packit b802ec
    struct timeval timestamp;
Packit b802ec
Packit b802ec
    /*  Read until no more packets are available  */
Packit b802ec
    while (true) {
Packit b802ec
        sockaddr_length = sizeof(struct sockaddr_storage);
Packit b802ec
        packet_length = recvfrom(socket, packet, PACKET_BUFFER_SIZE, 0,
Packit b802ec
                                 (struct sockaddr *) &remote_addr,
Packit b802ec
                                 &sockaddr_length);
Packit b802ec
Packit b802ec
        /*
Packit b802ec
           Get the time immediately after reading the packet to
Packit b802ec
           keep the timing as precise as we can.
Packit b802ec
         */
Packit b802ec
        if (gettimeofday(&timestamp, NULL)) {
Packit b802ec
            perror("gettimeofday failure");
Packit b802ec
            exit(EXIT_FAILURE);
Packit b802ec
        }
Packit b802ec
Packit b802ec
        if (packet_length == -1) {
Packit b802ec
            /*
Packit b802ec
               EAGAIN will be returned if there is no current packet
Packit b802ec
               available.
Packit b802ec
             */
Packit b802ec
            if (errno == EAGAIN) {
Packit b802ec
                return;
Packit b802ec
            }
Packit b802ec
Packit b802ec
            /*
Packit b802ec
               EINTER will be returned if we received a signal during
Packit b802ec
               receive.
Packit b802ec
             */
Packit b802ec
            if (errno == EINTR) {
Packit b802ec
                continue;
Packit b802ec
            }
Packit b802ec
Packit b802ec
            perror("Failure receiving replies");
Packit b802ec
            exit(EXIT_FAILURE);
Packit b802ec
        }
Packit b802ec
Packit b802ec
        handle_received_packet(net_state, &remote_addr, packet,
Packit b802ec
                               packet_length, &timestamp);
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    Attempt to send using the probe's socket, in order to check whether
Packit b802ec
    the connection has completed, for stream oriented protocols such as
Packit b802ec
    TCP.
Packit b802ec
*/
Packit b802ec
static
Packit b802ec
void receive_replies_from_probe_socket(
Packit b802ec
    struct net_state_t *net_state,
Packit b802ec
    struct probe_t *probe)
Packit b802ec
{
Packit b802ec
    int probe_socket;
Packit b802ec
    struct timeval zero_time;
Packit b802ec
    int err;
Packit b802ec
    int err_length = sizeof(int);
Packit b802ec
    fd_set write_set;
Packit b802ec
Packit b802ec
    probe_socket = probe->platform.socket;
Packit b802ec
    if (!probe_socket) {
Packit b802ec
        return;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    FD_ZERO(&write_set);
Packit b802ec
    FD_SET(probe_socket, &write_set);
Packit b802ec
Packit b802ec
    zero_time.tv_sec = 0;
Packit b802ec
    zero_time.tv_usec = 0;
Packit b802ec
Packit b802ec
    if (select(probe_socket + 1, NULL, &write_set, NULL, &zero_time) == -1) {
Packit b802ec
        if (errno == EAGAIN) {
Packit b802ec
            return;
Packit b802ec
        } else {
Packit b802ec
            perror("probe socket select error");
Packit b802ec
            exit(EXIT_FAILURE);
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*
Packit b802ec
       If the socket is writable, the connection attempt has completed.
Packit b802ec
     */
Packit b802ec
    if (!FD_ISSET(probe_socket, &write_set)) {
Packit b802ec
        return;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (getsockopt(probe_socket, SOL_SOCKET, SO_ERROR, &err, &err_length)) {
Packit b802ec
        perror("probe socket SO_ERROR");
Packit b802ec
        exit(EXIT_FAILURE);
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*
Packit b802ec
       If the connection complete successfully, or was refused, we can
Packit b802ec
       assume our probe arrived at the destination.
Packit b802ec
     */
Packit b802ec
    if (!err || err == ECONNREFUSED) {
Packit b802ec
        receive_probe(net_state, probe, ICMP_ECHOREPLY,
Packit b802ec
                      &probe->remote_addr, NULL, 0, NULL);
Packit b802ec
    } else {
Packit b802ec
        errno = err;
Packit b802ec
        report_packet_error(probe->token);
Packit b802ec
        free_probe(net_state, probe);
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Check both the IPv4 and IPv6 sockets for incoming packets  */
Packit b802ec
void receive_replies(
Packit b802ec
    struct net_state_t *net_state)
Packit b802ec
{
Packit b802ec
    struct probe_t *probe;
Packit b802ec
    struct probe_t *probe_safe_iter;
Packit b802ec
Packit b802ec
    if (net_state->platform.ip4_present) {
Packit b802ec
        receive_replies_from_icmp_socket(net_state,
Packit b802ec
                                         net_state->platform.
Packit b802ec
                                         ip4_recv_socket,
Packit b802ec
                                         handle_received_ip4_packet);
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (net_state->platform.ip6_present) {
Packit b802ec
        receive_replies_from_icmp_socket(net_state,
Packit b802ec
                                         net_state->platform.
Packit b802ec
                                         ip6_recv_socket,
Packit b802ec
                                         handle_received_ip6_packet);
Packit b802ec
    }
Packit b802ec
Packit b802ec
    LIST_FOREACH_SAFE(probe, &net_state->outstanding_probes,
Packit b802ec
                      probe_list_entry, probe_safe_iter) {
Packit b802ec
Packit b802ec
        receive_replies_from_probe_socket(net_state, probe);
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    Put all of our probe sockets in the read set used for an upcoming
Packit b802ec
    select so we can wake when any of them become readable.
Packit b802ec
*/
Packit b802ec
int gather_probe_sockets(
Packit b802ec
    const struct net_state_t *net_state,
Packit b802ec
    fd_set * write_set)
Packit b802ec
{
Packit b802ec
    int probe_socket;
Packit b802ec
    int nfds;
Packit b802ec
    const struct probe_t *probe;
Packit b802ec
Packit b802ec
    nfds = 0;
Packit b802ec
Packit b802ec
    LIST_FOREACH(probe, &net_state->outstanding_probes, probe_list_entry) {
Packit b802ec
        probe_socket = probe->platform.socket;
Packit b802ec
Packit b802ec
        if (probe_socket) {
Packit b802ec
            FD_SET(probe_socket, write_set);
Packit b802ec
            if (probe_socket >= nfds) {
Packit b802ec
                nfds = probe_socket + 1;
Packit b802ec
            }
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
Packit b802ec
    return nfds;
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    Check for any probes for which we have not received a response
Packit b802ec
    for some time, and report a time-out, assuming that we won't
Packit b802ec
    receive a future reply.
Packit b802ec
*/
Packit b802ec
void check_probe_timeouts(
Packit b802ec
    struct net_state_t *net_state)
Packit b802ec
{
Packit b802ec
    struct timeval now;
Packit b802ec
    struct probe_t *probe;
Packit b802ec
    struct probe_t *probe_safe_iter;
Packit b802ec
Packit b802ec
    if (gettimeofday(&now, NULL)) {
Packit b802ec
        perror("gettimeofday failure");
Packit b802ec
        exit(EXIT_FAILURE);
Packit b802ec
    }
Packit b802ec
Packit b802ec
    LIST_FOREACH_SAFE(probe, &net_state->outstanding_probes,
Packit b802ec
                      probe_list_entry, probe_safe_iter) {
Packit b802ec
Packit b802ec
        if (compare_timeval(probe->platform.timeout_time, now) < 0) {
Packit b802ec
            /*  Report timeout to the command stream  */
Packit b802ec
            printf("%d no-reply\n", probe->token);
Packit b802ec
Packit b802ec
            free_probe(net_state, probe);
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    Find the remaining time until the next probe times out.
Packit b802ec
    This may be a negative value if the next probe timeout has
Packit b802ec
    already elapsed.
Packit b802ec
Packit b802ec
    Returns false if no probes are currently outstanding, and true
Packit b802ec
    if a timeout value for the next probe exists.
Packit b802ec
*/
Packit b802ec
bool get_next_probe_timeout(
Packit b802ec
    const struct net_state_t *net_state,
Packit b802ec
    struct timeval *timeout)
Packit b802ec
{
Packit b802ec
    bool have_timeout;
Packit b802ec
    const struct probe_t *probe;
Packit b802ec
    struct timeval now;
Packit b802ec
    struct timeval probe_timeout;
Packit b802ec
Packit b802ec
    if (gettimeofday(&now, NULL)) {
Packit b802ec
        perror("gettimeofday failure");
Packit b802ec
        exit(EXIT_FAILURE);
Packit b802ec
    }
Packit b802ec
Packit b802ec
    have_timeout = false;
Packit b802ec
    LIST_FOREACH(probe, &net_state->outstanding_probes, probe_list_entry) {
Packit b802ec
        probe_timeout.tv_sec =
Packit b802ec
            probe->platform.timeout_time.tv_sec - now.tv_sec;
Packit b802ec
        probe_timeout.tv_usec =
Packit b802ec
            probe->platform.timeout_time.tv_usec - now.tv_usec;
Packit b802ec
Packit b802ec
        normalize_timeval(&probe_timeout);
Packit b802ec
        if (have_timeout) {
Packit b802ec
            if (compare_timeval(probe_timeout, *timeout) < 0) {
Packit b802ec
                /*  If this probe has a sooner timeout, store it instead  */
Packit b802ec
                *timeout = probe_timeout;
Packit b802ec
            }
Packit b802ec
        } else {
Packit b802ec
            *timeout = probe_timeout;
Packit b802ec
            have_timeout = true;
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
Packit b802ec
    return have_timeout;
Packit b802ec
}