mirror of
https://github.com/traviscross/mtr.git
synced 2024-09-21 10:27:10 +00:00
Merge pull request #484 from alarig/master
Change UDP and ICMP sockets binding to accept a source IP from the -a…
This commit is contained in:
commit
74d312d7e6
@ -71,19 +71,6 @@ uint16_t compute_checksum(
|
||||
return (~sum & 0xffff);
|
||||
}
|
||||
|
||||
/* Encode the IP header length field in the order required by the OS. */
|
||||
static
|
||||
uint16_t length_byte_swap(
|
||||
const struct net_state_t *net_state,
|
||||
uint16_t length)
|
||||
{
|
||||
if (net_state->platform.ip_length_host_order) {
|
||||
return length;
|
||||
} else {
|
||||
return htons(length);
|
||||
}
|
||||
}
|
||||
|
||||
/* Construct a combined sockaddr from a source address and source port */
|
||||
static
|
||||
void construct_addr_port(
|
||||
@ -95,38 +82,9 @@ void construct_addr_port(
|
||||
*sockaddr_port_offset(addr_with_port) = htons(port);
|
||||
}
|
||||
|
||||
/* Construct a header for IP version 4 */
|
||||
static
|
||||
void construct_ip4_header(
|
||||
const struct net_state_t *net_state,
|
||||
const struct probe_t *probe,
|
||||
char *packet_buffer,
|
||||
int packet_size,
|
||||
const struct probe_param_t *param)
|
||||
{
|
||||
struct IPHeader *ip;
|
||||
|
||||
ip = (struct IPHeader *) &packet_buffer[0];
|
||||
|
||||
memset(ip, 0, sizeof(struct IPHeader));
|
||||
|
||||
ip->version = 0x45;
|
||||
ip->tos = param->type_of_service;
|
||||
ip->len = length_byte_swap(net_state, packet_size);
|
||||
ip->ttl = param->ttl;
|
||||
ip->protocol = param->protocol;
|
||||
// ip->id = htons(getpid());
|
||||
memcpy(&ip->saddr,
|
||||
sockaddr_addr_offset(&probe->local_addr),
|
||||
sockaddr_addr_size(&probe->local_addr));
|
||||
memcpy(&ip->daddr,
|
||||
sockaddr_addr_offset(&probe->remote_addr),
|
||||
sockaddr_addr_size(&probe->remote_addr));
|
||||
}
|
||||
|
||||
/* Construct an ICMP header for IPv4 */
|
||||
static
|
||||
void construct_icmp4_header(
|
||||
int construct_icmp4_packet(
|
||||
const struct net_state_t *net_state,
|
||||
struct probe_t *probe,
|
||||
char *packet_buffer,
|
||||
@ -134,22 +92,17 @@ void construct_icmp4_header(
|
||||
const struct probe_param_t *param)
|
||||
{
|
||||
struct ICMPHeader *icmp;
|
||||
int icmp_size;
|
||||
|
||||
if (net_state->platform.ip4_socket_raw) {
|
||||
icmp = (struct ICMPHeader *) &packet_buffer[sizeof(struct IPHeader)];
|
||||
icmp_size = packet_size - sizeof(struct IPHeader);
|
||||
} else {
|
||||
icmp = (struct ICMPHeader *) &packet_buffer[0];
|
||||
icmp_size = packet_size;
|
||||
}
|
||||
icmp = (struct ICMPHeader *) packet_buffer;
|
||||
|
||||
memset(icmp, 0, sizeof(struct ICMPHeader));
|
||||
|
||||
icmp->type = ICMP_ECHO;
|
||||
icmp->id = htons(getpid());
|
||||
icmp->sequence = htons(probe->sequence);
|
||||
icmp->checksum = htons(compute_checksum(icmp, icmp_size));
|
||||
icmp->checksum = htons(compute_checksum(icmp, packet_size));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Construct an ICMP header for IPv6 */
|
||||
@ -238,7 +191,7 @@ int udp4_checksum(void *pheader, void *udata, int psize, int dsize,
|
||||
with the probe.
|
||||
*/
|
||||
static
|
||||
void construct_udp4_header(
|
||||
int construct_udp4_packet(
|
||||
const struct net_state_t *net_state,
|
||||
struct probe_t *probe,
|
||||
char *packet_buffer,
|
||||
@ -248,13 +201,8 @@ void construct_udp4_header(
|
||||
struct UDPHeader *udp;
|
||||
int udp_size;
|
||||
|
||||
if (net_state->platform.ip4_socket_raw) {
|
||||
udp = (struct UDPHeader *) &packet_buffer[sizeof(struct IPHeader)];
|
||||
udp_size = packet_size - sizeof(struct IPHeader);
|
||||
} else {
|
||||
udp = (struct UDPHeader *) &packet_buffer[0];
|
||||
udp_size = packet_size;
|
||||
}
|
||||
udp = (struct UDPHeader *) packet_buffer;
|
||||
udp_size = packet_size;
|
||||
|
||||
memset(udp, 0, sizeof(struct UDPHeader));
|
||||
|
||||
@ -283,6 +231,8 @@ void construct_udp4_header(
|
||||
*checksum_off = htons(udp4_checksum(&udph, udp,
|
||||
sizeof(struct UDPPseudoHeader),
|
||||
udp_size, udp->checksum != 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Construct a header for UDPv6 probes */
|
||||
@ -561,10 +511,10 @@ int construct_ip4_packet(
|
||||
int packet_size,
|
||||
const struct probe_param_t *param)
|
||||
{
|
||||
int send_socket = net_state->platform.ip4_send_socket;
|
||||
int send_socket;
|
||||
bool is_stream_protocol = false;
|
||||
int tos, ttl, socket;
|
||||
bool bind_send_socket = false;
|
||||
int tos, ttl;
|
||||
bool bind_send_socket = true;
|
||||
struct sockaddr_storage current_sockaddr;
|
||||
int current_sockaddr_len;
|
||||
|
||||
@ -574,23 +524,34 @@ int construct_ip4_packet(
|
||||
} else if (param->protocol == IPPROTO_SCTP) {
|
||||
is_stream_protocol = true;
|
||||
#endif
|
||||
} else {
|
||||
} else if (param->protocol == IPPROTO_ICMP) {
|
||||
if (net_state->platform.ip4_socket_raw) {
|
||||
construct_ip4_header(net_state, probe, packet_buffer, packet_size,
|
||||
param);
|
||||
}
|
||||
if (param->protocol == IPPROTO_ICMP) {
|
||||
construct_icmp4_header(net_state, probe, packet_buffer,
|
||||
packet_size, param);
|
||||
} else if (param->protocol == IPPROTO_UDP) {
|
||||
construct_udp4_header(net_state, probe, packet_buffer,
|
||||
packet_size, param);
|
||||
send_socket = net_state->platform.icmp4_send_socket;
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
send_socket = net_state->platform.ip4_txrx_icmp_socket;
|
||||
}
|
||||
|
||||
if (construct_icmp4_packet
|
||||
(net_state, probe, packet_buffer, packet_size, param)) {
|
||||
return -1;
|
||||
}
|
||||
} else if (param->protocol == IPPROTO_UDP) {
|
||||
if (net_state->platform.ip4_socket_raw) {
|
||||
send_socket = net_state->platform.udp4_send_socket;
|
||||
} else {
|
||||
send_socket = net_state->platform.ip4_txrx_udp_socket;
|
||||
}
|
||||
|
||||
if (construct_udp4_packet
|
||||
(net_state, probe, packet_buffer, packet_size, param)) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (is_stream_protocol) {
|
||||
send_socket =
|
||||
open_stream_socket(net_state, param->protocol, probe->sequence,
|
||||
@ -633,54 +594,51 @@ int construct_ip4_packet(
|
||||
#endif
|
||||
|
||||
/*
|
||||
Bind src port when not using raw socket to pass in ICMP id, kernel
|
||||
get ICMP id from src_port when using DGRAM socket.
|
||||
Check the current socket address, and if it is the same
|
||||
as the source address we intend, we will skip the bind.
|
||||
This is to accommodate Solaris, which, as of Solaris 11.3,
|
||||
will return an EINVAL error on bind if the socket is already
|
||||
bound, even if the same address is used.
|
||||
*/
|
||||
if (!net_state->platform.ip4_socket_raw &&
|
||||
param->protocol == IPPROTO_ICMP &&
|
||||
!param->is_probing_byte_order) {
|
||||
current_sockaddr_len = sizeof(struct sockaddr_in);
|
||||
bind_send_socket = true;
|
||||
socket = net_state->platform.ip4_txrx_icmp_socket;
|
||||
if (getsockname(socket, (struct sockaddr *) ¤t_sockaddr,
|
||||
¤t_sockaddr_len)) {
|
||||
return -1;
|
||||
}
|
||||
struct sockaddr_in *sin_cur =
|
||||
(struct sockaddr_in *) ¤t_sockaddr;
|
||||
current_sockaddr_len = sizeof(struct sockaddr_in);
|
||||
if (getsockname(send_socket, (struct sockaddr *) ¤t_sockaddr,
|
||||
¤t_sockaddr_len) == 0) {
|
||||
struct sockaddr_in *sin_cur = (struct sockaddr_in *) ¤t_sockaddr;
|
||||
|
||||
/* avoid double bind */
|
||||
if (sin_cur->sin_port) {
|
||||
bind_send_socket = false;
|
||||
if (net_state->platform.ip4_socket_raw) {
|
||||
if (memcmp(¤t_sockaddr,
|
||||
&probe->local_addr, sizeof(struct sockaddr_in)) == 0) {
|
||||
bind_send_socket = false;
|
||||
}
|
||||
} else {
|
||||
/* avoid double bind for DGRAM socket */
|
||||
if (sin_cur->sin_port) {
|
||||
bind_send_socket = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Bind to our local address */
|
||||
if (bind_send_socket && bind(socket, (struct sockaddr *)&probe->local_addr,
|
||||
if (bind_send_socket && bind(send_socket, (struct sockaddr *)&probe->local_addr,
|
||||
sizeof(struct sockaddr_in))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* set TOS and TTL for non-raw socket */
|
||||
if (!net_state->platform.ip4_socket_raw && !param->is_probing_byte_order) {
|
||||
if (param->protocol == IPPROTO_ICMP) {
|
||||
socket = net_state->platform.ip4_txrx_icmp_socket;
|
||||
} else if (param->protocol == IPPROTO_UDP) {
|
||||
socket = net_state->platform.ip4_txrx_udp_socket;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
tos = param->type_of_service;
|
||||
if (setsockopt(socket, SOL_IP, IP_TOS, &tos, sizeof(int))) {
|
||||
return -1;
|
||||
}
|
||||
ttl = param->ttl;
|
||||
if (setsockopt(socket, SOL_IP, IP_TTL,
|
||||
&ttl, sizeof(int)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
/* Set the type of service */
|
||||
tos = param->type_of_service;
|
||||
if (setsockopt(send_socket, SOL_IP, IP_TOS, &tos, sizeof(int))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set the time-to-live */
|
||||
ttl = param->ttl;
|
||||
if (setsockopt(send_socket, SOL_IP, IP_TTL,
|
||||
&ttl, sizeof(int)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -87,16 +87,21 @@ int send_packet(
|
||||
} else if (sockaddr->ss_family == AF_INET) {
|
||||
sockaddr_length = sizeof(struct sockaddr_in);
|
||||
|
||||
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) {
|
||||
if (param->protocol == IPPROTO_ICMP) {
|
||||
if (net_state->platform.ip4_socket_raw) {
|
||||
send_socket = net_state->platform.icmp4_send_socket;
|
||||
} else {
|
||||
send_socket = net_state->platform.ip4_txrx_icmp_socket;
|
||||
}
|
||||
} else if (param->protocol == IPPROTO_UDP) {
|
||||
if (net_state->platform.ip4_socket_raw) {
|
||||
send_socket = net_state->platform.udp4_send_socket;
|
||||
/* we got a ipv4 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.ip4_txrx_udp_socket;
|
||||
if (param->dest_port) {
|
||||
*sockaddr_port_offset(&dst) = htons(param->dest_port);
|
||||
@ -105,6 +110,7 @@ int send_packet(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (send_socket == 0) {
|
||||
@ -236,26 +242,19 @@ static
|
||||
int open_ip4_sockets_raw(
|
||||
struct net_state_t *net_state)
|
||||
{
|
||||
int send_socket;
|
||||
int send_socket_icmp;
|
||||
int send_socket_udp;
|
||||
int recv_socket;
|
||||
int trueopt = 1;
|
||||
|
||||
send_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
|
||||
if (send_socket == -1) {
|
||||
send_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
|
||||
if (send_socket == -1) {
|
||||
return -1;
|
||||
}
|
||||
send_socket_icmp = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
|
||||
if (send_socket_icmp == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
We will be including the IP header in transmitted packets.
|
||||
Linux doesn't require this, but BSD derived network stacks do.
|
||||
*/
|
||||
if (setsockopt
|
||||
(send_socket, IPPROTO_IP, IP_HDRINCL, &trueopt, sizeof(int))) {
|
||||
send_socket_udp = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
|
||||
if (send_socket_udp == -1) {
|
||||
close(send_socket_icmp);
|
||||
|
||||
close(send_socket);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -265,13 +264,15 @@ int open_ip4_sockets_raw(
|
||||
*/
|
||||
recv_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
|
||||
if (recv_socket == -1) {
|
||||
close(send_socket);
|
||||
close(send_socket_icmp);
|
||||
close(send_socket_udp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
net_state->platform.ip4_present = true;
|
||||
net_state->platform.ip4_socket_raw = true;
|
||||
net_state->platform.ip4_send_socket = send_socket;
|
||||
net_state->platform.icmp4_send_socket = send_socket_icmp;
|
||||
net_state->platform.udp4_send_socket = send_socket_udp;
|
||||
net_state->platform.ip4_recv_socket = recv_socket;
|
||||
|
||||
return 0;
|
||||
|
@ -54,8 +54,11 @@ struct net_state_platform_t {
|
||||
/* true if ipv6 socket is raw socket */
|
||||
bool ip6_socket_raw;
|
||||
|
||||
/* Socket used to send raw IPv4 packets */
|
||||
int ip4_send_socket;
|
||||
/* Send socket for ICMPv6 packets */
|
||||
int icmp4_send_socket;
|
||||
|
||||
/* Send socket for UDPv6 packets */
|
||||
int udp4_send_socket;
|
||||
|
||||
/* Socket used to receive IPv4 ICMP replies */
|
||||
int ip4_recv_socket;
|
||||
|
Loading…
Reference in New Issue
Block a user