schweikert / fping

High performance ping tool
https://fping.org
Other
1.03k stars 251 forks source link

Unprivileged mode no longer works #248

Closed sfan5 closed 2 years ago

sfan5 commented 2 years ago

This is inside an systemd-nspawn container running Alpine Linux edge (aarch64). ping_group_range is configured on the host. Did an update yesterday and it no longer works, I think the last working version was 5.0.

strace output attached: You can see it receives responses but seems to ignore them.

localhost:/# fping --version
fping: Version 5.1
localhost:/# strace fping 8.8.8.8
execve("/usr/sbin/fping", ["fping", "8.8.8.8"], 0xffffeee5ef58 /* 19 vars */) = 0
set_tid_address(0xffffac2f4230)         = 618
brk(NULL)                               = 0xaaaae0952000
brk(0xaaaae0954000)                     = 0xaaaae0954000
mmap(0xaaaae0952000, 4096, PROT_NONE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xaaaae0952000
mprotect(0xaaaac9ae9000, 4096, PROT_READ) = 0
geteuid()                               = 0
socket(AF_INET, SOCK_RAW, IPPROTO_ICMP) = -1 EPERM (Operation not permitted)
socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP) = 3
fcntl(3, F_GETFL)                       = 0x2 (flags O_RDWR)
fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK|O_LARGEFILE) = 0
socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6) = -1 EPERM (Operation not permitted)
socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6) = 4
fcntl(4, F_GETFL)                       = 0x2 (flags O_RDWR)
fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK|O_LARGEFILE) = 0
getuid()                                = 0
getpid()                                = 618
getuid()                                = 0
setsockopt(3, SOL_SOCKET, SO_TIMESTAMPNS_OLD, [1], 4) = 0
setsockopt(4, SOL_SOCKET, SO_TIMESTAMPNS_OLD, [1], 4) = 0
rt_sigprocmask(SIG_UNBLOCK, [RT_1 RT_2], NULL, 8) = 0
rt_sigaction(SIGQUIT, {sa_handler=0xaaaac9ad2db4, sa_mask=[INT QUIT], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0xffffac294de8}, NULL, 8) = 0
rt_sigaction(SIGINT, {sa_handler=0xaaaac9ad2db4, sa_mask=[INT QUIT], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0xffffac294de8}, NULL, 8) = 0
mmap(NULL, 1048580, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffffac147000
sendto(3, "\10\0\365\225\2j\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 64, 0, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("8.8.8.8")}, 16) = 64
pselect6(5, [3 4], NULL, NULL, {tv_sec=0, tv_nsec=500001000}, {sigmask=NULL, sigsetsize=8}) = 1 (in [3], left {tv_sec=0, tv_nsec=482443459})
recvmsg(3, {msg_name={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("8.8.8.8")}, msg_namelen=128 => 16, msg_iov=[{iov_base="\0\0E\326\272)\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., iov_len=128}], msg_iovlen=1, msg_controllen=0, msg_flags=MSG_CTRUNC}, MSG_TRUNC) = 64
pselect6(5, [3 4], NULL, NULL, {tv_sec=0, tv_nsec=0}, {sigmask=NULL, sigsetsize=8}) = 0 (Timeout)
pselect6(5, [3 4], NULL, NULL, {tv_sec=0, tv_nsec=480241000}, {sigmask=NULL, sigsetsize=8}) = 0 (Timeout)
sendto(3, "\10\0\365\224\2j\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 64, 0, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("8.8.8.8")}, 16) = 64
pselect6(5, [3 4], NULL, NULL, {tv_sec=0, tv_nsec=750001000}, {sigmask=NULL, sigsetsize=8}) = 1 (in [3], left {tv_sec=0, tv_nsec=732169869})
recvmsg(3, {msg_name={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("8.8.8.8")}, msg_namelen=128 => 16, msg_iov=[{iov_base="\0\0E\325\272)\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., iov_len=128}], msg_iovlen=1, msg_controllen=0, msg_flags=MSG_CTRUNC}, MSG_TRUNC) = 64
pselect6(5, [3 4], NULL, NULL, {tv_sec=0, tv_nsec=0}, {sigmask=NULL, sigsetsize=8}) = 0 (Timeout)
pselect6(5, [3 4], NULL, NULL, {tv_sec=0, tv_nsec=730211000}, {sigmask=NULL, sigsetsize=8}) = 0 (Timeout)
sendto(3, "\10\0\365\223\2j\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 64, 0, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("8.8.8.8")}, 16) = 64
pselect6(5, [3 4], NULL, NULL, {tv_sec=1, tv_nsec=125000000}, {sigmask=NULL, sigsetsize=8}) = 1 (in [3], left {tv_sec=1, tv_nsec=107416000})
recvmsg(3, {msg_name={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("8.8.8.8")}, msg_namelen=128 => 16, msg_iov=[{iov_base="\0\0E\324\272)\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., iov_len=128}], msg_iovlen=1, msg_controllen=0, msg_flags=MSG_CTRUNC}, MSG_TRUNC) = 64
pselect6(5, [3 4], NULL, NULL, {tv_sec=0, tv_nsec=0}, {sigmask=NULL, sigsetsize=8}) = 0 (Timeout)
pselect6(5, [3 4], NULL, NULL, {tv_sec=1, tv_nsec=105566000}, {sigmask=NULL, sigsetsize=8}) = 0 (Timeout)
sendto(3, "\10\0\365\222\2j\0\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 64, 0, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("8.8.8.8")}, 16) = 64
pselect6(5, [3 4], NULL, NULL, {tv_sec=1, tv_nsec=687500000}, {sigmask=NULL, sigsetsize=8}) = 1 (in [3], left {tv_sec=1, tv_nsec=670026544})
recvmsg(3, {msg_name={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("8.8.8.8")}, msg_namelen=128 => 16, msg_iov=[{iov_base="\0\0E\323\272)\0\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., iov_len=128}], msg_iovlen=1, msg_controllen=0, msg_flags=MSG_CTRUNC}, MSG_TRUNC) = 64
pselect6(5, [3 4], NULL, NULL, {tv_sec=0, tv_nsec=0}, {sigmask=NULL, sigsetsize=8}) = 0 (Timeout)
pselect6(5, [3 4], NULL, NULL, {tv_sec=1, tv_nsec=668183000}, {sigmask=NULL, sigsetsize=8}) = 0 (Timeout)
writev(1, [{iov_base="8.8.8.8 is unreachable", iov_len=22}, {iov_base="\n", iov_len=1}], 28.8.8.8 is unreachable
) = 23
exit_group(1)                           = ?
+++ exited with 1 +++
hmh commented 2 years ago

It has euid 0 (root), but not the required capability (CAP_NET_RAW) -- socket() returning EPERM -- so it can't use ICMP. Give it CAP_NET_RAW and it will work for IPv4. You might need CAP_NET_ADMIN for IPv6 if you're doing link-local pings, though, since that requires one to bind to a specific interface to work.

hmh commented 2 years ago

Why this regressed, I don't know. Maybe an strace of the working version (with the same base operating system image, please) ?

sfan5 commented 2 years ago
localhost:~/fping-5.0# ./src/fping --version
./src/fping: Version 5.0
localhost:~/fping-5.0# strace ./src/fping 8.8.8.8
execve("./src/fping", ["./src/fping", "8.8.8.8"], 0xffffef313ff8 /* 18 vars */) = 0
set_tid_address(0xffffbb817230)         = 254
brk(NULL)                               = 0xaaaad053c000
brk(0xaaaad053e000)                     = 0xaaaad053e000
mmap(0xaaaad053c000, 4096, PROT_NONE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xaaaad053c000
mprotect(0xaaaaaedc9000, 4096, PROT_READ) = 0
socket(AF_INET, SOCK_RAW, IPPROTO_ICMP) = -1 EPERM (Operation not permitted)
socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP) = 3
fcntl(3, F_GETFL)                       = 0x2 (flags O_RDWR)
fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK|O_LARGEFILE) = 0
socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6) = -1 EPERM (Operation not permitted)
socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6) = 4
fcntl(4, F_GETFL)                       = 0x2 (flags O_RDWR)
fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK|O_LARGEFILE) = 0
getuid()                                = 0
getpid()                                = 254
setsockopt(3, SOL_SOCKET, SO_TIMESTAMPNS_OLD, [1], 4) = 0
setsockopt(4, SOL_SOCKET, SO_TIMESTAMPNS_OLD, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
getsockname(3, {sa_family=AF_INET, sin_port=htons(47842), sin_addr=inet_addr("0.0.0.0")}, [16]) = 0
bind(4, {sa_family=AF_INET6, sin6_port=htons(0), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::", &sin6_addr), sin6_scope_id=0}, 28) = 0
getsockname(4, {sa_family=AF_INET6, sin6_port=htons(47843), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::", &sin6_addr), sin6_scope_id=0}, [28]) = 0
rt_sigprocmask(SIG_UNBLOCK, [RT_1 RT_2], NULL, 8) = 0
rt_sigaction(SIGQUIT, {sa_handler=0xaaaaaedb2dc0, sa_mask=[INT QUIT], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0xffffbb7b7de8}, NULL, 8) = 0
rt_sigaction(SIGINT, {sa_handler=0xaaaaaedb2dc0, sa_mask=[INT QUIT], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0xffffbb7b7de8}, NULL, 8) = 0
mmap(NULL, 1048580, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffffbb66a000
sendto(3, "\10\0=\35\272\342\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 64, 0, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("8.8.8.8")}, 16) = 64
pselect6(5, [3 4], NULL, NULL, {tv_sec=0, tv_nsec=500001000}, {sigmask=NULL, sigsetsize=8}) = 1 (in [3], left {tv_sec=0, tv_nsec=482108226})
recvmsg(3, {msg_name={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("8.8.8.8")}, msg_namelen=128 => 16, msg_iov=[{iov_base="\0\0E\35\272\342\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., iov_len=128}], msg_iovlen=1, msg_controllen=0, msg_flags=MSG_CTRUNC}, MSG_TRUNC) = 64
writev(1, [{iov_base="8.8.8.8 is alive", iov_len=16}, {iov_base="\n", iov_len=1}], 28.8.8.8 is alive
) = 17
pselect6(5, [3 4], NULL, NULL, {tv_sec=0, tv_nsec=0}, {sigmask=NULL, sigsetsize=8}) = 0 (Timeout)
exit_group(0)                           = ?
+++ exited with 0 +++
hmh commented 2 years ago

Eh, it notices it can't do ICMP, switches to UDP mode, sends packets and receives the reply. But from the strace, it looks like it doesn't recognize the reply as the target being alive.

I feared the regression was related to the changes I submitted to drop privileges later (required for IPv6 link-local to work on Linux), but it doesn't look related to that change at all. Instead, it looks like some logic got broken in 5.1.

I will leave the debugging of the other changes to someone who knows the code better...

schweikert commented 2 years ago

This seems caused by commit 31f76f607158997ab3967d0ee61404b263e754b3