Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linux unprivileged ping support #173

Merged
merged 3 commits into from
Feb 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions ci/test-11-nopriv.pl
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
#!/usr/bin/perl -w

use English;
use Test::Command;
use Test::More;

if( $^O eq 'darwin' ) {
plan skip_all => 'Test irrelevant on MacOS';
exit 0;
}

sub get_ping_gid_range {
open FD, "/proc/sys/net/ipv4/ping_group_range" or return undef;
chomp(my $line = <FD>);
my @range = split(/\s+/, $line);
close FD;
return @range;
}

my @gids = split(' ', $EGID);
my @allowed_gid_range = get_ping_gid_range();

# Linux test for unprivileged ping support
foreach(@gids) {
if ($_ >= $allowed_gid_range[0] && $_ <= $allowed_gid_range[1]) {
plan skip_all => "Userspace pings are allowed, gid $_ in range [$allowed_gid_range[0], $allowed_gid_range[1]]";
exit 0;
}
}

plan tests => 3;

# run without privileges
Expand Down
64 changes: 40 additions & 24 deletions src/fping.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,13 @@ HOST_ENTRY* ev_first;
HOST_ENTRY* ev_last;

char* prog;
int ident; /* our pid */
int ident4 = 0; /* our icmp identity field */
int socket4 = -1;
int using_sock_dgram4 = 0;
#ifndef IPV6
int hints_ai_family = AF_INET;
#else
int ident6 = 0;
int socket6 = -1;
int hints_ai_family = AF_UNSPEC;
#endif
Expand Down Expand Up @@ -358,7 +360,7 @@ int main(int argc, char** argv)
usage(0);
}

socket4 = open_ping_socket_ipv4();
socket4 = open_ping_socket_ipv4(&using_sock_dgram4);
#ifdef IPV6
socket6 = open_ping_socket_ipv6();
/* if called (sym-linked) via 'fping6', imply '-6'
Expand All @@ -368,14 +370,19 @@ int main(int argc, char** argv)
}
#endif

memset(&src_addr, 0, sizeof(src_addr));
#ifdef IPV6
memset(&src_addr6, 0, sizeof(src_addr6));
#endif

if ((uid = getuid())) {
/* drop privileges */
if (setuid(getuid()) == -1)
perror("cannot setuid");
}

optparse_init(&optparse_state, argv);
ident = getpid() & 0xFFFF;
ident4 = ident6 = getpid() & 0xFFFF;
verbose_flag = 1;
backoff_flag = 1;
opterr = 1;
Expand Down Expand Up @@ -965,12 +972,12 @@ int main(int argc, char** argv)
exit(num_noaddress ? 2 : 1);
}

