Blame packet/construct_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 "construct_unix.h"
Packit b802ec
Packit b802ec
#include <errno.h>
Packit b802ec
#include <stdio.h>
Packit b802ec
#include <string.h>
Packit b802ec
#include <sys/socket.h>
Packit b802ec
#include <unistd.h>
Packit b802ec
Packit b802ec
#include "protocols.h"
Packit b802ec
Packit b802ec
/*  A source of data for computing a checksum  */
Packit b802ec
struct checksum_source_t {
Packit b802ec
    const void *data;
Packit b802ec
    size_t size;
Packit b802ec
};
Packit b802ec
Packit b802ec
/*  Compute the IP checksum (or ICMP checksum) of a packet.  */
Packit b802ec
static
Packit b802ec
uint16_t compute_checksum(
Packit b802ec
    const void *packet,
Packit b802ec
    int size)
Packit b802ec
{
Packit b802ec
    const uint8_t *packet_bytes = (uint8_t *) packet;
Packit b802ec
    uint32_t sum = 0;
Packit b802ec
    int i;
Packit b802ec
Packit b802ec
    for (i = 0; i < size; i++) {
Packit b802ec
        if ((i & 1) == 0) {
Packit b802ec
            sum += packet_bytes[i] << 8;
Packit b802ec
        } else {
Packit b802ec
            sum += packet_bytes[i];
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*
Packit b802ec
       Sums which overflow a 16-bit value have the high bits
Packit b802ec
       added back into the low 16 bits.
Packit b802ec
     */
Packit b802ec
    while (sum >> 16) {
Packit b802ec
        sum = (sum >> 16) + (sum & 0xffff);
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*
Packit b802ec
       The value stored is the one's complement of the
Packit b802ec
       mathematical sum.
Packit b802ec
     */
Packit b802ec
    return (~sum & 0xffff);
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Encode the IP header length field in the order required by the OS.  */
Packit b802ec
static
Packit b802ec
uint16_t length_byte_swap(
Packit b802ec
    const struct net_state_t *net_state,
Packit b802ec
    uint16_t length)
Packit b802ec
{
Packit b802ec
    if (net_state->platform.ip_length_host_order) {
Packit b802ec
        return length;
Packit b802ec
    } else {
Packit b802ec
        return htons(length);
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Construct a combined sockaddr from a source address and source port  */
Packit b802ec
static
Packit b802ec
void construct_addr_port(
Packit b802ec
    struct sockaddr_storage *addr_with_port,
Packit b802ec
    const struct sockaddr_storage *addr,
Packit b802ec
    int port)
Packit b802ec
{
Packit b802ec
    struct sockaddr_in *addr4;
Packit b802ec
    struct sockaddr_in6 *addr6;
Packit b802ec
Packit b802ec
    memcpy(addr_with_port, addr, sizeof(struct sockaddr_storage));
Packit b802ec
Packit b802ec
    if (addr->ss_family == AF_INET6) {
Packit b802ec
        addr6 = (struct sockaddr_in6 *) addr_with_port;
Packit b802ec
        addr6->sin6_port = htons(port);
Packit b802ec
    } else {
Packit b802ec
        addr4 = (struct sockaddr_in *) addr_with_port;
Packit b802ec
        addr4->sin_port = htons(port);
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Construct a header for IP version 4  */
Packit b802ec
static
Packit b802ec
void construct_ip4_header(
Packit b802ec
    const struct net_state_t *net_state,
Packit b802ec
    char *packet_buffer,
Packit b802ec
    int packet_size,
Packit b802ec
    const struct sockaddr_storage *srcaddr,
Packit b802ec
    const struct sockaddr_storage *destaddr,
Packit b802ec
    const struct probe_param_t *param)
Packit b802ec
{
Packit b802ec
    struct IPHeader *ip;
Packit b802ec
    struct sockaddr_in *srcaddr4 = (struct sockaddr_in *) srcaddr;
Packit b802ec
    struct sockaddr_in *destaddr4 = (struct sockaddr_in *) destaddr;
Packit b802ec
Packit b802ec
    ip = (struct IPHeader *) &packet_buffer[0];
Packit b802ec
Packit b802ec
    memset(ip, 0, sizeof(struct IPHeader));
Packit b802ec
Packit b802ec
    ip->version = 0x45;
Packit b802ec
    ip->tos = param->type_of_service;
Packit b802ec
    ip->len = length_byte_swap(net_state, packet_size);
Packit b802ec
    ip->ttl = param->ttl;
Packit b802ec
    ip->protocol = param->protocol;
Packit b802ec
    memcpy(&ip->saddr, &srcaddr4->sin_addr, sizeof(uint32_t));
Packit b802ec
    memcpy(&ip->daddr, &destaddr4->sin_addr, sizeof(uint32_t));
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Construct an ICMP header for IPv4  */
Packit b802ec
static
Packit b802ec
void construct_icmp4_header(
Packit b802ec
    const struct net_state_t *net_state,
Packit b802ec
    int sequence,
Packit b802ec
    char *packet_buffer,
Packit b802ec
    int packet_size,
Packit b802ec
    const struct probe_param_t *param)
Packit b802ec
{
Packit b802ec
    struct ICMPHeader *icmp;
Packit b802ec
    int icmp_size;
Packit b802ec
Packit b802ec
    icmp = (struct ICMPHeader *) &packet_buffer[sizeof(struct IPHeader)];
Packit b802ec
    icmp_size = packet_size - sizeof(struct IPHeader);
Packit b802ec
Packit b802ec
    memset(icmp, 0, sizeof(struct ICMPHeader));
Packit b802ec
Packit b802ec
    icmp->type = ICMP_ECHO;
Packit b802ec
    icmp->id = htons(getpid());
Packit b802ec
    icmp->sequence = htons(sequence);
Packit b802ec
    icmp->checksum = htons(compute_checksum(icmp, icmp_size));
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Construct an ICMP header for IPv6  */
Packit b802ec
static
Packit b802ec
int construct_icmp6_packet(
Packit b802ec
    const struct net_state_t *net_state,
Packit b802ec
    int sequence,
Packit b802ec
    char *packet_buffer,
Packit b802ec
    int packet_size,
Packit b802ec
    const struct probe_param_t *param)
Packit b802ec
{
Packit b802ec
    struct ICMPHeader *icmp;
Packit b802ec
Packit b802ec
    icmp = (struct ICMPHeader *) packet_buffer;
Packit b802ec
Packit b802ec
    memset(icmp, 0, sizeof(struct ICMPHeader));
Packit b802ec
Packit b802ec
    icmp->type = ICMP6_ECHO;
Packit b802ec
    icmp->id = htons(getpid());
Packit b802ec
    icmp->sequence = htons(sequence);
Packit b802ec
Packit b802ec
    return 0;
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    Set the port numbers for an outgoing UDP probe.
Packit b802ec
    There is limited space in the header for a sequence number
Packit b802ec
    to identify the probe upon return.
Packit b802ec
Packit b802ec
    We store the sequence number in the destination port, the local
Packit b802ec
    port, or the checksum.  The location chosen depends upon which
Packit b802ec
    probe parameters have been requested.
Packit b802ec
*/
Packit b802ec
static
Packit b802ec
void set_udp_ports(
Packit b802ec
    struct UDPHeader *udp,
Packit b802ec
    int sequence,
Packit b802ec
    const struct probe_param_t *param)
Packit b802ec
{
Packit b802ec
    if (param->dest_port) {
Packit b802ec
        udp->dstport = htons(param->dest_port);
Packit b802ec
Packit b802ec
        if (param->local_port) {
Packit b802ec
            udp->srcport = htons(param->local_port);
Packit b802ec
            udp->checksum = htons(sequence);
Packit b802ec
        } else {
Packit b802ec
            udp->srcport = htons(sequence);
Packit b802ec
            udp->checksum = 0;
Packit b802ec
        }
Packit b802ec
    } else {
Packit b802ec
        udp->dstport = htons(sequence);
Packit b802ec
Packit b802ec
        if (param->local_port) {
Packit b802ec
            udp->srcport = htons(param->local_port);
Packit b802ec
        } else {
Packit b802ec
            udp->srcport = htons(getpid());
Packit b802ec
        }
Packit b802ec
Packit b802ec
        udp->checksum = 0;
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    Construct a header for UDP probes, using the port number associated
Packit b802ec
    with the probe.
Packit b802ec
*/
Packit b802ec
static
Packit b802ec
void construct_udp4_header(
Packit b802ec
    const struct net_state_t *net_state,
Packit b802ec
    int sequence,
Packit b802ec
    char *packet_buffer,
Packit b802ec
    int packet_size,
Packit b802ec
    const struct probe_param_t *param)
Packit b802ec
{
Packit b802ec
    struct UDPHeader *udp;
Packit b802ec
    int udp_size;
Packit b802ec
Packit b802ec
    udp = (struct UDPHeader *) &packet_buffer[sizeof(struct IPHeader)];
Packit b802ec
    udp_size = packet_size - sizeof(struct IPHeader);
Packit b802ec
Packit b802ec
    memset(udp, 0, sizeof(struct UDPHeader));
Packit b802ec
Packit b802ec
    set_udp_ports(udp, sequence, param);
Packit b802ec
    udp->length = htons(udp_size);
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Construct a header for UDPv6 probes  */
Packit b802ec
static
Packit b802ec
int construct_udp6_packet(
Packit b802ec
    const struct net_state_t *net_state,
Packit b802ec
    int sequence,
Packit b802ec
    char *packet_buffer,
Packit b802ec
    int packet_size,
Packit b802ec
    const struct probe_param_t *param)
Packit b802ec
{
Packit b802ec
    int udp_socket = net_state->platform.udp6_send_socket;
Packit b802ec
    struct UDPHeader *udp;
Packit b802ec
    int udp_size;
Packit b802ec
Packit b802ec
    udp = (struct UDPHeader *) packet_buffer;
Packit b802ec
    udp_size = packet_size;
Packit b802ec
Packit b802ec
    memset(udp, 0, sizeof(struct UDPHeader));
Packit b802ec
Packit b802ec
    set_udp_ports(udp, sequence, param);
Packit b802ec
    udp->length = htons(udp_size);
Packit b802ec
Packit b802ec
    /*
Packit b802ec
       Instruct the kernel to put the pseudoheader checksum into the
Packit b802ec
       UDP header.
Packit b802ec
     */
Packit b802ec
    int chksum_offset = (char *) &udp->checksum - (char *) udp;
Packit b802ec
    if (setsockopt(udp_socket, IPPROTO_IPV6,
Packit b802ec
                   IPV6_CHECKSUM, &chksum_offset, sizeof(int))) {
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    return 0;
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    Set the socket options for an outgoing stream protocol socket based on
Packit b802ec
    the packet parameters.
Packit b802ec
*/
Packit b802ec
static
Packit b802ec
int set_stream_socket_options(
Packit b802ec
    int stream_socket,
Packit b802ec
    const struct probe_param_t *param)
Packit b802ec
{
Packit b802ec
    int level;
Packit b802ec
    int opt;
Packit b802ec
    int reuse = 1;
Packit b802ec
Packit b802ec
    /*  Allow binding to a local port previously in use  */
Packit b802ec
#ifdef SO_REUSEPORT
Packit b802ec
    /*
Packit b802ec
       FreeBSD wants SO_REUSEPORT in addition to SO_REUSEADDR to
Packit b802ec
       bind to the same port
Packit b802ec
     */
Packit b802ec
    if (setsockopt(stream_socket, SOL_SOCKET, SO_REUSEPORT,
Packit b802ec
                   &reuse, sizeof(int)) == -1) {
Packit b802ec
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
#endif
Packit b802ec
Packit b802ec
    if (setsockopt(stream_socket, SOL_SOCKET, SO_REUSEADDR,
Packit b802ec
                   &reuse, sizeof(int)) == -1) {
Packit b802ec
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*  Set the number of hops the probe will transit across  */
Packit b802ec
    if (param->ip_version == 6) {
Packit b802ec
        level = IPPROTO_IPV6;
Packit b802ec
        opt = IPV6_UNICAST_HOPS;
Packit b802ec
    } else {
Packit b802ec
        level = IPPROTO_IP;
Packit b802ec
        opt = IP_TTL;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (setsockopt(stream_socket, level, opt, &param->ttl, sizeof(int)) ==
Packit b802ec
        -1) {
Packit b802ec
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*  Set the "type of service" field of the IP header  */
Packit b802ec
    if (param->ip_version == 6) {
Packit b802ec
        level = IPPROTO_IPV6;
Packit b802ec
        opt = IPV6_TCLASS;
Packit b802ec
    } else {
Packit b802ec
        level = IPPROTO_IP;
Packit b802ec
        opt = IP_TOS;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (setsockopt(stream_socket, level, opt,
Packit b802ec
                   &param->type_of_service, sizeof(int)) == -1) {
Packit b802ec
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
#ifdef SO_MARK
Packit b802ec
    if (param->routing_mark) {
Packit b802ec
        if (setsockopt(stream_socket, SOL_SOCKET,
Packit b802ec
                       SO_MARK, &param->routing_mark, sizeof(int))) {
Packit b802ec
            return -1;
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
#endif
Packit b802ec
Packit b802ec
    return 0;
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    Open a TCP or SCTP socket, respecting the probe paramters as much as
Packit b802ec
    we can, and use it as an outgoing probe.
Packit b802ec
*/
Packit b802ec
static
Packit b802ec
int open_stream_socket(
Packit b802ec
    const struct net_state_t *net_state,
Packit b802ec
    int protocol,
Packit b802ec
    int port,
Packit b802ec
    const struct sockaddr_storage *src_sockaddr,
Packit b802ec
    const struct sockaddr_storage *dest_sockaddr,
Packit b802ec
    const struct probe_param_t *param)
Packit b802ec
{
Packit b802ec
    int stream_socket;
Packit b802ec
    int addr_len;
Packit b802ec
    int dest_port;
Packit b802ec
    struct sockaddr_storage dest_port_addr;
Packit b802ec
    struct sockaddr_storage src_port_addr;
Packit b802ec
Packit b802ec
    if (param->ip_version == 6) {
Packit b802ec
        stream_socket = socket(AF_INET6, SOCK_STREAM, protocol);
Packit b802ec
        addr_len = sizeof(struct sockaddr_in6);
Packit b802ec
    } else if (param->ip_version == 4) {
Packit b802ec
        stream_socket = socket(AF_INET, SOCK_STREAM, protocol);
Packit b802ec
        addr_len = sizeof(struct sockaddr_in);
Packit b802ec
    } else {
Packit b802ec
        errno = EINVAL;
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (stream_socket == -1) {
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    set_socket_nonblocking(stream_socket);
Packit b802ec
Packit b802ec
    if (set_stream_socket_options(stream_socket, param)) {
Packit b802ec
        close(stream_socket);
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*
Packit b802ec
       Bind to a known local port so we can identify which probe
Packit b802ec
       causes a TTL expiration.
Packit b802ec
     */
Packit b802ec
    construct_addr_port(&src_port_addr, src_sockaddr, port);
Packit b802ec
    if (bind(stream_socket, (struct sockaddr *) &src_port_addr, addr_len)) {
Packit b802ec
        close(stream_socket);
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (param->dest_port) {
Packit b802ec
        dest_port = param->dest_port;
Packit b802ec
    } else {
Packit b802ec
        /*  Use http if no port is specified  */
Packit b802ec
        dest_port = HTTP_PORT;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*  Attempt a connection  */
Packit b802ec
    construct_addr_port(&dest_port_addr, dest_sockaddr, dest_port);
Packit b802ec
    if (connect
Packit b802ec
        (stream_socket, (struct sockaddr *) &dest_port_addr, addr_len)) {
Packit b802ec
Packit b802ec
        /*  EINPROGRESS simply means the connection is in progress  */
Packit b802ec
        if (errno != EINPROGRESS) {
Packit b802ec
            close(stream_socket);
Packit b802ec
            return -1;
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
Packit b802ec
    return stream_socket;
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    Determine the size of the constructed packet based on the packet
Packit b802ec
    parameters.  This is the amount of space the packet *we* construct
Packit b802ec
    uses, and doesn't include any headers the operating system tacks
Packit b802ec
    onto the packet.  (Such as the IPv6 header on non-Linux operating
Packit b802ec
    systems.)
Packit b802ec
*/
Packit b802ec
static
Packit b802ec
int compute_packet_size(
Packit b802ec
    const struct net_state_t *net_state,
Packit b802ec
    const struct probe_param_t *param)
Packit b802ec
{
Packit b802ec
    int packet_size;
Packit b802ec
Packit b802ec
    if (param->protocol == IPPROTO_TCP) {
Packit b802ec
        return 0;
Packit b802ec
    }
Packit b802ec
#ifdef IPPROTO_SCTP
Packit b802ec
    if (param->protocol == IPPROTO_SCTP) {
Packit b802ec
        return 0;
Packit b802ec
    }
Packit b802ec
#endif
Packit b802ec
Packit b802ec
    /*  Start by determining the full size, including omitted headers  */
Packit b802ec
    if (param->ip_version == 6) {
Packit b802ec
        packet_size = sizeof(struct IP6Header);
Packit b802ec
    } else if (param->ip_version == 4) {
Packit b802ec
        packet_size = sizeof(struct IPHeader);
Packit b802ec
    } else {
Packit b802ec
        errno = EINVAL;
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (param->protocol == IPPROTO_ICMP) {
Packit b802ec
        packet_size += sizeof(struct ICMPHeader);
Packit b802ec
    } else if (param->protocol == IPPROTO_UDP) {
Packit b802ec
        packet_size += sizeof(struct UDPHeader);
Packit b802ec
Packit b802ec
        /*  We may need to put the sequence number in the payload  */
Packit b802ec
        packet_size += sizeof(int);
Packit b802ec
    } else {
Packit b802ec
        errno = EINVAL;
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*
Packit b802ec
       If the requested size from send-probe is greater, extend the
Packit b802ec
       packet size.
Packit b802ec
     */
Packit b802ec
    if (param->packet_size > packet_size) {
Packit b802ec
        packet_size = param->packet_size;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*
Packit b802ec
       Since we don't explicitly construct the IPv6 header, we
Packit b802ec
       need to account for it in our transmitted size.
Packit b802ec
     */
Packit b802ec
    if (param->ip_version == 6) {
Packit b802ec
        packet_size -= sizeof(struct IP6Header);
Packit b802ec
    }
Packit b802ec
Packit b802ec
    return packet_size;
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Construct a packet for an IPv4 probe  */
Packit b802ec
static
Packit b802ec
int construct_ip4_packet(
Packit b802ec
    const struct net_state_t *net_state,
Packit b802ec
    int *packet_socket,
Packit b802ec
    int sequence,
Packit b802ec
    char *packet_buffer,
Packit b802ec
    int packet_size,
Packit b802ec
    const struct sockaddr_storage *src_sockaddr,
Packit b802ec
    const struct sockaddr_storage *dest_sockaddr,
Packit b802ec
    const struct probe_param_t *param)
Packit b802ec
{
Packit b802ec
    int send_socket = net_state->platform.ip4_send_socket;
Packit b802ec
    bool is_stream_protocol = false;
Packit b802ec
Packit b802ec
    if (param->protocol == IPPROTO_TCP) {
Packit b802ec
        is_stream_protocol = true;
Packit b802ec
#ifdef IPPROTO_SCTP
Packit b802ec
    } else if (param->protocol == IPPROTO_SCTP) {
Packit b802ec
        is_stream_protocol = true;
Packit b802ec
#endif
Packit b802ec
    } else {
Packit b802ec
        construct_ip4_header(net_state, packet_buffer, packet_size,
Packit b802ec
                             src_sockaddr, dest_sockaddr, param);
Packit b802ec
Packit b802ec
        if (param->protocol == IPPROTO_ICMP) {
Packit b802ec
            construct_icmp4_header(net_state, sequence, packet_buffer,
Packit b802ec
                                   packet_size, param);
Packit b802ec
        } else if (param->protocol == IPPROTO_UDP) {
Packit b802ec
            construct_udp4_header(net_state, sequence, packet_buffer,
Packit b802ec
                                  packet_size, param);
Packit b802ec
        } else {
Packit b802ec
            errno = EINVAL;
Packit b802ec
            return -1;
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (is_stream_protocol) {
Packit b802ec
        send_socket =
Packit b802ec
            open_stream_socket(net_state, param->protocol, sequence,
Packit b802ec
                               src_sockaddr, dest_sockaddr, param);
Packit b802ec
Packit b802ec
        if (send_socket == -1) {
Packit b802ec
            return -1;
Packit b802ec
        }
Packit b802ec
Packit b802ec
        *packet_socket = send_socket;
Packit b802ec
        return 0;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*
Packit b802ec
       The routing mark requires CAP_NET_ADMIN, as opposed to the
Packit b802ec
       CAP_NET_RAW which we are sometimes explicitly given.
Packit b802ec
       If we don't have CAP_NET_ADMIN, this will fail, so we'll 
Packit b802ec
       only set the mark if the user has explicitly requested it.
Packit b802ec
Packit b802ec
       Unfortunately, this means that once the mark is set, it won't
Packit b802ec
       be set on the socket again until a new mark is explicitly
Packit b802ec
       specified.
Packit b802ec
     */
Packit b802ec
#ifdef SO_MARK
Packit b802ec
    if (param->routing_mark) {
Packit b802ec
        if (setsockopt(send_socket, SOL_SOCKET,
Packit b802ec
                       SO_MARK, &param->routing_mark, sizeof(int))) {
Packit b802ec
            return -1;
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
#endif
Packit b802ec
Packit b802ec
    return 0;
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Construct a packet for an IPv6 probe  */
Packit b802ec
static
Packit b802ec
int construct_ip6_packet(
Packit b802ec
    const struct net_state_t *net_state,
Packit b802ec
    int *packet_socket,
Packit b802ec
    int sequence,
Packit b802ec
    char *packet_buffer,
Packit b802ec
    int packet_size,
Packit b802ec
    const struct sockaddr_storage *src_sockaddr,
Packit b802ec
    const struct sockaddr_storage *dest_sockaddr,
Packit b802ec
    const struct probe_param_t *param)
Packit b802ec
{
Packit b802ec
    int send_socket;
Packit b802ec
    bool is_stream_protocol = false;
Packit b802ec
    bool bind_send_socket = true;
Packit b802ec
    struct sockaddr_storage current_sockaddr;
Packit b802ec
    int current_sockaddr_len;
Packit b802ec
Packit b802ec
    if (param->protocol == IPPROTO_TCP) {
Packit b802ec
        is_stream_protocol = true;
Packit b802ec
#ifdef IPPROTO_SCTP
Packit b802ec
    } else if (param->protocol == IPPROTO_SCTP) {
Packit b802ec
        is_stream_protocol = true;
Packit b802ec
#endif
Packit b802ec
    } else if (param->protocol == IPPROTO_ICMP) {
Packit b802ec
        send_socket = net_state->platform.icmp6_send_socket;
Packit b802ec
Packit b802ec
        if (construct_icmp6_packet
Packit b802ec
            (net_state, sequence, packet_buffer, packet_size, param)) {
Packit b802ec
            return -1;
Packit b802ec
        }
Packit b802ec
    } else if (param->protocol == IPPROTO_UDP) {
Packit b802ec
        send_socket = net_state->platform.udp6_send_socket;
Packit b802ec
Packit b802ec
        if (construct_udp6_packet
Packit b802ec
            (net_state, sequence, packet_buffer, packet_size, param)) {
Packit b802ec
            return -1;
Packit b802ec
        }
Packit b802ec
    } else {
Packit b802ec
        errno = EINVAL;
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (is_stream_protocol) {
Packit b802ec
        send_socket =
Packit b802ec
            open_stream_socket(net_state, param->protocol, sequence,
Packit b802ec
                               src_sockaddr, dest_sockaddr, param);
Packit b802ec
Packit b802ec
        if (send_socket == -1) {
Packit b802ec
            return -1;
Packit b802ec
        }
Packit b802ec
Packit b802ec
        *packet_socket = send_socket;
Packit b802ec
        return 0;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*
Packit b802ec
       Check the current socket address, and if it is the same
Packit b802ec
       as the source address we intend, we will skip the bind.
Packit b802ec
       This is to accomodate Solaris, which, as of Solaris 11.3,
Packit b802ec
       will return an EINVAL error on bind if the socket is already
Packit b802ec
       bound, even if the same address is used.
Packit b802ec
     */
Packit b802ec
    current_sockaddr_len = sizeof(struct sockaddr_in6);
Packit b802ec
    if (getsockname(send_socket, (struct sockaddr *) &current_sockaddr,
Packit b802ec
                    &current_sockaddr_len) == 0) {
Packit b802ec
Packit b802ec
        if (memcmp(&current_sockaddr,
Packit b802ec
                   src_sockaddr, sizeof(struct sockaddr_in6)) == 0) {
Packit b802ec
            bind_send_socket = false;
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*  Bind to our local address  */
Packit b802ec
    if (bind_send_socket) {
Packit b802ec
        if (bind(send_socket, (struct sockaddr *) src_sockaddr,
Packit b802ec
                 sizeof(struct sockaddr_in6))) {
Packit b802ec
            return -1;
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*  The traffic class in IPv6 is analagous to ToS in IPv4  */
Packit b802ec
    if (setsockopt(send_socket, IPPROTO_IPV6,
Packit b802ec
                   IPV6_TCLASS, &param->type_of_service, sizeof(int))) {
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*  Set the time-to-live  */
Packit b802ec
    if (setsockopt(send_socket, IPPROTO_IPV6,
Packit b802ec
                   IPV6_UNICAST_HOPS, &param->ttl, sizeof(int))) {
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
#ifdef SO_MARK
Packit b802ec
    if (param->routing_mark) {
Packit b802ec
        if (setsockopt(send_socket,
Packit b802ec
                       SOL_SOCKET, SO_MARK, &param->routing_mark,
Packit b802ec
                       sizeof(int))) {
Packit b802ec
            return -1;
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
#endif
Packit b802ec
Packit b802ec
    return 0;
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Construct a probe packet based on the probe parameters  */
Packit b802ec
int construct_packet(
Packit b802ec
    const struct net_state_t *net_state,
Packit b802ec
    int *packet_socket,
Packit b802ec
    int sequence,
Packit b802ec
    char *packet_buffer,
Packit b802ec
    int packet_buffer_size,
Packit b802ec
    const struct sockaddr_storage *dest_sockaddr,
Packit b802ec
    const struct sockaddr_storage *src_sockaddr,
Packit b802ec
    const struct probe_param_t *param)
Packit b802ec
{
Packit b802ec
    int packet_size;
Packit b802ec
Packit b802ec
    packet_size = compute_packet_size(net_state, param);
Packit b802ec
    if (packet_size < 0) {
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (packet_buffer_size < packet_size) {
Packit b802ec
        errno = EINVAL;
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    memset(packet_buffer, param->bit_pattern, packet_size);
Packit b802ec
Packit b802ec
    if (param->ip_version == 6) {
Packit b802ec
        if (construct_ip6_packet(net_state, packet_socket, sequence,
Packit b802ec
                                 packet_buffer, packet_size,
Packit b802ec
                                 src_sockaddr, dest_sockaddr, param)) {
Packit b802ec
            return -1;
Packit b802ec
        }
Packit b802ec
    } else if (param->ip_version == 4) {
Packit b802ec
        if (construct_ip4_packet(net_state, packet_socket, sequence,
Packit b802ec
                                 packet_buffer, packet_size,
Packit b802ec
                                 src_sockaddr, dest_sockaddr, param)) {
Packit b802ec
            return -1;
Packit b802ec
        }
Packit b802ec
    } else {
Packit b802ec
        errno = EINVAL;
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    return packet_size;
Packit b802ec
}