version 1.1.1.1, 2019/10/21 14:25:31
|
version 1.1.1.2, 2021/03/17 00:07:30
|
Line 11
|
Line 11
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
GNU General Public License for more details. |
GNU General Public License for more details. |
|
|
You should have received a copy of the GNU General Public License | You should have received a copy of the GNU General Public License along |
along with this program; if not, write to the Free Software | with this program; if not, write to the Free Software Foundation, Inc., |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
*/ |
*/ |
|
|
#include "probe.h" |
#include "probe.h" |
Line 21
|
Line 21
|
#include <assert.h> |
#include <assert.h> |
#include <errno.h> |
#include <errno.h> |
#include <fcntl.h> |
#include <fcntl.h> |
|
#ifdef HAVE_ERROR_H |
|
#include <error.h> |
|
#else |
|
#include "portability/error.h" |
|
#endif |
|
#ifdef HAVE_LINUX_ERRQUEUE_H |
|
#include <linux/errqueue.h> |
|
#endif |
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
Line 29
|
Line 37
|
|
|
#include "platform.h" |
#include "platform.h" |
#include "protocols.h" |
#include "protocols.h" |
|
#include "sockaddr.h" |
#include "construct_unix.h" |
#include "construct_unix.h" |
#include "deconstruct_unix.h" |
#include "deconstruct_unix.h" |
#include "timeval.h" |
#include "timeval.h" |
Line 38 static
|
Line 47 static
|
int send_packet( |
int send_packet( |
const struct net_state_t *net_state, |
const struct net_state_t *net_state, |
const struct probe_param_t *param, |
const struct probe_param_t *param, |
|
int sequence, |
const char *packet, |
const char *packet, |
int packet_size, |
int packet_size, |
const struct sockaddr_storage *sockaddr) |
const struct sockaddr_storage *sockaddr) |
{ |
{ |
|
struct sockaddr_storage dst; |
int send_socket = 0; |
int send_socket = 0; |
int sockaddr_length; |
int sockaddr_length; |
|
|
|
memcpy(&dst, sockaddr, sizeof(struct sockaddr_storage)); |
|
|
if (sockaddr->ss_family == AF_INET6) { |
if (sockaddr->ss_family == AF_INET6) { |
sockaddr_length = sizeof(struct sockaddr_in6); |
sockaddr_length = sizeof(struct sockaddr_in6); |
|
|
if (param->protocol == IPPROTO_ICMP) { |
if (param->protocol == IPPROTO_ICMP) { |
send_socket = net_state->platform.icmp6_send_socket; | if (net_state->platform.ip6_socket_raw) { |
| send_socket = net_state->platform.icmp6_send_socket; |
| } else { |
| send_socket = net_state->platform.ip6_txrx_icmp_socket; |
| } |
} else if (param->protocol == IPPROTO_UDP) { |
} else if (param->protocol == IPPROTO_UDP) { |
send_socket = net_state->platform.udp6_send_socket; | if (net_state->platform.ip6_socket_raw) { |
| send_socket = net_state->platform.udp6_send_socket; |
| /* we got a ipv6 udp raw socket |
| * the remote port is in the payload |
| * we do not set in the sockaddr |
| */ |
| *sockaddr_port_offset(&dst) = 0; |
| } else { |
| send_socket = net_state->platform.ip6_txrx_udp_socket; |
| if (param->dest_port) { |
| *sockaddr_port_offset(&dst) = htons(param->dest_port); |
| } else { |
| *sockaddr_port_offset(&dst) = sequence; |
| } |
| } |
} |
} |
} else if (sockaddr->ss_family == AF_INET) { |
} else if (sockaddr->ss_family == AF_INET) { |
sockaddr_length = sizeof(struct sockaddr_in); |
sockaddr_length = sizeof(struct sockaddr_in); |
|
|
send_socket = net_state->platform.ip4_send_socket; | if (net_state->platform.ip4_socket_raw) { |
| send_socket = net_state->platform.ip4_send_socket; |
| } else { |
| if (param->protocol == IPPROTO_ICMP) { |
| if (param->is_probing_byte_order) { |
| send_socket = net_state->platform.ip4_tmp_icmp_socket;; |
| } else { |
| send_socket = net_state->platform.ip4_txrx_icmp_socket; |
| } |
| } else if (param->protocol == IPPROTO_UDP) { |
| send_socket = net_state->platform.ip4_txrx_udp_socket; |
| if (param->dest_port) { |
| *sockaddr_port_offset(&dst) = htons(param->dest_port); |
| } else { |
| *sockaddr_port_offset(&dst) = sequence; |
| } |
| } |
| } |
} |
} |
|
|
if (send_socket == 0) { |
if (send_socket == 0) { |
Line 65 int send_packet(
|
Line 113 int send_packet(
|
} |
} |
|
|
return sendto(send_socket, packet, packet_size, 0, |
return sendto(send_socket, packet, packet_size, 0, |
(struct sockaddr *) sockaddr, sockaddr_length); | (struct sockaddr *) &dst, sockaddr_length); |
} |
} |
|
|
/* |
/* |
Line 85 void check_length_order(
|
Line 133 void check_length_order(
|
{ |
{ |
char packet[PACKET_BUFFER_SIZE]; |
char packet[PACKET_BUFFER_SIZE]; |
struct probe_param_t param; |
struct probe_param_t param; |
struct sockaddr_storage dest_sockaddr; | struct probe_t p0 = {.sequence = MIN_PORT }; |
struct sockaddr_storage src_sockaddr; | |
ssize_t bytes_sent; |
ssize_t bytes_sent; |
int packet_size; |
int packet_size; |
|
|
|
#ifdef __linux__ |
|
/* Linux will accept either byte order and check below fails to work |
|
* in some cases due to sendto() returning EPERM. */ |
|
return; |
|
#endif |
|
|
memset(¶m, 0, sizeof(struct probe_param_t)); |
memset(¶m, 0, sizeof(struct probe_param_t)); |
param.ip_version = 4; |
param.ip_version = 4; |
param.protocol = IPPROTO_ICMP; |
param.protocol = IPPROTO_ICMP; |
param.ttl = 255; |
param.ttl = 255; |
param.remote_address = "127.0.0.1"; |
param.remote_address = "127.0.0.1"; |
|
param.is_probing_byte_order = true; |
|
|
if (resolve_probe_addresses(¶m, &dest_sockaddr, &src_sockaddr)) { | |
fprintf(stderr, "Error decoding localhost address\n"); | if (resolve_probe_addresses(net_state, ¶m, &p0.remote_addr, |
| &p0.local_addr)) { |
| fprintf(stderr, "Error decoding localhost address (%s/%s)\n", |
| probe_err, strerror (errno)); |
exit(EXIT_FAILURE); |
exit(EXIT_FAILURE); |
} |
} |
|
|
/* First attempt to ping the localhost with network byte order */ |
/* First attempt to ping the localhost with network byte order */ |
net_state->platform.ip_length_host_order = false; |
net_state->platform.ip_length_host_order = false; |
|
|
packet_size = construct_packet(net_state, NULL, MIN_PORT, | packet_size = construct_packet(net_state, NULL, &p0, |
packet, PACKET_BUFFER_SIZE, |
packet, PACKET_BUFFER_SIZE, |
&dest_sockaddr, &src_sockaddr, ¶m); | ¶m); |
if (packet_size < 0) { |
if (packet_size < 0) { |
perror("Unable to send to localhost"); | error(EXIT_FAILURE, errno, "Unable to send to localhost"); |
exit(EXIT_FAILURE); | |
} |
} |
|
|
bytes_sent = |
bytes_sent = |
send_packet(net_state, ¶m, packet, packet_size, | send_packet(net_state, ¶m, MIN_PORT, packet, packet_size, |
&dest_sockaddr); | &p0.remote_addr); |
if (bytes_sent > 0) { |
if (bytes_sent > 0) { |
return; |
return; |
} |
} |
Line 122 void check_length_order(
|
Line 178 void check_length_order(
|
/* Since network byte order failed, try host byte order */ |
/* Since network byte order failed, try host byte order */ |
net_state->platform.ip_length_host_order = true; |
net_state->platform.ip_length_host_order = true; |
|
|
packet_size = construct_packet(net_state, NULL, MIN_PORT, | packet_size = construct_packet(net_state, NULL, &p0, |
packet, PACKET_BUFFER_SIZE, |
packet, PACKET_BUFFER_SIZE, |
&dest_sockaddr, &src_sockaddr, ¶m); | ¶m); |
if (packet_size < 0) { |
if (packet_size < 0) { |
perror("Unable to send to localhost"); | error(EXIT_FAILURE, errno, "Unable to send to localhost"); |
exit(EXIT_FAILURE); | |
} |
} |
|
|
bytes_sent = |
bytes_sent = |
send_packet(net_state, ¶m, packet, packet_size, | send_packet(net_state, ¶m, MIN_PORT, packet, packet_size, |
&dest_sockaddr); | &p0.remote_addr); |
if (bytes_sent < 0) { |
if (bytes_sent < 0) { |
perror("Unable to send with swapped length"); | error(EXIT_FAILURE, errno, "Unable to send with swapped length"); |
exit(EXIT_FAILURE); | |
} |
} |
} |
} |
|
|
Line 169 void set_socket_nonblocking(
|
Line 223 void set_socket_nonblocking(
|
|
|
flags = fcntl(socket, F_GETFL, 0); |
flags = fcntl(socket, F_GETFL, 0); |
if (flags == -1) { |
if (flags == -1) { |
perror("Unexpected socket F_GETFL error"); | error(EXIT_FAILURE, errno, "Unexpected socket F_GETFL error"); |
exit(EXIT_FAILURE); | |
} |
} |
|
|
if (fcntl(socket, F_SETFL, flags | O_NONBLOCK)) { |
if (fcntl(socket, F_SETFL, flags | O_NONBLOCK)) { |
perror("Unexpected socket F_SETFL O_NONBLOCK error"); | error(EXIT_FAILURE, errno, "Unexpected socket F_SETFL O_NONBLOCK error"); |
exit(EXIT_FAILURE); | |
} |
} |
} |
} |
|
|
/* Open the raw sockets for sending/receiving IPv4 packets */ |
/* Open the raw sockets for sending/receiving IPv4 packets */ |
static |
static |
int open_ip4_sockets( | int open_ip4_sockets_raw( |
struct net_state_t *net_state) |
struct net_state_t *net_state) |
{ |
{ |
int send_socket; |
int send_socket; |
Line 190 int open_ip4_sockets(
|
Line 242 int open_ip4_sockets(
|
|
|
send_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); |
send_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); |
if (send_socket == -1) { |
if (send_socket == -1) { |
return -1; | send_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); |
| if (send_socket == -1) { |
| return -1; |
| } |
} |
} |
|
|
/* |
/* |
Line 215 int open_ip4_sockets(
|
Line 270 int open_ip4_sockets(
|
} |
} |
|
|
net_state->platform.ip4_present = true; |
net_state->platform.ip4_present = true; |
|
net_state->platform.ip4_socket_raw = true; |
net_state->platform.ip4_send_socket = send_socket; |
net_state->platform.ip4_send_socket = send_socket; |
net_state->platform.ip4_recv_socket = recv_socket; |
net_state->platform.ip4_recv_socket = recv_socket; |
|
|
return 0; |
return 0; |
} |
} |
|
|
|
#ifdef HAVE_LINUX_ERRQUEUE_H |
|
/* Open DGRAM sockets for sending/receiving IPv4 packets */ |
|
static |
|
int open_ip4_sockets_dgram( |
|
struct net_state_t *net_state) |
|
{ |
|
int udp_socket; |
|
int icmp_socket, icmp_tmp_socket; |
|
#ifdef HAVE_LINUX_ERRQUEUE_H |
|
int val = 1; |
|
#endif |
|
|
|
icmp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); |
|
if (icmp_socket == -1) { |
|
return -1; |
|
} |
|
#ifdef HAVE_LINUX_ERRQUEUE_H |
|
if (setsockopt(icmp_socket, SOL_IP, IP_RECVERR, &val, sizeof(val)) < 0) { |
|
return -1; |
|
} |
|
#endif |
|
|
|
udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); |
|
if (udp_socket == -1) { |
|
close(icmp_socket); |
|
return -1; |
|
} |
|
#ifdef HAVE_LINUX_ERRQUEUE_H |
|
if (setsockopt(udp_socket, SOL_IP, IP_RECVERR, &val, sizeof(val)) < 0) { |
|
close(icmp_socket); |
|
close(udp_socket); |
|
return -1; |
|
} |
|
#endif |
|
|
|
icmp_tmp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); |
|
if (icmp_tmp_socket == -1) { |
|
close(icmp_socket); |
|
close(udp_socket); |
|
return -1; |
|
} |
|
|
|
net_state->platform.ip4_present = true; |
|
net_state->platform.ip4_socket_raw = false; |
|
net_state->platform.ip4_txrx_icmp_socket = icmp_socket; |
|
net_state->platform.ip4_tmp_icmp_socket = icmp_tmp_socket; |
|
net_state->platform.ip4_txrx_udp_socket = udp_socket; |
|
|
|
return 0; |
|
} |
|
#endif |
|
|
/* Open the raw sockets for sending/receiving IPv6 packets */ |
/* Open the raw sockets for sending/receiving IPv6 packets */ |
static |
static |
int open_ip6_sockets( | int open_ip6_sockets_raw( |
struct net_state_t *net_state) |
struct net_state_t *net_state) |
{ |
{ |
int send_socket_icmp; |
int send_socket_icmp; |
Line 251 int open_ip6_sockets(
|
Line 359 int open_ip6_sockets(
|
} |
} |
|
|
net_state->platform.ip6_present = true; |
net_state->platform.ip6_present = true; |
|
net_state->platform.ip6_socket_raw = true; |
net_state->platform.icmp6_send_socket = send_socket_icmp; |
net_state->platform.icmp6_send_socket = send_socket_icmp; |
net_state->platform.udp6_send_socket = send_socket_udp; |
net_state->platform.udp6_send_socket = send_socket_udp; |
net_state->platform.ip6_recv_socket = recv_socket; |
net_state->platform.ip6_recv_socket = recv_socket; |
Line 258 int open_ip6_sockets(
|
Line 367 int open_ip6_sockets(
|
return 0; |
return 0; |
} |
} |
|
|
|
#ifdef HAVE_LINUX_ERRQUEUE_H |
|
/* Open DGRAM sockets for sending/receiving IPv6 packets */ |
|
static |
|
int open_ip6_sockets_dgram( |
|
struct net_state_t *net_state) |
|
{ |
|
int icmp_socket; |
|
int udp_socket; |
|
#ifdef HAVE_LINUX_ERRQUEUE_H |
|
int val = 1; |
|
#endif |
|
|
|
icmp_socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6); |
|
if (icmp_socket == -1) { |
|
return -1; |
|
} |
|
#ifdef HAVE_LINUX_ERRQUEUE_H |
|
if (setsockopt(icmp_socket, SOL_IPV6, IPV6_RECVERR, &val, sizeof(val)) < 0) { |
|
return -1; |
|
} |
|
#endif |
|
|
|
udp_socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); |
|
if (udp_socket == -1) { |
|
close(icmp_socket); |
|
return -1; |
|
} |
|
#ifdef HAVE_LINUX_ERRQUEUE_H |
|
if (setsockopt(udp_socket, SOL_IPV6, IPV6_RECVERR, &val, sizeof(val)) < 0) { |
|
close(icmp_socket); |
|
close(udp_socket); |
|
return -1; |
|
} |
|
#endif |
|
|
|
net_state->platform.ip6_present = true; |
|
net_state->platform.ip6_socket_raw = false; |
|
net_state->platform.ip6_txrx_icmp_socket = icmp_socket; |
|
net_state->platform.ip6_txrx_udp_socket = udp_socket; |
|
|
|
return 0; |
|
} |
|
#endif |
|
|
/* |
/* |
The first half of the net state initialization. Since this |
The first half of the net state initialization. Since this |
happens with elevated privileges, this is kept as minimal |
happens with elevated privileges, this is kept as minimal |
Line 273 void init_net_state_privileged(
|
Line 426 void init_net_state_privileged(
|
|
|
net_state->platform.next_sequence = MIN_PORT; |
net_state->platform.next_sequence = MIN_PORT; |
|
|
if (open_ip4_sockets(net_state)) { | if (open_ip4_sockets_raw(net_state)) { |
ip4_err = errno; | #ifdef HAVE_LINUX_ERRQUEUE_H |
| /* fall back to using unprivileged sockets */ |
| if (open_ip4_sockets_dgram(net_state)) { |
| ip4_err = errno; |
| } |
| #endif |
} |
} |
if (open_ip6_sockets(net_state)) { | if (open_ip6_sockets_raw(net_state)) { |
ip6_err = errno; | #ifdef HAVE_LINUX_ERRQUEUE_H |
| /* fall back to using unprivileged sockets */ |
| if (open_ip6_sockets_dgram(net_state)) { |
| ip6_err = errno; |
| } |
| #endif |
} |
} |
|
|
/* |
/* |
Line 286 void init_net_state_privileged(
|
Line 449 void init_net_state_privileged(
|
*/ |
*/ |
if (!net_state->platform.ip4_present |
if (!net_state->platform.ip4_present |
&& !net_state->platform.ip6_present) { |
&& !net_state->platform.ip6_present) { |
| error(0, ip4_err, "Failure to open IPv4 sockets"); |
errno = ip4_err; | error(0, ip6_err, "Failure to open IPv6 sockets"); |
perror("Failure to open IPv4 sockets"); | |
| |
errno = ip6_err; | |
perror("Failure to open IPv6 sockets"); | |
| |
exit(EXIT_FAILURE); |
exit(EXIT_FAILURE); |
} |
} |
} |
} |
Line 304 void init_net_state_privileged(
|
Line 462 void init_net_state_privileged(
|
void init_net_state( |
void init_net_state( |
struct net_state_t *net_state) |
struct net_state_t *net_state) |
{ |
{ |
set_socket_nonblocking(net_state->platform.ip4_recv_socket); | if (net_state->platform.ip4_socket_raw) { |
set_socket_nonblocking(net_state->platform.ip6_recv_socket); | set_socket_nonblocking(net_state->platform.ip4_recv_socket); |
| } else { |
| set_socket_nonblocking(net_state->platform.ip4_txrx_icmp_socket); |
| set_socket_nonblocking(net_state->platform.ip4_txrx_udp_socket); |
| } |
| if (net_state->platform.ip6_socket_raw) { |
| set_socket_nonblocking(net_state->platform.ip6_recv_socket); |
| } else { |
| set_socket_nonblocking(net_state->platform.ip6_txrx_icmp_socket); |
| set_socket_nonblocking(net_state->platform.ip6_txrx_udp_socket); |
| } |
|
|
if (net_state->platform.ip4_present) { |
if (net_state->platform.ip4_present) { |
check_length_order(net_state); |
check_length_order(net_state); |
Line 387 void send_probe(
|
Line 555 void send_probe(
|
{ |
{ |
char packet[PACKET_BUFFER_SIZE]; |
char packet[PACKET_BUFFER_SIZE]; |
struct probe_t *probe; |
struct probe_t *probe; |
|
int trytimes; |
int packet_size; |
int packet_size; |
struct sockaddr_storage src_sockaddr; |
|
|
|
probe = alloc_probe(net_state, param->command_token); |
probe = alloc_probe(net_state, param->command_token); |
if (probe == NULL) { |
if (probe == NULL) { |
Line 396 void send_probe(
|
Line 564 void send_probe(
|
return; |
return; |
} |
} |
|
|
if (resolve_probe_addresses(param, &probe->remote_addr, &src_sockaddr)) { | if (resolve_probe_addresses(net_state, param, &probe->remote_addr, |
| &probe->local_addr)) { |
printf("%d invalid-argument\n", param->command_token); |
printf("%d invalid-argument\n", param->command_token); |
free_probe(net_state, probe); |
free_probe(net_state, probe); |
return; |
return; |
} |
} |
|
|
if (gettimeofday(&probe->platform.departure_time, NULL)) { |
if (gettimeofday(&probe->platform.departure_time, NULL)) { |
perror("gettimeofday failure"); | error(EXIT_FAILURE, errno, "gettimeofday failure"); |
exit(EXIT_FAILURE); | |
} |
} |
|
|
packet_size = | // there might be an off-by-one in the number of tries here. |
construct_packet(net_state, &probe->platform.socket, | // this is intentional. It is no use exhausting the very last |
probe->sequence, packet, PACKET_BUFFER_SIZE, | // open port. Max 10 retries would've been acceptable too I think. |
&probe->remote_addr, &src_sockaddr, param); | for (trytimes=MIN_PORT; trytimes < MAX_PORT; trytimes++) { |
| |
| packet_size = construct_packet(net_state, &probe->platform.socket, |
| probe, packet, PACKET_BUFFER_SIZE, |
| param); |
|
|
|
if (packet_size > 0) break; // no retry if we succeed. |
|
|
|
if ((param->protocol != IPPROTO_TCP) && |
|
(param->protocol != IPPROTO_SCTP)) break; // no retry if not TCP/SCTP |
|
|
|
if ((errno != EADDRINUSE) && (errno != EADDRNOTAVAIL)) { |
|
break; // no retry |
|
} |
|
|
|
probe->sequence = net_state->platform.next_sequence++; |
|
|
|
if (net_state->platform.next_sequence > MAX_PORT) { |
|
net_state->platform.next_sequence = MIN_PORT; |
|
} |
|
} |
|
|
if (packet_size < 0) { |
if (packet_size < 0) { |
/* |
/* |
When using a stream protocol, FreeBSD will return ECONNREFUSED |
When using a stream protocol, FreeBSD will return ECONNREFUSED |
Line 431 void send_probe(
|
Line 619 void send_probe(
|
} |
} |
|
|
if (packet_size > 0) { |
if (packet_size > 0) { |
if (send_packet(net_state, param, | if (send_packet(net_state, param, probe->sequence, |
packet, packet_size, &probe->remote_addr) == -1) { |
packet, packet_size, &probe->remote_addr) == -1) { |
|
|
report_packet_error(param->command_token); |
report_packet_error(param->command_token); |
Line 488 void receive_probe(
|
Line 676 void receive_probe(
|
|
|
if (timestamp == NULL) { |
if (timestamp == NULL) { |
if (gettimeofday(&now, NULL)) { |
if (gettimeofday(&now, NULL)) { |
perror("gettimeofday failure"); | error(EXIT_FAILURE, errno, "gettimeofday failure"); |
exit(EXIT_FAILURE); | |
} |
} |
|
|
timestamp = &now; |
timestamp = &now; |
Line 505 void receive_probe(
|
Line 692 void receive_probe(
|
|
|
/* |
/* |
Read all available packets through our receiving raw socket, and |
Read all available packets through our receiving raw socket, and |
handle any responses to probes we have preivously sent. | handle any responses to probes we have previously sent. |
*/ |
*/ |
static |
static |
void receive_replies_from_icmp_socket( | void receive_replies_from_recv_socket( |
struct net_state_t *net_state, |
struct net_state_t *net_state, |
int socket, |
int socket, |
received_packet_func_t handle_received_packet) |
received_packet_func_t handle_received_packet) |
Line 516 void receive_replies_from_icmp_socket(
|
Line 703 void receive_replies_from_icmp_socket(
|
char packet[PACKET_BUFFER_SIZE]; |
char packet[PACKET_BUFFER_SIZE]; |
int packet_length; |
int packet_length; |
struct sockaddr_storage remote_addr; |
struct sockaddr_storage remote_addr; |
socklen_t sockaddr_length; |
|
struct timeval timestamp; |
struct timeval timestamp; |
|
int flag = 0; |
|
#ifdef HAVE_LINUX_ERRQUEUE_H |
|
struct cmsghdr *cm; |
|
struct sock_extended_err *ee = NULL; |
|
bool icmp_connrefused_received = false; |
|
bool icmp_hostunreach_received = false; |
|
#endif |
|
|
/* Read until no more packets are available */ |
/* Read until no more packets are available */ |
while (true) { |
while (true) { |
sockaddr_length = sizeof(struct sockaddr_storage); | struct iovec iov; |
packet_length = recvfrom(socket, packet, PACKET_BUFFER_SIZE, 0, | struct msghdr msg; |
(struct sockaddr *) &remote_addr, | char control[1024]; |
&sockaddr_length); | |
|
|
|
memset(&msg, 0, sizeof(msg)); |
|
memset(&iov, 0, sizeof(iov)); |
|
iov.iov_base = packet; |
|
iov.iov_len = sizeof(packet); |
|
msg.msg_iov = &iov; |
|
msg.msg_iovlen = 1; |
|
msg.msg_name = (struct sockaddr*) &remote_addr; |
|
msg.msg_namelen = sizeof(remote_addr); |
|
msg.msg_control = control; |
|
msg.msg_controllen = sizeof(control); |
|
packet_length = recvmsg(socket, &msg, flag); |
|
|
/* |
/* |
Get the time immediately after reading the packet to |
Get the time immediately after reading the packet to |
keep the timing as precise as we can. |
keep the timing as precise as we can. |
*/ |
*/ |
if (gettimeofday(×tamp, NULL)) { |
if (gettimeofday(×tamp, NULL)) { |
perror("gettimeofday failure"); | error(EXIT_FAILURE, errno, "gettimeofday failure"); |
exit(EXIT_FAILURE); | |
} |
} |
|
|
if (packet_length == -1) { |
if (packet_length == -1) { |
Line 549 void receive_replies_from_icmp_socket(
|
Line 752 void receive_replies_from_icmp_socket(
|
receive. |
receive. |
*/ |
*/ |
if (errno == EINTR) { |
if (errno == EINTR) { |
|
/* clear error */ |
|
int so_err; |
|
socklen_t so_err_size = sizeof(so_err); |
|
int err; |
|
|
|
do { |
|
err = getsockopt(socket, SOL_SOCKET, SO_ERROR, &so_err, &so_err_size); |
|
} while (err < 0 && errno == EINTR); |
continue; |
continue; |
} |
} |
|
|
perror("Failure receiving replies"); | /* handle error received in error queue */ |
exit(EXIT_FAILURE); | if (errno == EHOSTUNREACH) { |
| /* potential error caused by ttl, read inner icmp hdr from err queue */ |
| #ifdef HAVE_LINUX_ERRQUEUE_H |
| icmp_hostunreach_received = true; |
| flag |= MSG_ERRQUEUE; |
| #endif |
| continue; |
| } |
| |
| if (errno == ECONNREFUSED) { |
| /* udp packet reached dst, read inner udp hdr from err queue */ |
| #ifdef HAVE_LINUX_ERRQUEUE_H |
| icmp_connrefused_received = true; |
| flag |= MSG_ERRQUEUE; |
| #endif |
| continue; |
| } |
| |
| error(EXIT_FAILURE, errno, "Failure receiving replies"); |
} |
} |
|
|
handle_received_packet(net_state, &remote_addr, packet, | #ifdef HAVE_LINUX_ERRQUEUE_H |
packet_length, ×tamp); | /* get src ip for packets read from err queue */ |
| if (flag & MSG_ERRQUEUE) { |
| for (cm = CMSG_FIRSTHDR(&msg); cm; cm = CMSG_NXTHDR(&msg, cm)) { |
| if (cm->cmsg_level == SOL_IP) { |
| if (cm->cmsg_type == IP_RECVERR) { |
| ee = (struct sock_extended_err *) CMSG_DATA(cm); |
| } |
| } |
| else if (cm->cmsg_level == SOL_IPV6) { |
| if (cm->cmsg_type == IPV6_RECVERR) { |
| ee = (struct sock_extended_err *) CMSG_DATA(cm); |
| } |
| } |
| } |
| if (ee) { |
| memcpy(&remote_addr, SO_EE_OFFENDER(ee), sizeof(remote_addr)); |
| } |
| } |
| |
| #ifdef SO_PROTOCOL |
| if (icmp_connrefused_received) { |
| /* using ICMP type ICMP_ECHOREPLY is not a bug, it is an |
| indication of successfully reaching dst host. |
| */ |
| handle_error_queue_packet(net_state, &remote_addr, ICMP_ECHOREPLY, IPPROTO_UDP, |
| packet, packet_length, ×tamp); |
| } else if (icmp_hostunreach_received) { |
| /* handle packet based on send socket protocol */ |
| int proto, length = sizeof(int); |
| |
| if (getsockopt(socket, SOL_SOCKET, SO_PROTOCOL, &proto, &length) < 0) { |
| error(EXIT_FAILURE, errno, "getsockopt SO_PROTOCOL error"); |
| } |
| handle_error_queue_packet(net_state, &remote_addr, ICMP_TIME_EXCEEDED, proto, |
| packet, packet_length, ×tamp); |
| } else { |
| #endif |
| #endif |
| /* ICMP packets received from raw socket */ |
| handle_received_packet(net_state, &remote_addr, packet, |
| packet_length, ×tamp); |
| #ifdef HAVE_LINUX_ERRQUEUE_H |
| #ifdef SO_PROTOCOL |
| } |
| #endif |
| #endif |
} |
} |
} |
} |
|
|
Line 592 void receive_replies_from_probe_socket(
|
Line 866 void receive_replies_from_probe_socket(
|
if (errno == EAGAIN) { |
if (errno == EAGAIN) { |
return; |
return; |
} else { |
} else { |
perror("probe socket select error"); | error(EXIT_FAILURE, errno, "probe socket select error"); |
exit(EXIT_FAILURE); | |
} |
} |
} |
} |
|
|
Line 605 void receive_replies_from_probe_socket(
|
Line 878 void receive_replies_from_probe_socket(
|
} |
} |
|
|
if (getsockopt(probe_socket, SOL_SOCKET, SO_ERROR, &err, &err_length)) { |
if (getsockopt(probe_socket, SOL_SOCKET, SO_ERROR, &err, &err_length)) { |
perror("probe socket SO_ERROR"); | error(EXIT_FAILURE, errno, "probe socket SO_ERROR"); |
exit(EXIT_FAILURE); | |
} |
} |
|
|
/* |
/* |
Line 631 void receive_replies(
|
Line 903 void receive_replies(
|
struct probe_t *probe_safe_iter; |
struct probe_t *probe_safe_iter; |
|
|
if (net_state->platform.ip4_present) { |
if (net_state->platform.ip4_present) { |
receive_replies_from_icmp_socket(net_state, | if (net_state->platform.ip4_socket_raw) { |
net_state->platform. | receive_replies_from_recv_socket(net_state, |
ip4_recv_socket, | net_state->platform. |
handle_received_ip4_packet); | ip4_recv_socket, |
| handle_received_ip4_packet); |
| } else { |
| receive_replies_from_recv_socket(net_state, |
| net_state->platform. |
| ip4_txrx_icmp_socket, |
| handle_received_ip4_packet); |
| receive_replies_from_recv_socket(net_state, |
| net_state->platform. |
| ip4_txrx_udp_socket, |
| handle_received_ip4_packet); |
| } |
} |
} |
|
|
if (net_state->platform.ip6_present) { |
if (net_state->platform.ip6_present) { |
receive_replies_from_icmp_socket(net_state, | if (net_state->platform.ip6_socket_raw) { |
net_state->platform. | receive_replies_from_recv_socket(net_state, |
ip6_recv_socket, | net_state->platform. |
handle_received_ip6_packet); | ip6_recv_socket, |
| handle_received_ip6_packet); |
| } else { |
| receive_replies_from_recv_socket(net_state, |
| net_state->platform. |
| ip6_txrx_icmp_socket, |
| handle_received_ip6_packet); |
| receive_replies_from_recv_socket(net_state, |
| net_state->platform. |
| ip6_txrx_udp_socket, |
| handle_received_ip6_packet); |
| } |
} |
} |
|
|
LIST_FOREACH_SAFE(probe, &net_state->outstanding_probes, |
LIST_FOREACH_SAFE(probe, &net_state->outstanding_probes, |
Line 692 void check_probe_timeouts(
|
Line 986 void check_probe_timeouts(
|
struct probe_t *probe_safe_iter; |
struct probe_t *probe_safe_iter; |
|
|
if (gettimeofday(&now, NULL)) { |
if (gettimeofday(&now, NULL)) { |
perror("gettimeofday failure"); | error(EXIT_FAILURE, errno, "gettimeofday failure"); |
exit(EXIT_FAILURE); | |
} |
} |
|
|
LIST_FOREACH_SAFE(probe, &net_state->outstanding_probes, |
LIST_FOREACH_SAFE(probe, &net_state->outstanding_probes, |
Line 726 bool get_next_probe_timeout(
|
Line 1019 bool get_next_probe_timeout(
|
struct timeval probe_timeout; |
struct timeval probe_timeout; |
|
|
if (gettimeofday(&now, NULL)) { |
if (gettimeofday(&now, NULL)) { |
perror("gettimeofday failure"); | error(EXIT_FAILURE, errno, "gettimeofday failure"); |
exit(EXIT_FAILURE); | |
} |
} |
|
|
have_timeout = false; |
have_timeout = false; |