if (src_addr_set && socket4 >= 0) {
socket_set_src_addr_ipv4(socket4, &src_addr);
if (socket4 >= 0) {
socket_set_src_addr_ipv4(socket4, &src_addr, &ident4);
}
#ifdef IPV6
if (src_addr6_set && socket6 >= 0) {
socket_set_src_addr_ipv6(socket6, &src_addr6);
if (socket6 >= 0) {
socket_set_src_addr_ipv6(socket6, &src_addr6, &ident6);
}
#endif

Expand Down Expand Up @@ -1674,11 +1681,11 @@ int send_ping(HOST_ENTRY* h)
#endif /* DEBUG || _DEBUG */

if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
n = socket_sendto_ping_ipv4(socket4, (struct sockaddr*)&h->saddr, h->saddr_len, myseq, ident);
n = socket_sendto_ping_ipv4(socket4, (struct sockaddr*)&h->saddr, h->saddr_len, myseq, ident4);
}
#ifdef IPV6
else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
n = socket_sendto_ping_ipv6(socket6, (struct sockaddr*)&h->saddr, h->saddr_len, myseq, ident);
n = socket_sendto_ping_ipv6(socket6, (struct sockaddr*)&h->saddr, h->saddr_len, myseq, ident6);
}
#endif
else {
Expand Down Expand Up @@ -1830,19 +1837,22 @@ int decode_icmp_ipv4(
unsigned short* id,
unsigned short* seq)
{
struct ip* ip = (struct ip*)reply_buf;
struct icmp* icp;
int hlen = 0;

if (!using_sock_dgram4) {
struct ip* ip = (struct ip*)reply_buf;

#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__)
/* The alpha headers are decidedly broken.
* Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
* ip_v. So, to get ip_hl, we mask off the bottom four bits.
*/
hlen = (ip->ip_vhl & 0x0F) << 2;
/* The alpha headers are decidedly broken.
* Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
* ip_v. So, to get ip_hl, we mask off the bottom four bits.
*/
hlen = (ip->ip_vhl & 0x0F) << 2;
#else
hlen = ip->ip_hl << 2;
hlen = ip->ip_hl << 2;
#endif
}

if (reply_buf_len < hlen + ICMP_MINLEN) {
/* too short */
Expand Down Expand Up @@ -1871,7 +1881,7 @@ int decode_icmp_ipv4(

sent_icmp = (struct icmp*)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));

if (sent_icmp->icmp_type != ICMP_ECHO || ntohs(sent_icmp->icmp_id) != ident) {
if (sent_icmp->icmp_type != ICMP_ECHO || sent_icmp->icmp_id != ident4) {
/* not caused by us */
return 0;
}
Expand Down Expand Up @@ -1920,7 +1930,7 @@ int decode_icmp_ipv4(
return 0;
}

*id = ntohs(icp->icmp_id);
*id = icp->icmp_id;
*seq = ntohs(icp->icmp_seq);

return 1;
Expand Down Expand Up @@ -1963,7 +1973,7 @@ int decode_icmp_ipv6(

sent_icmp = (struct icmp6_hdr*)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip));

if (sent_icmp->icmp6_type != ICMP_ECHO || ntohs(sent_icmp->icmp6_id) != ident) {
if (sent_icmp->icmp6_type != ICMP_ECHO || sent_icmp->icmp6_id != ident6) {
/* not caused by us */
return 0;
}
Expand Down Expand Up @@ -2012,7 +2022,7 @@ int decode_icmp_ipv6(
return 0;
}

*id = ntohs(icp->icmp6_id);
*id = icp->icmp6_id;
*seq = ntohs(icp->icmp6_seq);

return 1;
Expand Down Expand Up @@ -2082,6 +2092,13 @@ int wait_for_reply(long wait_time)
&seq)) {
return 1;
}
if (id != ident4) {
return 1; /* packet received, but not the one we are looking for! */
}
if (using_sock_dgram4) {
/* IP header is not included in read SOCK_DGRAM ICMP responses */
result += sizeof(struct ip);
}
}
#ifdef IPV6
else if (response_addr.ss_family == AF_INET6) {
Expand All @@ -2094,16 +2111,15 @@ int wait_for_reply(long wait_time)
&seq)) {
return 1;
}
if (id != ident6) {
return 1; /* packet received, but not the one we are looking for! */
}
}
#endif
else {
return 1;
}

if (id != ident) {
return 1; /* packet received, but not the one we are looking for! */
}

