Skip to content

Commit

Permalink
Merge pull request #484 from alarig/master
Browse files Browse the repository at this point in the history
Change UDP and ICMP sockets binding to accept a source IP from the -a…
  • Loading branch information
rewolff authored Sep 11, 2023
2 parents ad48183 + d529dbe commit 74d312d
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 138 deletions.
176 changes: 67 additions & 109 deletions packet/construct_unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -95,61 +82,27 @@ 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,
int packet_size,
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 */
Expand Down Expand Up @@ -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,
Expand All @@ -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));

Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;

Expand All @@ -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);
send_socket = net_state->platform.icmp4_send_socket;
} else {
send_socket = net_state->platform.ip4_txrx_icmp_socket;
}
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);

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 {
errno = EINVAL;
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,
Expand Down Expand Up @@ -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 *) &current_sockaddr,
&current_sockaddr_len)) {
return -1;
}
struct sockaddr_in *sin_cur =
(struct sockaddr_in *) &current_sockaddr;
current_sockaddr_len = sizeof(struct sockaddr_in);
if (getsockname(send_socket, (struct sockaddr *) &current_sockaddr,
&current_sockaddr_len) == 0) {
struct sockaddr_in *sin_cur = (struct sockaddr_in *) &current_sockaddr;

/* avoid double bind */
if (sin_cur->sin_port) {
bind_send_socket = false;
if (net_state->platform.ip4_socket_raw) {
if (memcmp(&current_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;
}

Expand Down
55 changes: 28 additions & 27 deletions packet/probe_unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -105,6 +110,7 @@ int send_packet(
}
}
}

}

if (send_socket == 0) {
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
Expand Down
7 changes: 5 additions & 2 deletions packet/probe_unix.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 74d312d

Please sign in to comment.