diff --git a/README.md b/README.md index 4c45e62..43925b6 100644 --- a/README.md +++ b/README.md @@ -28,13 +28,14 @@ If you want to install fping from source, proceed as follows: (see: `./configure --help`) 2. Run `make; make install`. 3. Make fping either setuid, or, if under Linux: - `sudo setcap cap_net_raw+ep fping` + `sudo setcap cap_net_raw,cap_net_admin+ep fping` If you can't run fping as root or can't use the cap_net_raw capability, you can also run fping in unprivileged mode. This works on MacOS and also on Linux, provided that your GID is included in the range defined in `/proc/sys/net/ipv4/ping_group_range`. This is particularly useful for running -fping in rootless / unprivileged containers. +fping in rootless / unprivileged containers. The --fwmark option needs root or +cap_net_admin. setuid will not work for --fwmark. ## Usage diff --git a/ci/prepare-linux.sh b/ci/prepare-linux.sh index 2c4d727..efe8b3b 100755 --- a/ci/prepare-linux.sh +++ b/ci/prepare-linux.sh @@ -1,6 +1,6 @@ #!/bin/bash -sudo setcap cap_net_raw+ep src/fping +sudo setcap cap_net_raw,cap_net_admin+ep src/fping if [[ ! $PATH =~ fping/src ]]; then PATH=/home/dws/checkouts/fping/src:$PATH diff --git a/ci/test-07-options-i-m.pl b/ci/test-07-options-i-m.pl index 781377f..2eddc45 100755 --- a/ci/test-07-options-i-m.pl +++ b/ci/test-07-options-i-m.pl @@ -1,10 +1,11 @@ #!/usr/bin/perl -w -use Test::Command tests => 9; +use Test::Command tests => 12; use Test::More; # -i n interval between sending ping packets (in millisec) (default 25) # -l loop sending pings forever +# -k set fwmark on ping packets # -m ping multiple interfaces on target host # -M don't fragment @@ -24,6 +25,17 @@ $cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0 }); } +# fping -k +SKIP: { +if($^O ne 'linux') { + skip '-k option is only supported on Linux', 3; +} +my $cmd = Test::Command->new(cmd => 'sudo env "PATH=$PATH" fping -k 256 127.0.0.1'); +$cmd->exit_is_num(0); +$cmd->stdout_is_eq("127.0.0.1 is alive\n"); +$cmd->stderr_is_eq(""); +} + # fping -l with SIGQUIT { my $cmd = Test::Command->new(cmd => '(sleep 2; pkill -QUIT fping; sleep 2; pkill fping)& fping -p 900 -l 127.0.0.1'); diff --git a/contrib/fping.spec b/contrib/fping.spec index 823c7ff..71d1ea0 100644 --- a/contrib/fping.spec +++ b/contrib/fping.spec @@ -54,8 +54,8 @@ rm -rf $RPM_BUILD_ROOT %post if [ -x /usr/sbin/setcap ]; then /bin/chmod 0755 /usr/sbin/fping* - /usr/sbin/setcap cap_net_raw+ep /usr/sbin/fping - /usr/sbin/setcap cap_net_raw+ep /usr/sbin/fping6 + /usr/sbin/setcap cap_net_raw,cap_net_admin+ep /usr/sbin/fping + /usr/sbin/setcap cap_net_raw,cap_net_admin+ep /usr/sbin/fping6 fi %changelog diff --git a/doc/fping.pod b/doc/fping.pod index faa327b..ac2eb3e 100644 --- a/doc/fping.pod +++ b/doc/fping.pod @@ -130,6 +130,11 @@ to any target (default is 10, minimum is 1). Set the interface (requires SO_BINDTODEVICE support). +=item B<-k>, B<--fwmark>=I + +Set FWMARK on ping packets for policy-based routing. Requires Linux kernel +2.6.25<=, and root privileges or cap_net_admin. + =item B<-l>, B<--loop> Loop sending packets to each target indefinitely. Can be interrupted with diff --git a/src/fping.c b/src/fping.c index 27711c8..ae248b1 100644 --- a/src/fping.c +++ b/src/fping.c @@ -363,6 +363,8 @@ int randomly_lose_flag, trace_flag, print_per_system_flag; int lose_factor; #endif /* DEBUG || _DEBUG */ +unsigned int fwmark = 0; + char *filename = NULL; /* file containing hosts to ping */ /*** forward declarations ***/ @@ -518,6 +520,9 @@ int main(int argc, char **argv) { "ttl", 'H', OPTPARSE_REQUIRED }, { "interval", 'i', OPTPARSE_REQUIRED }, { "iface", 'I', OPTPARSE_REQUIRED }, +#ifdef SO_MARK + { "fwmark", 'k', OPTPARSE_REQUIRED }, +#endif { "loop", 'l', OPTPARSE_NONE }, { "all", 'm', OPTPARSE_NONE }, { "dontfrag", 'M', OPTPARSE_NONE }, @@ -761,6 +766,23 @@ int main(int argc, char **argv) case 'f': filename = optparse_state.optarg; break; +#ifdef SO_MARK + case 'k': + if (!(fwmark = (unsigned int)atol(optparse_state.optarg))) + usage(1); + + if (socket4 >= 0) + if(-1 == setsockopt(socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark)) + perror("fwmark ipv4"); + +#ifdef IPV6 + if (socket6 >= 0) + if(-1 == setsockopt(socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark)) + perror("fwmark ipv6"); +#endif + + break; +#endif case 'g': /* use IP list generation */ @@ -2923,6 +2945,9 @@ void usage(int is_error) fprintf(out, " -H, --ttl=N set the IP TTL value (Time To Live hops)\n"); #ifdef SO_BINDTODEVICE fprintf(out, " -I, --iface=IFACE bind to a particular interface\n"); +#endif +#ifdef SO_MARK + fprintf(out, " -k, --fwmark=FWMARK set the routing mark\n"); #endif fprintf(out, " -l, --loop loop mode: send pings forever\n"); fprintf(out, " -m, --all use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");