|
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, ¶m->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 |
¶m->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, ¶m->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, ¶m->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 *) ¤t_sockaddr,
|
|
Packit |
b802ec |
¤t_sockaddr_len) == 0) {
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (memcmp(¤t_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, ¶m->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, ¶m->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, ¶m->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 |
}
|