Merge pull request #173 from tycho/linux-unprivileged-ping

Linux unprivileged ping support
pull/179/head
David Schweikert 5 years ago committed by GitHub
commit 43767e2fbc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,5 +1,6 @@
#!/usr/bin/perl -w
use English;
use Test::Command;
use Test::More;
@ -7,6 +8,26 @@ 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

@ -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
@ -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'
@ -368,6 +370,11 @@ 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)
@ -375,7 +382,7 @@ int main(int argc, char** argv)
}
optparse_init(&optparse_state, argv);
ident = getpid() & 0xFFFF;
ident4 = ident6 = getpid() & 0xFFFF;
verbose_flag = 1;
backoff_flag = 1;
opterr = 1;
@ -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
@ -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 {
@ -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 */
@ -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;
}
@ -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;
@ -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;
}
@ -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;
@ -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) {
@ -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;

@ -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

@ -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;
@ -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) {
@ -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 */
@ -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)
@ -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) {

@ -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)
@ -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) {

Loading…
Cancel
Save