seqmap_value = seqmap_fetch(seq, &current_time);
if (seqmap_value == NULL) {
return 1;
Expand Down
6 changes: 3 additions & 3 deletions src/fping.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ int in_cksum( unsigned short *p, int n );
extern int random_data_flag;

/* socket.c */
int open_ping_socket_ipv4();
int open_ping_socket_ipv4(int *using_sock_dgram);
void init_ping_buffer_ipv4(size_t ping_data_size);
void socket_set_src_addr_ipv4(int s, struct in_addr *src_addr);
void socket_set_src_addr_ipv4(int s, struct in_addr *src_addr, int *ident);
int socket_sendto_ping_ipv4(int s, struct sockaddr *saddr, socklen_t saddr_len, uint16_t icmp_seq, uint16_t icmp_id);
#ifdef IPV6
int open_ping_socket_ipv6();
void init_ping_buffer_ipv6(size_t ping_data_size);
void socket_set_src_addr_ipv6(int s, struct in6_addr *src_addr);
void socket_set_src_addr_ipv6(int s, struct in6_addr *src_addr, int *ident);
int socket_sendto_ping_ipv6(int s, struct sockaddr *saddr, socklen_t saddr_len, uint16_t icmp_seq, uint16_t icmp_id);
#endif

Expand Down
29 changes: 23 additions & 6 deletions src/socket4.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
char* ping_buffer_ipv4 = 0;
size_t ping_pkt_size_ipv4;

int open_ping_socket_ipv4()
int open_ping_socket_ipv4(int *using_sock_dgram)
{
struct protoent* proto;
int s;
Expand All @@ -56,6 +56,8 @@ int open_ping_socket_ipv4()
if ((proto = getprotobyname("icmp")) == NULL)
crash_and_burn("icmp: unknown protocol");

*using_sock_dgram = 0;

/* create raw socket for ICMP calls (ping) */
s = socket(AF_INET, SOCK_RAW, proto->p_proto);
if (s < 0) {
Expand All @@ -64,6 +66,13 @@ int open_ping_socket_ipv4()
if (s < 0) {
return -1;
}

#ifdef __linux__
/* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
* structure is missing in the message.
*/
*using_sock_dgram = 1;
#endif
}

/* Make sure that we use non-blocking IO */
Expand All @@ -89,15 +98,23 @@ void init_ping_buffer_ipv4(size_t ping_data_size)
crash_and_burn("can't malloc ping packet");
}

void socket_set_src_addr_ipv4(int s, struct in_addr* src_addr)
void socket_set_src_addr_ipv4(int s, struct in_addr* src_addr, int *ident)
{
struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
socklen_t len = sizeof(sa);

memset(&sa, 0, len);
sa.sin_family = AF_INET;
sa.sin_addr = *src_addr;

if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) < 0)
if (bind(s, (struct sockaddr*)&sa, len) < 0)
errno_crash_and_burn("cannot bind source address");

memset(&sa, 0, len);
if (getsockname(s, (struct sockaddr *)&sa, &len) < 0)
errno_crash_and_burn("can't get ICMP socket identity");

if (sa.sin_port)
*ident = sa.sin_port;
}

unsigned short calcsum(unsigned short* buffer, int length)
Expand Down Expand Up @@ -128,7 +145,7 @@ int socket_sendto_ping_ipv4(int s, struct sockaddr* saddr, socklen_t saddr_len,
icp->icmp_code = 0;
icp->icmp_cksum = 0;
icp->icmp_seq = htons(icmp_seq_nr);
icp->icmp_id = htons(icmp_id_nr);
icp->icmp_id = icmp_id_nr;

if (random_data_flag) {
for (n = ((char*)&icp->icmp_data - (char*)icp); n < ping_pkt_size_ipv4; ++n) {
Expand Down
14 changes: 11 additions & 3 deletions src/socket6.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,23 @@ void init_ping_buffer_ipv6(size_t ping_data_size)
crash_and_burn("can't malloc ping packet");
}

void socket_set_src_addr_ipv6(int s, struct in6_addr* src_addr)
void socket_set_src_addr_ipv6(int s, struct in6_addr* src_addr, int *ident)
{
struct sockaddr_in6 sa;
socklen_t len = sizeof(sa);

memset(&sa, 0, sizeof(sa));
sa.sin6_family = AF_INET6;
sa.sin6_addr = *src_addr;

if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) < 0)
errno_crash_and_burn("cannot bind source address");

memset(&sa, 0, len);
if (getsockname(s, (struct sockaddr *)&sa, &len) < 0)
errno_crash_and_burn("can't get ICMP socket identity");

if (sa.sin6_port)
*ident = sa.sin6_port;
}

int socket_sendto_ping_ipv6(int s, struct sockaddr* saddr, socklen_t saddr_len, uint16_t icmp_seq_nr, uint16_t icmp_id_nr)
Expand All @@ -108,7 +116,7 @@ int socket_sendto_ping_ipv6(int s, struct sockaddr* saddr, socklen_t saddr_len,
icp->icmp6_type = ICMP6_ECHO_REQUEST;
icp->icmp6_code = 0;
icp->icmp6_seq = htons(icmp_seq_nr);
icp->icmp6_id = htons(icmp_id_nr);
icp->icmp6_id = icmp_id_nr;

if (random_data_flag) {
for (n = sizeof(struct icmp6_hdr); n < ping_pkt_size_ipv6; ++n) {
Expand Down